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

Annotation of src/libexec/ftpd/ftpcmd.y, Revision 1.1.1.2

1.1       cgd         1: /*
1.1.1.2 ! cjs         2:  * Copyright (c) 1985, 1988, 1993, 1994
        !             3:  *     The Regents of the University of California.  All rights reserved.
1.1       cgd         4:  *
                      5:  * Redistribution and use in source and binary forms, with or without
                      6:  * modification, are permitted provided that the following conditions
                      7:  * are met:
                      8:  * 1. Redistributions of source code must retain the above copyright
                      9:  *    notice, this list of conditions and the following disclaimer.
                     10:  * 2. Redistributions in binary form must reproduce the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer in the
                     12:  *    documentation and/or other materials provided with the distribution.
                     13:  * 3. All advertising materials mentioning features or use of this software
                     14:  *    must display the following acknowledgement:
                     15:  *     This product includes software developed by the University of
                     16:  *     California, Berkeley and its contributors.
                     17:  * 4. Neither the name of the University nor the names of its contributors
                     18:  *    may be used to endorse or promote products derived from this software
                     19:  *    without specific prior written permission.
                     20:  *
                     21:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     22:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     23:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     24:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     25:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     26:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     27:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     28:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     29:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     30:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     31:  * SUCH DAMAGE.
                     32:  *
1.1.1.2 ! cjs        33:  *     @(#)ftpcmd.y    8.3 (Berkeley) 4/6/94
1.1       cgd        34:  */
                     35:
                     36: /*
                     37:  * Grammar for FTP commands.
                     38:  * See RFC 959.
                     39:  */
                     40:
                     41: %{
                     42:
                     43: #ifndef lint
1.1.1.2 ! cjs        44: static char sccsid[] = "@(#)ftpcmd.y   8.3 (Berkeley) 4/6/94";
1.1       cgd        45: #endif /* not lint */
                     46:
                     47: #include <sys/param.h>
                     48: #include <sys/socket.h>
                     49: #include <sys/stat.h>
1.1.1.2 ! cjs        50:
1.1       cgd        51: #include <netinet/in.h>
                     52: #include <arpa/ftp.h>
1.1.1.2 ! cjs        53:
        !            54: #include <ctype.h>
        !            55: #include <errno.h>
        !            56: #include <glob.h>
1.1       cgd        57: #include <pwd.h>
1.1.1.2 ! cjs        58: #include <setjmp.h>
        !            59: #include <signal.h>
1.1       cgd        60: #include <stdio.h>
                     61: #include <stdlib.h>
                     62: #include <string.h>
1.1.1.2 ! cjs        63: #include <syslog.h>
        !            64: #include <time.h>
        !            65: #include <unistd.h>
        !            66:
        !            67: #include "extern.h"
1.1       cgd        68:
                     69: extern struct sockaddr_in data_dest;
                     70: extern int logged_in;
                     71: extern struct passwd *pw;
                     72: extern int guest;
                     73: extern int logging;
                     74: extern int type;
                     75: extern int form;
                     76: extern int debug;
                     77: extern int timeout;
                     78: extern int maxtimeout;
                     79: extern  int pdata;
                     80: extern char hostname[], remotehost[];
                     81: extern char proctitle[];
                     82: extern int usedefault;
                     83: extern  int transflag;
                     84: extern  char tmpline[];
                     85:
                     86: off_t  restart_point;
                     87:
                     88: static int cmd_type;
                     89: static int cmd_form;
                     90: static int cmd_bytesz;
                     91: char   cbuf[512];
                     92: char   *fromname;
                     93:
                     94: %}
                     95:
1.1.1.2 ! cjs        96: %union {
        !            97:        int     i;
        !            98:        char   *s;
        !            99: }
        !           100:
1.1       cgd       101: %token
                    102:        A       B       C       E       F       I
                    103:        L       N       P       R       S       T
                    104:
1.1.1.2 ! cjs       105:        SP      CRLF    COMMA
1.1       cgd       106:
                    107:        USER    PASS    ACCT    REIN    QUIT    PORT
                    108:        PASV    TYPE    STRU    MODE    RETR    STOR
                    109:        APPE    MLFL    MAIL    MSND    MSOM    MSAM
                    110:        MRSQ    MRCP    ALLO    REST    RNFR    RNTO
                    111:        ABOR    DELE    CWD     LIST    NLST    SITE
                    112:        STAT    HELP    NOOP    MKD     RMD     PWD
                    113:        CDUP    STOU    SMNT    SYST    SIZE    MDTM
                    114:
                    115:        UMASK   IDLE    CHMOD
                    116:
                    117:        LEXERR
                    118:
1.1.1.2 ! cjs       119: %token <s> STRING
        !           120: %token <i> NUMBER
        !           121:
        !           122: %type  <i> check_login octal_number byte_size
        !           123: %type  <i> struct_code mode_code type_code form_code
        !           124: %type  <s> pathstring pathname password username
        !           125:
1.1       cgd       126: %start cmd_list
                    127:
                    128: %%
                    129:
1.1.1.2 ! cjs       130: cmd_list
        !           131:        : /* empty */
        !           132:        | cmd_list cmd
        !           133:                {
1.1       cgd       134:                        fromname = (char *) 0;
                    135:                        restart_point = (off_t) 0;
                    136:                }
1.1.1.2 ! cjs       137:        | cmd_list rcmd
1.1       cgd       138:        ;
                    139:
1.1.1.2 ! cjs       140: cmd
        !           141:        : USER SP username CRLF
        !           142:                {
        !           143:                        user($3);
        !           144:                        free($3);
        !           145:                }
        !           146:        | PASS SP password CRLF
        !           147:                {
        !           148:                        pass($3);
        !           149:                        free($3);
1.1       cgd       150:                }
1.1.1.2 ! cjs       151:        | PORT SP host_port CRLF
        !           152:                {
1.1       cgd       153:                        usedefault = 0;
                    154:                        if (pdata >= 0) {
                    155:                                (void) close(pdata);
                    156:                                pdata = -1;
                    157:                        }
                    158:                        reply(200, "PORT command successful.");
                    159:                }
1.1.1.2 ! cjs       160:        | PASV CRLF
        !           161:                {
1.1       cgd       162:                        passive();
                    163:                }
1.1.1.2 ! cjs       164:        | TYPE SP type_code CRLF
        !           165:                {
1.1       cgd       166:                        switch (cmd_type) {
                    167:
                    168:                        case TYPE_A:
                    169:                                if (cmd_form == FORM_N) {
                    170:                                        reply(200, "Type set to A.");
                    171:                                        type = cmd_type;
                    172:                                        form = cmd_form;
                    173:                                } else
                    174:                                        reply(504, "Form must be N.");
                    175:                                break;
                    176:
                    177:                        case TYPE_E:
                    178:                                reply(504, "Type E not implemented.");
                    179:                                break;
                    180:
                    181:                        case TYPE_I:
                    182:                                reply(200, "Type set to I.");
                    183:                                type = cmd_type;
                    184:                                break;
                    185:
                    186:                        case TYPE_L:
                    187: #if NBBY == 8
                    188:                                if (cmd_bytesz == 8) {
                    189:                                        reply(200,
                    190:                                            "Type set to L (byte size 8).");
                    191:                                        type = cmd_type;
                    192:                                } else
                    193:                                        reply(504, "Byte size must be 8.");
                    194: #else /* NBBY == 8 */
                    195:                                UNIMPLEMENTED for NBBY != 8
                    196: #endif /* NBBY == 8 */
                    197:                        }
                    198:                }
1.1.1.2 ! cjs       199:        | STRU SP struct_code CRLF
        !           200:                {
1.1       cgd       201:                        switch ($3) {
                    202:
                    203:                        case STRU_F:
                    204:                                reply(200, "STRU F ok.");
                    205:                                break;
                    206:
                    207:                        default:
                    208:                                reply(504, "Unimplemented STRU type.");
                    209:                        }
                    210:                }
1.1.1.2 ! cjs       211:        | MODE SP mode_code CRLF
        !           212:                {
1.1       cgd       213:                        switch ($3) {
                    214:
                    215:                        case MODE_S:
                    216:                                reply(200, "MODE S ok.");
                    217:                                break;
                    218:
                    219:                        default:
                    220:                                reply(502, "Unimplemented MODE type.");
                    221:                        }
                    222:                }
1.1.1.2 ! cjs       223:        | ALLO SP NUMBER CRLF
        !           224:                {
1.1       cgd       225:                        reply(202, "ALLO command ignored.");
                    226:                }
1.1.1.2 ! cjs       227:        | ALLO SP NUMBER SP R SP NUMBER CRLF
        !           228:                {
1.1       cgd       229:                        reply(202, "ALLO command ignored.");
                    230:                }
1.1.1.2 ! cjs       231:        | RETR check_login SP pathname CRLF
        !           232:                {
1.1       cgd       233:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       234:                                retrieve((char *) 0, $4);
1.1       cgd       235:                        if ($4 != NULL)
1.1.1.2 ! cjs       236:                                free($4);
1.1       cgd       237:                }
1.1.1.2 ! cjs       238:        | STOR check_login SP pathname CRLF
        !           239:                {
1.1       cgd       240:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       241:                                store($4, "w", 0);
1.1       cgd       242:                        if ($4 != NULL)
1.1.1.2 ! cjs       243:                                free($4);
1.1       cgd       244:                }
1.1.1.2 ! cjs       245:        | APPE check_login SP pathname CRLF
        !           246:                {
1.1       cgd       247:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       248:                                store($4, "a", 0);
1.1       cgd       249:                        if ($4 != NULL)
1.1.1.2 ! cjs       250:                                free($4);
1.1       cgd       251:                }
1.1.1.2 ! cjs       252:        | NLST check_login CRLF
        !           253:                {
1.1       cgd       254:                        if ($2)
                    255:                                send_file_list(".");
                    256:                }
1.1.1.2 ! cjs       257:        | NLST check_login SP STRING CRLF
        !           258:                {
        !           259:                        if ($2 && $4 != NULL)
        !           260:                                send_file_list($4);
1.1       cgd       261:                        if ($4 != NULL)
1.1.1.2 ! cjs       262:                                free($4);
1.1       cgd       263:                }
1.1.1.2 ! cjs       264:        | LIST check_login CRLF
        !           265:                {
1.1       cgd       266:                        if ($2)
                    267:                                retrieve("/bin/ls -lgA", "");
                    268:                }
1.1.1.2 ! cjs       269:        | LIST check_login SP pathname CRLF
        !           270:                {
1.1       cgd       271:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       272:                                retrieve("/bin/ls -lgA %s", $4);
1.1       cgd       273:                        if ($4 != NULL)
1.1.1.2 ! cjs       274:                                free($4);
1.1       cgd       275:                }
1.1.1.2 ! cjs       276:        | STAT check_login SP pathname CRLF
        !           277:                {
1.1       cgd       278:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       279:                                statfilecmd($4);
1.1       cgd       280:                        if ($4 != NULL)
1.1.1.2 ! cjs       281:                                free($4);
1.1       cgd       282:                }
1.1.1.2 ! cjs       283:        | STAT CRLF
        !           284:                {
1.1       cgd       285:                        statcmd();
                    286:                }
1.1.1.2 ! cjs       287:        | DELE check_login SP pathname CRLF
        !           288:                {
1.1       cgd       289:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       290:                                delete($4);
1.1       cgd       291:                        if ($4 != NULL)
1.1.1.2 ! cjs       292:                                free($4);
1.1       cgd       293:                }
1.1.1.2 ! cjs       294:        | RNTO SP pathname CRLF
        !           295:                {
1.1       cgd       296:                        if (fromname) {
1.1.1.2 ! cjs       297:                                renamecmd(fromname, $3);
1.1       cgd       298:                                free(fromname);
                    299:                                fromname = (char *) 0;
                    300:                        } else {
                    301:                                reply(503, "Bad sequence of commands.");
                    302:                        }
1.1.1.2 ! cjs       303:                        free($3);
1.1       cgd       304:                }
1.1.1.2 ! cjs       305:        | ABOR CRLF
        !           306:                {
1.1       cgd       307:                        reply(225, "ABOR command successful.");
                    308:                }
1.1.1.2 ! cjs       309:        | CWD check_login CRLF
        !           310:                {
1.1       cgd       311:                        if ($2)
                    312:                                cwd(pw->pw_dir);
                    313:                }
1.1.1.2 ! cjs       314:        | CWD check_login SP pathname CRLF
        !           315:                {
1.1       cgd       316:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       317:                                cwd($4);
1.1       cgd       318:                        if ($4 != NULL)
1.1.1.2 ! cjs       319:                                free($4);
1.1       cgd       320:                }
1.1.1.2 ! cjs       321:        | HELP CRLF
        !           322:                {
1.1       cgd       323:                        help(cmdtab, (char *) 0);
                    324:                }
1.1.1.2 ! cjs       325:        | HELP SP STRING CRLF
        !           326:                {
        !           327:                        char *cp = $3;
1.1       cgd       328:
                    329:                        if (strncasecmp(cp, "SITE", 4) == 0) {
1.1.1.2 ! cjs       330:                                cp = $3 + 4;
1.1       cgd       331:                                if (*cp == ' ')
                    332:                                        cp++;
                    333:                                if (*cp)
                    334:                                        help(sitetab, cp);
                    335:                                else
                    336:                                        help(sitetab, (char *) 0);
                    337:                        } else
1.1.1.2 ! cjs       338:                                help(cmdtab, $3);
1.1       cgd       339:                }
1.1.1.2 ! cjs       340:        | NOOP CRLF
        !           341:                {
1.1       cgd       342:                        reply(200, "NOOP command successful.");
                    343:                }
1.1.1.2 ! cjs       344:        | MKD check_login SP pathname CRLF
        !           345:                {
1.1       cgd       346:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       347:                                makedir($4);
1.1       cgd       348:                        if ($4 != NULL)
1.1.1.2 ! cjs       349:                                free($4);
1.1       cgd       350:                }
1.1.1.2 ! cjs       351:        | RMD check_login SP pathname CRLF
        !           352:                {
1.1       cgd       353:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       354:                                removedir($4);
1.1       cgd       355:                        if ($4 != NULL)
1.1.1.2 ! cjs       356:                                free($4);
1.1       cgd       357:                }
1.1.1.2 ! cjs       358:        | PWD check_login CRLF
        !           359:                {
1.1       cgd       360:                        if ($2)
                    361:                                pwd();
                    362:                }
1.1.1.2 ! cjs       363:        | CDUP check_login CRLF
        !           364:                {
1.1       cgd       365:                        if ($2)
                    366:                                cwd("..");
                    367:                }
1.1.1.2 ! cjs       368:        | SITE SP HELP CRLF
        !           369:                {
1.1       cgd       370:                        help(sitetab, (char *) 0);
                    371:                }
1.1.1.2 ! cjs       372:        | SITE SP HELP SP STRING CRLF
        !           373:                {
        !           374:                        help(sitetab, $5);
1.1       cgd       375:                }
1.1.1.2 ! cjs       376:        | SITE SP UMASK check_login CRLF
        !           377:                {
1.1       cgd       378:                        int oldmask;
                    379:
                    380:                        if ($4) {
                    381:                                oldmask = umask(0);
                    382:                                (void) umask(oldmask);
                    383:                                reply(200, "Current UMASK is %03o", oldmask);
                    384:                        }
                    385:                }
1.1.1.2 ! cjs       386:        | SITE SP UMASK check_login SP octal_number CRLF
        !           387:                {
1.1       cgd       388:                        int oldmask;
                    389:
                    390:                        if ($4) {
                    391:                                if (($6 == -1) || ($6 > 0777)) {
                    392:                                        reply(501, "Bad UMASK value");
                    393:                                } else {
                    394:                                        oldmask = umask($6);
                    395:                                        reply(200,
                    396:                                            "UMASK set to %03o (was %03o)",
                    397:                                            $6, oldmask);
                    398:                                }
                    399:                        }
                    400:                }
1.1.1.2 ! cjs       401:        | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
        !           402:                {
1.1       cgd       403:                        if ($4 && ($8 != NULL)) {
                    404:                                if ($6 > 0777)
                    405:                                        reply(501,
                    406:                                "CHMOD: Mode value must be between 0 and 0777");
1.1.1.2 ! cjs       407:                                else if (chmod($8, $6) < 0)
        !           408:                                        perror_reply(550, $8);
1.1       cgd       409:                                else
                    410:                                        reply(200, "CHMOD command successful.");
                    411:                        }
                    412:                        if ($8 != NULL)
1.1.1.2 ! cjs       413:                                free($8);
1.1       cgd       414:                }
1.1.1.2 ! cjs       415:        | SITE SP IDLE CRLF
        !           416:                {
1.1       cgd       417:                        reply(200,
                    418:                            "Current IDLE time limit is %d seconds; max %d",
                    419:                                timeout, maxtimeout);
                    420:                }
1.1.1.2 ! cjs       421:        | SITE SP IDLE SP NUMBER CRLF
        !           422:                {
1.1       cgd       423:                        if ($5 < 30 || $5 > maxtimeout) {
                    424:                                reply(501,
                    425:                        "Maximum IDLE time must be between 30 and %d seconds",
                    426:                                    maxtimeout);
                    427:                        } else {
                    428:                                timeout = $5;
                    429:                                (void) alarm((unsigned) timeout);
                    430:                                reply(200,
                    431:                                    "Maximum IDLE time set to %d seconds",
                    432:                                    timeout);
                    433:                        }
                    434:                }
1.1.1.2 ! cjs       435:        | STOU check_login SP pathname CRLF
        !           436:                {
1.1       cgd       437:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       438:                                store($4, "w", 1);
1.1       cgd       439:                        if ($4 != NULL)
1.1.1.2 ! cjs       440:                                free($4);
1.1       cgd       441:                }
1.1.1.2 ! cjs       442:        | SYST CRLF
        !           443:                {
1.1       cgd       444: #ifdef unix
                    445: #ifdef BSD
                    446:                        reply(215, "UNIX Type: L%d Version: BSD-%d",
                    447:                                NBBY, BSD);
                    448: #else /* BSD */
                    449:                        reply(215, "UNIX Type: L%d", NBBY);
                    450: #endif /* BSD */
                    451: #else /* unix */
                    452:                        reply(215, "UNKNOWN Type: L%d", NBBY);
                    453: #endif /* unix */
                    454:                }
                    455:
                    456:                /*
                    457:                 * SIZE is not in RFC959, but Postel has blessed it and
                    458:                 * it will be in the updated RFC.
                    459:                 *
                    460:                 * Return size of file in a format suitable for
                    461:                 * using with RESTART (we just count bytes).
                    462:                 */
1.1.1.2 ! cjs       463:        | SIZE check_login SP pathname CRLF
        !           464:                {
1.1       cgd       465:                        if ($2 && $4 != NULL)
1.1.1.2 ! cjs       466:                                sizecmd($4);
1.1       cgd       467:                        if ($4 != NULL)
1.1.1.2 ! cjs       468:                                free($4);
1.1       cgd       469:                }
                    470:
                    471:                /*
                    472:                 * MDTM is not in RFC959, but Postel has blessed it and
                    473:                 * it will be in the updated RFC.
                    474:                 *
                    475:                 * Return modification time of file as an ISO 3307
                    476:                 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
                    477:                 * where xxx is the fractional second (of any precision,
                    478:                 * not necessarily 3 digits)
                    479:                 */
1.1.1.2 ! cjs       480:        | MDTM check_login SP pathname CRLF
        !           481:                {
1.1       cgd       482:                        if ($2 && $4 != NULL) {
                    483:                                struct stat stbuf;
1.1.1.2 ! cjs       484:                                if (stat($4, &stbuf) < 0)
        !           485:                                        reply(550, "%s: %s",
        !           486:                                            $4, strerror(errno));
        !           487:                                else if (!S_ISREG(stbuf.st_mode)) {
        !           488:                                        reply(550, "%s: not a plain file.", $4);
1.1       cgd       489:                                } else {
1.1.1.2 ! cjs       490:                                        struct tm *t;
1.1       cgd       491:                                        t = gmtime(&stbuf.st_mtime);
                    492:                                        reply(213,
                    493:                                            "19%02d%02d%02d%02d%02d%02d",
                    494:                                            t->tm_year, t->tm_mon+1, t->tm_mday,
                    495:                                            t->tm_hour, t->tm_min, t->tm_sec);
                    496:                                }
                    497:                        }
                    498:                        if ($4 != NULL)
1.1.1.2 ! cjs       499:                                free($4);
1.1       cgd       500:                }
1.1.1.2 ! cjs       501:        | QUIT CRLF
        !           502:                {
1.1       cgd       503:                        reply(221, "Goodbye.");
                    504:                        dologout(0);
                    505:                }
1.1.1.2 ! cjs       506:        | error CRLF
        !           507:                {
1.1       cgd       508:                        yyerrok;
                    509:                }
                    510:        ;
1.1.1.2 ! cjs       511: rcmd
        !           512:        : RNFR check_login SP pathname CRLF
        !           513:                {
1.1       cgd       514:                        char *renamefrom();
                    515:
                    516:                        restart_point = (off_t) 0;
                    517:                        if ($2 && $4) {
1.1.1.2 ! cjs       518:                                fromname = renamefrom($4);
1.1       cgd       519:                                if (fromname == (char *) 0 && $4) {
1.1.1.2 ! cjs       520:                                        free($4);
1.1       cgd       521:                                }
                    522:                        }
                    523:                }
1.1.1.2 ! cjs       524:        | REST SP byte_size CRLF
        !           525:                {
1.1       cgd       526:                        fromname = (char *) 0;
1.1.1.2 ! cjs       527:                        restart_point = $3;     /* XXX $3 is only "int" */
        !           528:                        reply(350, "Restarting at %qd. %s", restart_point,
1.1       cgd       529:                            "Send STORE or RETRIEVE to initiate transfer.");
                    530:                }
                    531:        ;
1.1.1.2 ! cjs       532:
        !           533: username
        !           534:        : STRING
1.1       cgd       535:        ;
                    536:
1.1.1.2 ! cjs       537: password
        !           538:        : /* empty */
        !           539:                {
        !           540:                        $$ = (char *)calloc(1, sizeof(char));
1.1       cgd       541:                }
1.1.1.2 ! cjs       542:        | STRING
1.1       cgd       543:        ;
                    544:
1.1.1.2 ! cjs       545: byte_size
        !           546:        : NUMBER
1.1       cgd       547:        ;
                    548:
1.1.1.2 ! cjs       549: host_port
        !           550:        : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
1.1       cgd       551:                NUMBER COMMA NUMBER
1.1.1.2 ! cjs       552:                {
        !           553:                        char *a, *p;
1.1       cgd       554:
                    555:                        a = (char *)&data_dest.sin_addr;
                    556:                        a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
                    557:                        p = (char *)&data_dest.sin_port;
                    558:                        p[0] = $9; p[1] = $11;
                    559:                        data_dest.sin_family = AF_INET;
                    560:                }
                    561:        ;
                    562:
1.1.1.2 ! cjs       563: form_code
        !           564:        : N
        !           565:                {
        !           566:                        $$ = FORM_N;
        !           567:                }
        !           568:        | T
        !           569:                {
        !           570:                        $$ = FORM_T;
        !           571:                }
        !           572:        | C
        !           573:                {
        !           574:                        $$ = FORM_C;
        !           575:                }
1.1       cgd       576:        ;
                    577:
1.1.1.2 ! cjs       578: type_code
        !           579:        : A
        !           580:                {
        !           581:                        cmd_type = TYPE_A;
        !           582:                        cmd_form = FORM_N;
        !           583:                }
        !           584:        | A SP form_code
        !           585:                {
        !           586:                        cmd_type = TYPE_A;
        !           587:                        cmd_form = $3;
        !           588:                }
        !           589:        | E
        !           590:                {
        !           591:                        cmd_type = TYPE_E;
        !           592:                        cmd_form = FORM_N;
        !           593:                }
        !           594:        | E SP form_code
        !           595:                {
        !           596:                        cmd_type = TYPE_E;
        !           597:                        cmd_form = $3;
        !           598:                }
        !           599:        | I
        !           600:                {
        !           601:                        cmd_type = TYPE_I;
        !           602:                }
        !           603:        | L
        !           604:                {
        !           605:                        cmd_type = TYPE_L;
        !           606:                        cmd_bytesz = NBBY;
        !           607:                }
        !           608:        | L SP byte_size
        !           609:                {
        !           610:                        cmd_type = TYPE_L;
        !           611:                        cmd_bytesz = $3;
        !           612:                }
        !           613:                /* this is for a bug in the BBN ftp */
        !           614:        | L byte_size
        !           615:                {
        !           616:                        cmd_type = TYPE_L;
        !           617:                        cmd_bytesz = $2;
        !           618:                }
1.1       cgd       619:        ;
                    620:
1.1.1.2 ! cjs       621: struct_code
        !           622:        : F
        !           623:                {
        !           624:                        $$ = STRU_F;
        !           625:                }
        !           626:        | R
        !           627:                {
        !           628:                        $$ = STRU_R;
        !           629:                }
        !           630:        | P
        !           631:                {
        !           632:                        $$ = STRU_P;
        !           633:                }
1.1       cgd       634:        ;
                    635:
1.1.1.2 ! cjs       636: mode_code
        !           637:        : S
        !           638:                {
        !           639:                        $$ = MODE_S;
        !           640:                }
        !           641:        | B
        !           642:                {
        !           643:                        $$ = MODE_B;
        !           644:                }
        !           645:        | C
        !           646:                {
        !           647:                        $$ = MODE_C;
        !           648:                }
1.1       cgd       649:        ;
                    650:
1.1.1.2 ! cjs       651: pathname
        !           652:        : pathstring
        !           653:                {
        !           654:                        /*
        !           655:                         * Problem: this production is used for all pathname
        !           656:                         * processing, but only gives a 550 error reply.
        !           657:                         * This is a valid reply in some cases but not in others.
        !           658:                         */
        !           659:                        if (logged_in && $1 && *$1 == '~') {
        !           660:                                glob_t gl;
        !           661:                                int flags =
        !           662:                                 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
        !           663:
        !           664:                                memset(&gl, 0, sizeof(gl));
        !           665:                                if (glob($1, flags, NULL, &gl) ||
        !           666:                                    gl.gl_pathc == 0) {
        !           667:                                        reply(550, "not found");
        !           668:                                        $$ = NULL;
        !           669:                                } else {
        !           670:                                        $$ = strdup(gl.gl_pathv[0]);
        !           671:                                }
        !           672:                                globfree(&gl);
        !           673:                                free($1);
        !           674:                        } else
        !           675:                                $$ = $1;
        !           676:                }
1.1       cgd       677:        ;
                    678:
1.1.1.2 ! cjs       679: pathstring
        !           680:        : STRING
1.1       cgd       681:        ;
                    682:
1.1.1.2 ! cjs       683: octal_number
        !           684:        : NUMBER
        !           685:                {
        !           686:                        int ret, dec, multby, digit;
1.1       cgd       687:
1.1.1.2 ! cjs       688:                        /*
        !           689:                         * Convert a number that was read as decimal number
        !           690:                         * to what it would be if it had been read as octal.
        !           691:                         */
        !           692:                        dec = $1;
        !           693:                        multby = 1;
        !           694:                        ret = 0;
        !           695:                        while (dec) {
        !           696:                                digit = dec%10;
        !           697:                                if (digit > 7) {
        !           698:                                        ret = -1;
        !           699:                                        break;
        !           700:                                }
        !           701:                                ret += digit * multby;
        !           702:                                multby *= 8;
        !           703:                                dec /= 10;
1.1       cgd       704:                        }
1.1.1.2 ! cjs       705:                        $$ = ret;
1.1       cgd       706:                }
                    707:        ;
                    708:
1.1.1.2 ! cjs       709:
        !           710: check_login
        !           711:        : /* empty */
        !           712:                {
        !           713:                        if (logged_in)
        !           714:                                $$ = 1;
        !           715:                        else {
        !           716:                                reply(530, "Please login with USER and PASS.");
        !           717:                                $$ = 0;
        !           718:                        }
1.1       cgd       719:                }
                    720:        ;
                    721:
                    722: %%
                    723:
                    724: extern jmp_buf errcatch;
                    725:
                    726: #define        CMD     0       /* beginning of command */
                    727: #define        ARGS    1       /* expect miscellaneous arguments */
                    728: #define        STR1    2       /* expect SP followed by STRING */
                    729: #define        STR2    3       /* expect STRING */
                    730: #define        OSTR    4       /* optional SP then STRING */
                    731: #define        ZSTR1   5       /* SP then optional STRING */
                    732: #define        ZSTR2   6       /* optional STRING after SP */
                    733: #define        SITECMD 7       /* SITE command */
                    734: #define        NSTR    8       /* Number followed by a string */
                    735:
                    736: struct tab {
                    737:        char    *name;
                    738:        short   token;
                    739:        short   state;
                    740:        short   implemented;    /* 1 if command is implemented */
                    741:        char    *help;
                    742: };
                    743:
                    744: struct tab cmdtab[] = {                /* In order defined in RFC 765 */
                    745:        { "USER", USER, STR1, 1,        "<sp> username" },
                    746:        { "PASS", PASS, ZSTR1, 1,       "<sp> password" },
                    747:        { "ACCT", ACCT, STR1, 0,        "(specify account)" },
                    748:        { "SMNT", SMNT, ARGS, 0,        "(structure mount)" },
                    749:        { "REIN", REIN, ARGS, 0,        "(reinitialize server state)" },
                    750:        { "QUIT", QUIT, ARGS, 1,        "(terminate service)", },
                    751:        { "PORT", PORT, ARGS, 1,        "<sp> b0, b1, b2, b3, b4" },
                    752:        { "PASV", PASV, ARGS, 1,        "(set server in passive mode)" },
                    753:        { "TYPE", TYPE, ARGS, 1,        "<sp> [ A | E | I | L ]" },
                    754:        { "STRU", STRU, ARGS, 1,        "(specify file structure)" },
                    755:        { "MODE", MODE, ARGS, 1,        "(specify transfer mode)" },
                    756:        { "RETR", RETR, STR1, 1,        "<sp> file-name" },
                    757:        { "STOR", STOR, STR1, 1,        "<sp> file-name" },
                    758:        { "APPE", APPE, STR1, 1,        "<sp> file-name" },
                    759:        { "MLFL", MLFL, OSTR, 0,        "(mail file)" },
                    760:        { "MAIL", MAIL, OSTR, 0,        "(mail to user)" },
                    761:        { "MSND", MSND, OSTR, 0,        "(mail send to terminal)" },
                    762:        { "MSOM", MSOM, OSTR, 0,        "(mail send to terminal or mailbox)" },
                    763:        { "MSAM", MSAM, OSTR, 0,        "(mail send to terminal and mailbox)" },
                    764:        { "MRSQ", MRSQ, OSTR, 0,        "(mail recipient scheme question)" },
                    765:        { "MRCP", MRCP, STR1, 0,        "(mail recipient)" },
                    766:        { "ALLO", ALLO, ARGS, 1,        "allocate storage (vacuously)" },
1.1.1.2 ! cjs       767:        { "REST", REST, ARGS, 1,        "<sp> offset (restart command)" },
1.1       cgd       768:        { "RNFR", RNFR, STR1, 1,        "<sp> file-name" },
                    769:        { "RNTO", RNTO, STR1, 1,        "<sp> file-name" },
                    770:        { "ABOR", ABOR, ARGS, 1,        "(abort operation)" },
                    771:        { "DELE", DELE, STR1, 1,        "<sp> file-name" },
                    772:        { "CWD",  CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
                    773:        { "XCWD", CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
                    774:        { "LIST", LIST, OSTR, 1,        "[ <sp> path-name ]" },
                    775:        { "NLST", NLST, OSTR, 1,        "[ <sp> path-name ]" },
                    776:        { "SITE", SITE, SITECMD, 1,     "site-cmd [ <sp> arguments ]" },
                    777:        { "SYST", SYST, ARGS, 1,        "(get type of operating system)" },
                    778:        { "STAT", STAT, OSTR, 1,        "[ <sp> path-name ]" },
                    779:        { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
                    780:        { "NOOP", NOOP, ARGS, 1,        "" },
                    781:        { "MKD",  MKD,  STR1, 1,        "<sp> path-name" },
                    782:        { "XMKD", MKD,  STR1, 1,        "<sp> path-name" },
                    783:        { "RMD",  RMD,  STR1, 1,        "<sp> path-name" },
                    784:        { "XRMD", RMD,  STR1, 1,        "<sp> path-name" },
                    785:        { "PWD",  PWD,  ARGS, 1,        "(return current directory)" },
                    786:        { "XPWD", PWD,  ARGS, 1,        "(return current directory)" },
                    787:        { "CDUP", CDUP, ARGS, 1,        "(change to parent directory)" },
                    788:        { "XCUP", CDUP, ARGS, 1,        "(change to parent directory)" },
                    789:        { "STOU", STOU, STR1, 1,        "<sp> file-name" },
                    790:        { "SIZE", SIZE, OSTR, 1,        "<sp> path-name" },
                    791:        { "MDTM", MDTM, OSTR, 1,        "<sp> path-name" },
                    792:        { NULL,   0,    0,    0,        0 }
                    793: };
                    794:
                    795: struct tab sitetab[] = {
                    796:        { "UMASK", UMASK, ARGS, 1,      "[ <sp> umask ]" },
                    797:        { "IDLE", IDLE, ARGS, 1,        "[ <sp> maximum-idle-time ]" },
                    798:        { "CHMOD", CHMOD, NSTR, 1,      "<sp> mode <sp> file-name" },
                    799:        { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
                    800:        { NULL,   0,    0,    0,        0 }
                    801: };
                    802:
1.1.1.2 ! cjs       803: static char    *copy __P((char *));
        !           804: static void     help __P((struct tab *, char *));
        !           805: static struct tab *
        !           806:                 lookup __P((struct tab *, char *));
        !           807: static void     sizecmd __P((char *));
        !           808: static void     toolong __P((int));
        !           809: static int      yylex __P((void));
        !           810:
        !           811: static struct tab *
1.1       cgd       812: lookup(p, cmd)
1.1.1.2 ! cjs       813:        struct tab *p;
1.1       cgd       814:        char *cmd;
                    815: {
                    816:
                    817:        for (; p->name != NULL; p++)
                    818:                if (strcmp(cmd, p->name) == 0)
                    819:                        return (p);
                    820:        return (0);
                    821: }
                    822:
                    823: #include <arpa/telnet.h>
                    824:
                    825: /*
                    826:  * getline - a hacked up version of fgets to ignore TELNET escape codes.
                    827:  */
                    828: char *
                    829: getline(s, n, iop)
                    830:        char *s;
1.1.1.2 ! cjs       831:        int n;
        !           832:        FILE *iop;
1.1       cgd       833: {
1.1.1.2 ! cjs       834:        int c;
1.1       cgd       835:        register char *cs;
                    836:
                    837:        cs = s;
                    838: /* tmpline may contain saved command from urgent mode interruption */
                    839:        for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
                    840:                *cs++ = tmpline[c];
                    841:                if (tmpline[c] == '\n') {
                    842:                        *cs++ = '\0';
                    843:                        if (debug)
                    844:                                syslog(LOG_DEBUG, "command: %s", s);
                    845:                        tmpline[0] = '\0';
                    846:                        return(s);
                    847:                }
                    848:                if (c == 0)
                    849:                        tmpline[0] = '\0';
                    850:        }
                    851:        while ((c = getc(iop)) != EOF) {
                    852:                c &= 0377;
                    853:                if (c == IAC) {
                    854:                    if ((c = getc(iop)) != EOF) {
                    855:                        c &= 0377;
                    856:                        switch (c) {
                    857:                        case WILL:
                    858:                        case WONT:
                    859:                                c = getc(iop);
                    860:                                printf("%c%c%c", IAC, DONT, 0377&c);
                    861:                                (void) fflush(stdout);
                    862:                                continue;
                    863:                        case DO:
                    864:                        case DONT:
                    865:                                c = getc(iop);
                    866:                                printf("%c%c%c", IAC, WONT, 0377&c);
                    867:                                (void) fflush(stdout);
                    868:                                continue;
                    869:                        case IAC:
                    870:                                break;
                    871:                        default:
                    872:                                continue;       /* ignore command */
                    873:                        }
                    874:                    }
                    875:                }
                    876:                *cs++ = c;
                    877:                if (--n <= 0 || c == '\n')
                    878:                        break;
                    879:        }
                    880:        if (c == EOF && cs == s)
                    881:                return (NULL);
                    882:        *cs++ = '\0';
1.1.1.2 ! cjs       883:        if (debug) {
        !           884:                if (!guest && strncasecmp("pass ", s, 5) == 0) {
        !           885:                        /* Don't syslog passwords */
        !           886:                        syslog(LOG_DEBUG, "command: %.5s ???", s);
        !           887:                } else {
        !           888:                        register char *cp;
        !           889:                        register int len;
        !           890:
        !           891:                        /* Don't syslog trailing CR-LF */
        !           892:                        len = strlen(s);
        !           893:                        cp = s + len - 1;
        !           894:                        while (cp >= s && (*cp == '\n' || *cp == '\r')) {
        !           895:                                --cp;
        !           896:                                --len;
        !           897:                        }
        !           898:                        syslog(LOG_DEBUG, "command: %.*s", len, s);
        !           899:                }
        !           900:        }
1.1       cgd       901:        return (s);
                    902: }
                    903:
                    904: static void
1.1.1.2 ! cjs       905: toolong(signo)
        !           906:        int signo;
1.1       cgd       907: {
                    908:
                    909:        reply(421,
1.1.1.2 ! cjs       910:            "Timeout (%d seconds): closing control connection.", timeout);
        !           911:        if (logging)
        !           912:                syslog(LOG_INFO, "User %s timed out after %d seconds",
        !           913:                    (pw ? pw -> pw_name : "unknown"), timeout);
1.1       cgd       914:        dologout(1);
                    915: }
                    916:
1.1.1.2 ! cjs       917: static int
1.1       cgd       918: yylex()
                    919: {
                    920:        static int cpos, state;
1.1.1.2 ! cjs       921:        char *cp, *cp2;
        !           922:        struct tab *p;
1.1       cgd       923:        int n;
1.1.1.2 ! cjs       924:        char c;
1.1       cgd       925:
                    926:        for (;;) {
                    927:                switch (state) {
                    928:
                    929:                case CMD:
                    930:                        (void) signal(SIGALRM, toolong);
                    931:                        (void) alarm((unsigned) timeout);
                    932:                        if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
                    933:                                reply(221, "You could at least say goodbye.");
                    934:                                dologout(0);
                    935:                        }
                    936:                        (void) alarm(0);
                    937: #ifdef SETPROCTITLE
                    938:                        if (strncasecmp(cbuf, "PASS", 4) != NULL)
                    939:                                setproctitle("%s: %s", proctitle, cbuf);
                    940: #endif /* SETPROCTITLE */
1.1.1.2 ! cjs       941:                        if ((cp = strchr(cbuf, '\r'))) {
1.1       cgd       942:                                *cp++ = '\n';
                    943:                                *cp = '\0';
                    944:                        }
                    945:                        if ((cp = strpbrk(cbuf, " \n")))
                    946:                                cpos = cp - cbuf;
                    947:                        if (cpos == 0)
                    948:                                cpos = 4;
                    949:                        c = cbuf[cpos];
                    950:                        cbuf[cpos] = '\0';
                    951:                        upper(cbuf);
                    952:                        p = lookup(cmdtab, cbuf);
                    953:                        cbuf[cpos] = c;
                    954:                        if (p != 0) {
                    955:                                if (p->implemented == 0) {
                    956:                                        nack(p->name);
                    957:                                        longjmp(errcatch,0);
                    958:                                        /* NOTREACHED */
                    959:                                }
                    960:                                state = p->state;
1.1.1.2 ! cjs       961:                                yylval.s = p->name;
1.1       cgd       962:                                return (p->token);
                    963:                        }
                    964:                        break;
                    965:
                    966:                case SITECMD:
                    967:                        if (cbuf[cpos] == ' ') {
                    968:                                cpos++;
                    969:                                return (SP);
                    970:                        }
                    971:                        cp = &cbuf[cpos];
                    972:                        if ((cp2 = strpbrk(cp, " \n")))
                    973:                                cpos = cp2 - cbuf;
                    974:                        c = cbuf[cpos];
                    975:                        cbuf[cpos] = '\0';
                    976:                        upper(cp);
                    977:                        p = lookup(sitetab, cp);
                    978:                        cbuf[cpos] = c;
                    979:                        if (p != 0) {
                    980:                                if (p->implemented == 0) {
                    981:                                        state = CMD;
                    982:                                        nack(p->name);
                    983:                                        longjmp(errcatch,0);
                    984:                                        /* NOTREACHED */
                    985:                                }
                    986:                                state = p->state;
1.1.1.2 ! cjs       987:                                yylval.s = p->name;
1.1       cgd       988:                                return (p->token);
                    989:                        }
                    990:                        state = CMD;
                    991:                        break;
                    992:
                    993:                case OSTR:
                    994:                        if (cbuf[cpos] == '\n') {
                    995:                                state = CMD;
                    996:                                return (CRLF);
                    997:                        }
                    998:                        /* FALLTHROUGH */
                    999:
                   1000:                case STR1:
                   1001:                case ZSTR1:
                   1002:                dostr1:
                   1003:                        if (cbuf[cpos] == ' ') {
                   1004:                                cpos++;
                   1005:                                state = state == OSTR ? STR2 : ++state;
                   1006:                                return (SP);
                   1007:                        }
                   1008:                        break;
                   1009:
                   1010:                case ZSTR2:
                   1011:                        if (cbuf[cpos] == '\n') {
                   1012:                                state = CMD;
                   1013:                                return (CRLF);
                   1014:                        }
                   1015:                        /* FALLTHROUGH */
                   1016:
                   1017:                case STR2:
                   1018:                        cp = &cbuf[cpos];
                   1019:                        n = strlen(cp);
                   1020:                        cpos += n - 1;
                   1021:                        /*
                   1022:                         * Make sure the string is nonempty and \n terminated.
                   1023:                         */
                   1024:                        if (n > 1 && cbuf[cpos] == '\n') {
                   1025:                                cbuf[cpos] = '\0';
1.1.1.2 ! cjs      1026:                                yylval.s = copy(cp);
1.1       cgd      1027:                                cbuf[cpos] = '\n';
                   1028:                                state = ARGS;
                   1029:                                return (STRING);
                   1030:                        }
                   1031:                        break;
                   1032:
                   1033:                case NSTR:
                   1034:                        if (cbuf[cpos] == ' ') {
                   1035:                                cpos++;
                   1036:                                return (SP);
                   1037:                        }
                   1038:                        if (isdigit(cbuf[cpos])) {
                   1039:                                cp = &cbuf[cpos];
                   1040:                                while (isdigit(cbuf[++cpos]))
                   1041:                                        ;
                   1042:                                c = cbuf[cpos];
                   1043:                                cbuf[cpos] = '\0';
1.1.1.2 ! cjs      1044:                                yylval.i = atoi(cp);
1.1       cgd      1045:                                cbuf[cpos] = c;
                   1046:                                state = STR1;
                   1047:                                return (NUMBER);
                   1048:                        }
                   1049:                        state = STR1;
                   1050:                        goto dostr1;
                   1051:
                   1052:                case ARGS:
                   1053:                        if (isdigit(cbuf[cpos])) {
                   1054:                                cp = &cbuf[cpos];
                   1055:                                while (isdigit(cbuf[++cpos]))
                   1056:                                        ;
                   1057:                                c = cbuf[cpos];
                   1058:                                cbuf[cpos] = '\0';
1.1.1.2 ! cjs      1059:                                yylval.i = atoi(cp);
1.1       cgd      1060:                                cbuf[cpos] = c;
                   1061:                                return (NUMBER);
                   1062:                        }
                   1063:                        switch (cbuf[cpos++]) {
                   1064:
                   1065:                        case '\n':
                   1066:                                state = CMD;
                   1067:                                return (CRLF);
                   1068:
                   1069:                        case ' ':
                   1070:                                return (SP);
                   1071:
                   1072:                        case ',':
                   1073:                                return (COMMA);
                   1074:
                   1075:                        case 'A':
                   1076:                        case 'a':
                   1077:                                return (A);
                   1078:
                   1079:                        case 'B':
                   1080:                        case 'b':
                   1081:                                return (B);
                   1082:
                   1083:                        case 'C':
                   1084:                        case 'c':
                   1085:                                return (C);
                   1086:
                   1087:                        case 'E':
                   1088:                        case 'e':
                   1089:                                return (E);
                   1090:
                   1091:                        case 'F':
                   1092:                        case 'f':
                   1093:                                return (F);
                   1094:
                   1095:                        case 'I':
                   1096:                        case 'i':
                   1097:                                return (I);
                   1098:
                   1099:                        case 'L':
                   1100:                        case 'l':
                   1101:                                return (L);
                   1102:
                   1103:                        case 'N':
                   1104:                        case 'n':
                   1105:                                return (N);
                   1106:
                   1107:                        case 'P':
                   1108:                        case 'p':
                   1109:                                return (P);
                   1110:
                   1111:                        case 'R':
                   1112:                        case 'r':
                   1113:                                return (R);
                   1114:
                   1115:                        case 'S':
                   1116:                        case 's':
                   1117:                                return (S);
                   1118:
                   1119:                        case 'T':
                   1120:                        case 't':
                   1121:                                return (T);
                   1122:
                   1123:                        }
                   1124:                        break;
                   1125:
                   1126:                default:
                   1127:                        fatal("Unknown state in scanner.");
                   1128:                }
                   1129:                yyerror((char *) 0);
                   1130:                state = CMD;
                   1131:                longjmp(errcatch,0);
                   1132:        }
                   1133: }
                   1134:
1.1.1.2 ! cjs      1135: void
1.1       cgd      1136: upper(s)
1.1.1.2 ! cjs      1137:        char *s;
1.1       cgd      1138: {
                   1139:        while (*s != '\0') {
                   1140:                if (islower(*s))
                   1141:                        *s = toupper(*s);
                   1142:                s++;
                   1143:        }
                   1144: }
                   1145:
1.1.1.2 ! cjs      1146: static char *
1.1       cgd      1147: copy(s)
                   1148:        char *s;
                   1149: {
                   1150:        char *p;
                   1151:
                   1152:        p = malloc((unsigned) strlen(s) + 1);
                   1153:        if (p == NULL)
                   1154:                fatal("Ran out of memory.");
                   1155:        (void) strcpy(p, s);
                   1156:        return (p);
                   1157: }
                   1158:
1.1.1.2 ! cjs      1159: static void
1.1       cgd      1160: help(ctab, s)
                   1161:        struct tab *ctab;
                   1162:        char *s;
                   1163: {
1.1.1.2 ! cjs      1164:        struct tab *c;
        !          1165:        int width, NCMDS;
1.1       cgd      1166:        char *type;
                   1167:
                   1168:        if (ctab == sitetab)
                   1169:                type = "SITE ";
                   1170:        else
                   1171:                type = "";
                   1172:        width = 0, NCMDS = 0;
                   1173:        for (c = ctab; c->name != NULL; c++) {
                   1174:                int len = strlen(c->name);
                   1175:
                   1176:                if (len > width)
                   1177:                        width = len;
                   1178:                NCMDS++;
                   1179:        }
                   1180:        width = (width + 8) &~ 7;
                   1181:        if (s == 0) {
1.1.1.2 ! cjs      1182:                int i, j, w;
1.1       cgd      1183:                int columns, lines;
                   1184:
                   1185:                lreply(214, "The following %scommands are recognized %s.",
                   1186:                    type, "(* =>'s unimplemented)");
                   1187:                columns = 76 / width;
                   1188:                if (columns == 0)
                   1189:                        columns = 1;
                   1190:                lines = (NCMDS + columns - 1) / columns;
                   1191:                for (i = 0; i < lines; i++) {
                   1192:                        printf("   ");
                   1193:                        for (j = 0; j < columns; j++) {
                   1194:                                c = ctab + j * lines + i;
                   1195:                                printf("%s%c", c->name,
                   1196:                                        c->implemented ? ' ' : '*');
                   1197:                                if (c + lines >= &ctab[NCMDS])
                   1198:                                        break;
                   1199:                                w = strlen(c->name) + 1;
                   1200:                                while (w < width) {
                   1201:                                        putchar(' ');
                   1202:                                        w++;
                   1203:                                }
                   1204:                        }
                   1205:                        printf("\r\n");
                   1206:                }
                   1207:                (void) fflush(stdout);
                   1208:                reply(214, "Direct comments to ftp-bugs@%s.", hostname);
                   1209:                return;
                   1210:        }
                   1211:        upper(s);
                   1212:        c = lookup(ctab, s);
                   1213:        if (c == (struct tab *)0) {
                   1214:                reply(502, "Unknown command %s.", s);
                   1215:                return;
                   1216:        }
                   1217:        if (c->implemented)
                   1218:                reply(214, "Syntax: %s%s %s", type, c->name, c->help);
                   1219:        else
                   1220:                reply(214, "%s%-*s\t%s; unimplemented.", type, width,
                   1221:                    c->name, c->help);
                   1222: }
                   1223:
1.1.1.2 ! cjs      1224: static void
1.1       cgd      1225: sizecmd(filename)
1.1.1.2 ! cjs      1226:        char *filename;
1.1       cgd      1227: {
                   1228:        switch (type) {
                   1229:        case TYPE_L:
                   1230:        case TYPE_I: {
                   1231:                struct stat stbuf;
1.1.1.2 ! cjs      1232:                if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1.1       cgd      1233:                        reply(550, "%s: not a plain file.", filename);
                   1234:                else
1.1.1.2 ! cjs      1235:                        reply(213, "%qu", stbuf.st_size);
        !          1236:                break; }
1.1       cgd      1237:        case TYPE_A: {
                   1238:                FILE *fin;
1.1.1.2 ! cjs      1239:                int c;
        !          1240:                off_t count;
1.1       cgd      1241:                struct stat stbuf;
                   1242:                fin = fopen(filename, "r");
                   1243:                if (fin == NULL) {
                   1244:                        perror_reply(550, filename);
                   1245:                        return;
                   1246:                }
1.1.1.2 ! cjs      1247:                if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
1.1       cgd      1248:                        reply(550, "%s: not a plain file.", filename);
                   1249:                        (void) fclose(fin);
                   1250:                        return;
                   1251:                }
                   1252:
                   1253:                count = 0;
                   1254:                while((c=getc(fin)) != EOF) {
                   1255:                        if (c == '\n')  /* will get expanded to \r\n */
                   1256:                                count++;
                   1257:                        count++;
                   1258:                }
                   1259:                (void) fclose(fin);
                   1260:
1.1.1.2 ! cjs      1261:                reply(213, "%qd", count);
        !          1262:                break; }
1.1       cgd      1263:        default:
                   1264:                reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
                   1265:        }
                   1266: }

CVSweb <webmaster@jp.NetBSD.org>