Home | History | Annotate | Download | only in in.ftpd
      1 /*
      2  * Copyright 2002-2003 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
      7 
      8 /****************************************************************************
      9 
     10   Copyright (c) 1999,2000 WU-FTPD Development Group.
     11   All rights reserved.
     12 
     13   Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
     14     The Regents of the University of California.
     15   Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
     16   Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
     17   Portions Copyright (c) 1989 Massachusetts Institute of Technology.
     18   Portions Copyright (c) 1998 Sendmail, Inc.
     19   Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P.  Allman.
     20   Portions Copyright (c) 1997 by Stan Barber.
     21   Portions Copyright (c) 1997 by Kent Landfield.
     22   Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
     23     Free Software Foundation, Inc.
     24 
     25   Use and distribution of this software and its source code are governed
     26   by the terms and conditions of the WU-FTPD Software License ("LICENSE").
     27 
     28   If you did not receive a copy of the license, it may be obtained online
     29   at http://www.wu-ftpd.org/license.html.
     30 
     31   $Id: extensions.c,v 1.48 2000/07/01 18:17:38 wuftpd Exp $
     32 
     33 ****************************************************************************/
     34 #include "config.h"
     35 
     36 #include <stdio.h>
     37 #include <errno.h>
     38 #include <string.h>
     39 
     40 #ifdef HAVE_SYS_SYSLOG_H
     41 #include <sys/syslog.h>
     42 #endif
     43 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
     44 #include <syslog.h>
     45 #endif
     46 
     47 #ifdef TIME_WITH_SYS_TIME
     48 #include <time.h>
     49 #include <sys/time.h>
     50 #else
     51 #ifdef HAVE_SYS_TIME_H
     52 #include <sys/time.h>
     53 #else
     54 #include <time.h>
     55 #endif
     56 #endif
     57 #include <pwd.h>
     58 #include <setjmp.h>
     59 #include <grp.h>
     60 
     61 #include <sys/types.h>
     62 #include <sys/stat.h>
     63 #include <sys/file.h>
     64 #include <sys/param.h>
     65 
     66 #ifdef HAVE_SYS_FS_UFS_QUOTA_H
     67 #include <sys/fs/ufs_quota.h>
     68 #elif defined(HAVE_UFS_UFS_QUOTA_H)
     69 #include <ufs/ufs/quota.h>
     70 #elif defined(HAVE_UFS_QUOTA_H)
     71 #include <ufs/quota.h>
     72 #elif defined(HAVE_SYS_MNTENT_H)
     73 #include <sys/mntent.h>
     74 #elif defined(HAVE_SYS_MNTTAB_H)
     75 #include <sys/mnttab.h>
     76 #endif
     77 
     78 #if defined(HAVE_STATVFS)
     79 #include <sys/statvfs.h>
     80 #elif defined(HAVE_SYS_VFS)
     81 #include <sys/vfs.h>
     82 #elif defined(HAVE_SYS_MOUNT)
     83 #include <sys/mount.h>
     84 #endif
     85 
     86 #include <arpa/ftp.h>
     87 
     88 #ifdef HAVE_PATHS_H
     89 #include <paths.h>
     90 #endif
     91 #include "pathnames.h"
     92 #include "extensions.h"
     93 #include "wu_fnmatch.h"
     94 #include "proto.h"
     95 
     96 #if defined(HAVE_FTW)
     97 #include <ftw.h>
     98 #else
     99 #include "support/ftw.h"
    100 #endif
    101 
    102 #ifdef QUOTA
    103 struct dqblk quota;
    104 char *time_quota(long curstate, long softlimit, long timelimit, char *timeleft);
    105 #endif
    106 
    107 #ifdef HAVE_REGEX_H
    108 #include <regex.h>
    109 #endif
    110 
    111 #if defined(HAVE_REGEX) && defined(SVR4) && ! (defined(NO_LIBGEN))
    112 #include <libgen.h>
    113 #endif
    114 
    115 extern int type, transflag, ftwflag, authenticated, autospout_free, data,
    116     pdata, anonymous, guest;
    117 extern char chroot_path[], guestpw[];
    118 
    119 #ifdef TRANSFER_COUNT
    120 extern off_t data_count_in;
    121 extern off_t data_count_out;
    122 #ifdef TRANSFER_LIMIT
    123 extern off_t data_limit_raw_in;
    124 extern off_t data_limit_raw_out;
    125 extern off_t data_limit_raw_total;
    126 extern off_t data_limit_data_in;
    127 extern off_t data_limit_data_out;
    128 extern off_t data_limit_data_total;
    129 #ifdef RATIO /* 1998/08/06 K.Wakui */
    130 #define TRUNC_KB(n)   ((n)/1024+(((n)%1024)?1:0))
    131 extern time_t	login_time;
    132 extern time_t	limit_time;
    133 extern off_t    total_free_dl;
    134 extern int      upload_download_rate;
    135 #endif /* RATIO */
    136 #endif
    137 #endif
    138 
    139 #ifdef OTHER_PASSWD
    140 #include "getpwnam.h"
    141 extern char _path_passwd[];
    142 #endif
    143 
    144 #ifdef LOG_FAILED
    145 extern char the_user[];
    146 #endif
    147 
    148 extern char *globerr, remotehost[];
    149 #ifdef THROUGHPUT
    150 extern char remoteaddr[];
    151 #endif
    152 
    153 #ifndef HAVE_REGEX
    154 char *re_comp(const char *regex);
    155 int re_exec(const char *p1);
    156 #endif
    157 
    158 char shuttime[30], denytime[30], disctime[30];
    159 
    160 FILE *dout;
    161 
    162 time_t newer_time;
    163 
    164 int show_fullinfo;
    165 
    166 /* This always was a bug, because neither st_size nor time_t were required to
    167    be compatible with int, but needs fixing properly for C9X. */
    168 
    169 /* Some systems use one format, some another.  This takes care of the garbage */
    170 /* Do the system specific stuff only if we aren't autoconfed */
    171 #if !defined(L_FORMAT)
    172 #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
    173 #define L_FORMAT "qd"
    174 #else
    175 #define L_FORMAT "d"
    176 #endif
    177 #endif
    178 #if !defined(T_FORMAT)
    179 #define T_FORMAT "d"
    180 #endif
    181 #if !defined(PW_UID_FORMAT)
    182 #define PW_UID_FORMAT "d"
    183 #endif
    184 #if !defined(GR_GID_FORMAT)
    185 #define GR_GID_FORMAT "d"
    186 #endif
    187 
    188 int snprintf(char *str, size_t count, const char *fmt,...);
    189 
    190 #ifdef SITE_NEWER
    191 int check_newer(const char *path, const struct stat *st, int flag)
    192 {
    193     if (st->st_mtime > newer_time) {
    194 	if (show_fullinfo != 0) {
    195 	    if (flag == FTW_F || flag == FTW_D) {
    196 		fprintf(dout, "%s %" L_FORMAT " %" T_FORMAT " %s\n",
    197 			flag == FTW_F ? "F" : "D",
    198 			st->st_size, st->st_mtime, path);
    199 	    }
    200 	}
    201 	else if (flag == FTW_F)
    202 	    fprintf(dout, "%s\n", path);
    203     }
    204 
    205     /* When an ABOR has been received (which sets ftwflag > 1) return a
    206      * non-zero value which causes ftw to stop tree traversal and return.
    207      */
    208 
    209     return (ftwflag > 1 ? 1 : 0);
    210 }
    211 #endif
    212 
    213 #if defined(HAVE_STATVFS)
    214 long getSize(char *s)
    215 {
    216     struct statvfs buf;
    217 
    218     if (statvfs(s, &buf) != 0)
    219 	return (0);
    220 
    221     return (buf.f_bavail * buf.f_frsize / 1024);
    222 }
    223 #elif defined(HAVE_SYS_VFS) || defined (HAVE_SYS_MOUNT)
    224 long getSize(char *s)
    225 {
    226     struct statfs buf;
    227 
    228     if (statfs(s, &buf) != 0)
    229 	return (0);
    230 
    231     return (buf.f_bavail * buf.f_bsize / 1024);
    232 }
    233 #endif
    234 
    235 /*************************************************************************/
    236 /* FUNCTION  : msg_massage                                               */
    237 /* PURPOSE   : Scan a message line for magic cookies, replacing them as  */
    238 /*             needed.                                                   */
    239 /* ARGUMENTS : pointer input and output buffers                          */
    240 /*************************************************************************/
    241 
    242 void msg_massage(const char *inbuf, char *outbuf, size_t outlen)
    243 {
    244     const char *inptr = inbuf;
    245     char *outptr = outbuf;
    246 #ifdef QUOTA
    247     char timeleft[80];
    248 #endif
    249     char buffer[MAXPATHLEN];
    250     time_t curtime;
    251     int limit;
    252 #ifndef LOG_FAILED
    253     extern struct passwd *pw;
    254 #endif
    255     struct aclmember *entry;
    256 
    257 #ifdef VIRTUAL
    258     extern int virtual_mode;
    259     extern int virtual_ftpaccess;
    260     extern char virtual_email[];
    261 #endif
    262     extern char hostname[];
    263     extern char authuser[];
    264 
    265     (void) acl_getclass(buffer);
    266     limit = acl_getlimit(buffer, NULL);
    267 
    268     while ((outlen > 1) && (*inptr != '\0')) {
    269 	if (*inptr != '%') {
    270 	    *outptr++ = *inptr;
    271 	    outlen -= 1;
    272 	}
    273 	else {
    274 	    entry = NULL;
    275 	    switch (*++inptr) {
    276 	    case 'E':
    277 #ifdef VIRTUAL
    278 		if (virtual_mode && !virtual_ftpaccess && virtual_email[0] != '\0')
    279 		    snprintf(outptr, outlen, "%s", virtual_email);
    280 		else
    281 #endif
    282 		if ((getaclentry("email", &entry)) && ARG0)
    283 		    snprintf(outptr, outlen, "%s", ARG0);
    284 		else
    285 		    *outptr = '\0';
    286 		break;
    287 
    288 	    case 'N':
    289 		snprintf(outptr, outlen, "%d", acl_countusers(buffer));
    290 		break;
    291 
    292 	    case 'M':
    293 		if (limit == -1)
    294 		    strncpy(outptr, "unlimited", outlen);
    295 		else
    296 		    snprintf(outptr, outlen, "%d", limit);
    297 		break;
    298 
    299 	    case 'T':
    300 		(void) time(&curtime);
    301 		strncpy(outptr, ctime(&curtime), outlen);
    302 		if (outlen > 24)
    303 		    *(outptr + 24) = '\0';
    304 		break;
    305 
    306 	    case 'F':
    307 #if defined(HAVE_STATVFS) || defined(HAVE_SYS_VFS) || defined(HAVE_SYS_MOUNT)
    308 		snprintf(outptr, outlen, "%lu", (long) getSize("."));
    309 #else
    310 		*outptr = '\0';
    311 #endif
    312 		break;
    313 
    314 	    case 'C':
    315 #ifdef HAVE_GETCWD
    316 		(void) getcwd(outptr, outlen);
    317 #else
    318 #error	wu-ftpd on this platform has security deficiencies!!!
    319 		(void) getwd(outptr);
    320 #endif
    321 		break;
    322 
    323 	    case 'R':
    324 		strncpy(outptr, remotehost, outlen);
    325 		break;
    326 
    327 	    case 'L':
    328 		strncpy(outptr, hostname, outlen);
    329 		break;
    330 
    331 	    case 'U':
    332 		if (xferdone && anonymous)
    333 		    strncpy(outptr, guestpw, outlen);
    334 		else
    335 #ifdef LOG_FAILED
    336 		    strncpy(outptr, the_user, outlen);
    337 #else /* LOG_FAILED */
    338 		    strncpy(outptr,
    339 			    (pw == NULL) ? "[unknown]" : pw->pw_name, outlen);
    340 #endif /* LOG_FAILED */
    341 		break;
    342 
    343 	    case 's':
    344 		strncpy(outptr, shuttime, outlen);
    345 		if (outlen > 24)
    346 		    *(outptr + 24) = '\0';
    347 		break;
    348 
    349 	    case 'd':
    350 		strncpy(outptr, disctime, outlen);
    351 		if (outlen > 24)
    352 		    *(outptr + 24) = '\0';
    353 		break;
    354 
    355 	    case 'r':
    356 		strncpy(outptr, denytime, outlen);
    357 		if (outlen > 24)
    358 		    *(outptr + 24) = '\0';
    359 		break;
    360 
    361 /* KH : cookie %u for RFC931 name */
    362 	    case 'u':
    363 		if (authenticated)
    364 		    strncpy(outptr, authuser, outlen);
    365 		else {
    366 		    if (xferdone)
    367 			snprintf(outptr, outlen, "%c", '*');
    368 		    else
    369 			strncpy(outptr, "[unknown]", outlen);
    370 		}
    371 		break;
    372 
    373 #ifdef QUOTA
    374 	    case 'B':
    375 #ifdef QUOTA_BLOCKS		/* 1024-blocks instead of 512-blocks */
    376 		snprintf(outptr, outlen, "%ld", quota.dqb_bhardlimit % 2 ?
    377 			 (long) (quota.dqb_bhardlimit / 2 + 1) : (long) (quota.dqb_bhardlimit / 2));
    378 #else
    379 		snprintf(outptr, outlen, "%ld", (long) quota.dqb_bhardlimit);
    380 #endif
    381 		break;
    382 
    383 	    case 'b':
    384 #ifdef QUOTA_BLOCKS		/* 1024-blocks instead of 512-blocks */
    385 		snprintf(outptr, outlen, "%ld", quota.dqb_bsoftlimit % 2 ?
    386 			 (long) (quota.dqb_bsoftlimit / 2 + 1) : (long) (quota.dqb_bsoftlimit / 2));
    387 #else
    388 		snprintf(outptr, outlen, "%ld", (long) quota.dqb_bsoftlimit);
    389 #endif
    390 		break;
    391 
    392 	    case 'Q':
    393 #ifdef QUOTA_BLOCKS		/* 1024-blocks instead of 512-blocks */
    394 		snprintf(outptr, outlen, "%ld", quota.dqb_curblocks % 2 ?
    395 			 (long) (quota.dqb_curblocks / 2 + 1) : (long) (quota.dqb_curblocks / 2));
    396 #else
    397 		snprintf(outptr, outlen, "%ld", quota.dqb_curblocks);
    398 #endif
    399 		break;
    400 
    401 	    case 'I':
    402 #if defined(QUOTA_INODE)
    403 		snprintf(outptr, outlen, "%d", quota.dqb_ihardlimit);
    404 #else
    405 		snprintf(outptr, outlen, "%ld", (long) quota.dqb_fhardlimit);
    406 #endif
    407 		break;
    408 
    409 	    case 'i':
    410 #if defined(QUOTA_INODE)
    411 		snprintf(outptr, outlen, "%d", quota.dqb_isoftlimit);
    412 #else
    413 		snprintf(outptr, outlen, "%ld", (long) quota.dqb_fsoftlimit);
    414 #endif
    415 		break;
    416 
    417 	    case 'q':
    418 #if defined(QUOTA_INODE)
    419 		snprintf(outptr, outlen, "%d", quota.dqb_curinodes);
    420 #else
    421 		snprintf(outptr, outlen, "%ld", (long) quota.dqb_curfiles);
    422 #endif
    423 		break;
    424 
    425 	    case 'H':
    426 		time_quota(quota.dqb_curblocks, quota.dqb_bsoftlimit,
    427 #if defined(QUOTA_INODE)
    428 			   quota.dqb_btime, timeleft);
    429 #else
    430 			   quota.dqb_btimelimit, timeleft);
    431 #endif
    432 		strncpy(outptr, timeleft, outlen);
    433 		break;
    434 
    435 	    case 'h':
    436 #if defined(QUOTA_INODE)
    437 		time_quota(quota.dqb_curinodes, quota.dqb_isoftlimit,
    438 			   quota.dqb_itime, timeleft);
    439 #else
    440 		time_quota(quota.dqb_curfiles, quota.dqb_fsoftlimit,
    441 			   quota.dqb_ftimelimit, timeleft);
    442 #endif
    443 		strncpy(outptr, timeleft, outlen);
    444 		break;
    445 #endif /* QUOTA */
    446 
    447 	    case '%':
    448 		*outptr++ = '%';
    449 		outlen -= 1;
    450 		*outptr = '\0';
    451 		break;
    452 
    453 #ifdef TRANSFER_COUNT
    454 #ifdef TRANSFER_LIMIT
    455 #ifdef RATIO
    456             case 'x':
    457                 switch (*++inptr) {
    458                 case 'u':       /* upload bytes */
    459                     sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_count_in) );
    460                     break;
    461                 case 'd':       /* download bytes */
    462                     sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_count_out) );
    463                     break;
    464                 case 'R':       /* rate 1:n */
    465                     if( upload_download_rate > 0 ) {
    466                         sprintf(outptr,"%d", upload_download_rate );
    467                     }
    468                     else {
    469                         strcpy(outptr,"free");
    470                     }
    471                     break;
    472                 case 'c':       /* credit bytes */
    473                     if( upload_download_rate > 0 ) {
    474                         off_t credit=( data_count_in * upload_download_rate) - (data_count_out - total_free_dl);
    475                         sprintf(outptr,"%" L_FORMAT, TRUNC_KB(credit) );
    476                     }
    477                     else {
    478                         strcpy(outptr,"unlimited");
    479                     }
    480                     break;
    481                 case 'T':       /* time limit (minutes) */
    482                     if( limit_time > 0 ) {
    483                         sprintf(outptr,"%d", limit_time );
    484                     }
    485                     else {
    486                         strcpy(outptr,"unlimited");
    487                     }
    488                     break;
    489                 case 'E':       /* elapsed time from loggedin (minutes) */
    490                     sprintf(outptr,"%d", (time(NULL)-login_time)/60 );
    491                     break;
    492                 case 'L':       /* times left until force logout (minutes) */
    493                     if( limit_time > 0 ) {
    494                         sprintf(outptr,"%d", limit_time-(time(NULL)-login_time)/60 );
    495                     }
    496                     else {
    497                         strcpy(outptr,"unlimited");
    498                     }
    499                     break;
    500                 case 'U':       /* upload limit */
    501 		    if( data_limit_raw_in > 0 ) {
    502 			sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_in));
    503 		    }
    504 		    else if( data_limit_data_in > 0 ) {
    505 			sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_in));
    506 		    }
    507 		    else if( data_limit_raw_total > 0 ) {
    508 			sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_total));
    509 		    }
    510 		    else if( data_limit_data_total > 0 ) {
    511 			sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_total));
    512 		    }
    513 		    else {
    514 			strcpy(outptr, "unlimited");
    515 		    }
    516                     break;
    517                 case 'D':       /* download limit */
    518 		    if( data_limit_raw_out > 0 ) {
    519 			sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_out));
    520 		    }
    521 		    else if( data_limit_data_out > 0 ) {
    522 			sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_out));
    523 		    }
    524 		    else if( data_limit_raw_total > 0 ) {
    525 			sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_total));
    526 		    }
    527 		    else if( data_limit_data_total > 0 ) {
    528 			sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_total));
    529 		    }
    530 		    else {
    531 			strcpy(outptr, "unlimited");
    532 		    }
    533                     break;
    534                 default:
    535                     strcpy(outptr,"%??");
    536                     break;
    537                 }
    538                 break;
    539 #endif /* RATIO */
    540 #endif
    541 #endif
    542 		/* File transfer logging (xferlog) */
    543 		case 'X':
    544 		    if (xferdone) {  /* only if a transfer has just occurred */
    545 			switch (*++inptr) {
    546 			case 't':
    547 			    snprintf(outptr, outlen, "%d", xfervalues.transfer_time);
    548 			    break;
    549 			case 's':
    550 			    snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.filesize);
    551 			    break;
    552 			case 'n':
    553 			    snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.transfer_bytes);
    554 			    break;
    555 			case 'P': /* absolute pathname */
    556 			    /* FALLTHROUGH */
    557 			case 'p': /* chroot-relative pathname */
    558 			{
    559 			    char namebuf[MAXPATHLEN];
    560 			    int loop;
    561 
    562 			    if (*inptr == 'P')
    563 				wu_realpath(xfervalues.filename, namebuf, chroot_path);
    564 			    else
    565 				fb_realpath(xfervalues.filename, namebuf);
    566 			    for (loop = 0; namebuf[loop]; loop++) {
    567 				if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
    568 				    namebuf[loop] = '_';
    569 			    }
    570 			    snprintf(outptr, outlen, "%s", namebuf);
    571 			    break;
    572 			}
    573 			case 'y':
    574 			    snprintf(outptr, outlen, "%c", xfervalues.transfer_type);
    575 			    break;
    576 			case 'f':
    577 			    snprintf(outptr, outlen, "%s", xfervalues.special_action);
    578 			    break;
    579 			case 'd':
    580 			    snprintf(outptr, outlen, "%c", xfervalues.transfer_direction);
    581 			    break;
    582 			case 'm':
    583 			    snprintf(outptr, outlen, "%c", xfervalues.access_mode);
    584 			    break;
    585 			case 'a':
    586 			    snprintf(outptr, outlen, "%d", xfervalues.auth);
    587 			    break;
    588 			case 'r':
    589 			    snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.restart_offset);
    590 			    break;
    591 			case 'c':
    592 			    snprintf(outptr, outlen, "%c", xfervalues.completion);
    593 			    break;
    594 			default:
    595 			    snprintf(outptr, outlen, "%%X%c", *inptr);
    596 			    break;
    597 			}
    598 		    }
    599 		    else
    600 			snprintf(outptr, outlen, "%%%c", *inptr);
    601 		    break;
    602 
    603 	    default:
    604 		*outptr++ = '%';
    605 		outlen -= 1;
    606 		if (outlen > 1) {
    607 		    *outptr++ = *inptr;
    608 		    outlen -= 1;
    609 		}
    610 		*outptr = '\0';
    611 		break;
    612 	    }
    613 	    outptr[outlen - 1] = '\0';
    614 	    while (*outptr) {
    615 		outptr++;
    616 		outlen -= 1;
    617 	    }
    618 	}
    619 	inptr++;
    620     }
    621     if (outlen > 0)
    622 	*outptr = '\0';
    623 }
    624 
    625 /*************************************************************************/
    626 /* FUNCTION  : cwd_beenhere                                              */
    627 /* PURPOSE   : Return 1 if the user has already visited this directory   */
    628 /*             via C_WD.                                                 */
    629 /* ARGUMENTS : a power-of-two directory function code (README, MESSAGE)  */
    630 /*************************************************************************/
    631 
    632 int cwd_beenhere(int dircode)
    633 {
    634     struct dirlist {
    635 	struct dirlist *next;
    636 	int dircode;
    637 	char dirname[1];
    638     };
    639 
    640     static struct dirlist *head = NULL;
    641     struct dirlist *curptr;
    642     char cwd[MAXPATHLEN];
    643 
    644     (void) fb_realpath(".", cwd);
    645 
    646     for (curptr = head; curptr != NULL; curptr = curptr->next)
    647 	if (strcmp(curptr->dirname, cwd) == 0) {
    648 	    if (!(curptr->dircode & dircode)) {
    649 		curptr->dircode |= dircode;
    650 		return (0);
    651 	    }
    652 	    return (1);
    653 	}
    654     curptr = (struct dirlist *) malloc(strlen(cwd) + 1 + sizeof(struct dirlist));
    655 
    656     if (curptr != NULL) {
    657 	curptr->next = head;
    658 	head = curptr;
    659 	curptr->dircode = dircode;
    660 	strcpy(curptr->dirname, cwd);
    661     }
    662     return (0);
    663 }
    664 
    665 /*************************************************************************/
    666 /* FUNCTION  : show_banner                                               */
    667 /* PURPOSE   : Display a banner on the user's terminal before login      */
    668 /* ARGUMENTS : reply code to use                                         */
    669 /*************************************************************************/
    670 
    671 void show_banner(int msgcode)
    672 {
    673     char *crptr, linebuf[1024], outbuf[1024];
    674     struct aclmember *entry = NULL;
    675     FILE *infile;
    676 
    677 #ifdef VIRTUAL
    678     extern int virtual_mode;
    679     extern int virtual_ftpaccess;
    680     extern char virtual_banner[];
    681 
    682     if (virtual_mode && !virtual_ftpaccess) {
    683 	infile = fopen(virtual_banner, "r");
    684 	if (infile) {
    685 	    while (fgets(linebuf, sizeof(linebuf), infile) != NULL) {
    686 		if ((crptr = strchr(linebuf, '\n')) != NULL)
    687 		    *crptr = '\0';
    688 		msg_massage(linebuf, outbuf, sizeof(outbuf));
    689 		lreply(msgcode, "%s", outbuf);
    690 	    }
    691 	    fclose(infile);
    692 #ifndef NO_SUCKING_NEWLINES
    693 	    lreply(msgcode, "");
    694 #endif
    695 	}
    696     }
    697     else {
    698 #endif
    699 	/* banner <path> */
    700 	while (getaclentry("banner", &entry)) {
    701 	    if (!ARG0)
    702 		continue;
    703 	    infile = fopen(ARG0, "r");
    704 	    if (infile) {
    705 		while (fgets(linebuf, sizeof(linebuf), infile) != NULL) {
    706 		    if ((crptr = strchr(linebuf, '\n')) != NULL)
    707 			*crptr = '\0';
    708 		    msg_massage(linebuf, outbuf, sizeof(outbuf));
    709 		    lreply(msgcode, "%s", outbuf);
    710 		}
    711 		fclose(infile);
    712 #ifndef NO_SUCKING_NEWLINES
    713 		lreply(msgcode, "");
    714 #endif
    715 	    }
    716 	}
    717 #ifdef VIRTUAL
    718     }
    719 #endif
    720 }
    721 /*************************************************************************/
    722 /* FUNCTION  : show_message                                              */
    723 /* PURPOSE   : Display a message on the user's terminal if the current   */
    724 /*             conditions are right                                      */
    725 /* ARGUMENTS : reply code to use, LOG_IN|CMD                             */
    726 /*************************************************************************/
    727 
    728 void show_message(int msgcode, int mode)
    729 {
    730     char *crptr, linebuf[1024], outbuf[1024], class[MAXPATHLEN], cwd[MAXPATHLEN];
    731     int show, which;
    732     struct aclmember *entry = NULL;
    733     FILE *infile;
    734 
    735     if (mode == C_WD && cwd_beenhere(1) != 0)
    736 	return;
    737 
    738 #ifdef HAVE_GETCWD
    739     (void) getcwd(cwd, MAXPATHLEN - 1);
    740 #else
    741     (void) getwd(cwd);
    742 #endif
    743     (void) acl_getclass(class);
    744 
    745     /* message <path> [<when> [<class>]] */
    746     while (getaclentry("message", &entry)) {
    747 	if (!ARG0)
    748 	    continue;
    749 	show = 0;
    750 
    751 	if (mode == LOG_IN && (!ARG1 || !strcasecmp(ARG1, "login")))
    752 	    if (!ARG2)
    753 		show++;
    754 	    else {
    755 		for (which = 2; (which < MAXARGS) && ARG[which]; which++)
    756 		    if (strcasecmp(class, ARG[which]) == 0)
    757 			show++;
    758 	    }
    759 	if (mode == C_WD && ARG1 && !strncasecmp(ARG1, "cwd=", 4) &&
    760 	    (!strcmp((ARG1) + 4, cwd) || *(ARG1 + 4) == '*' ||
    761 	     !wu_fnmatch((ARG1) + 4, cwd, FNM_PATHNAME)))
    762 	    if (!ARG2)
    763 		show++;
    764 	    else {
    765 		for (which = 2; (which < MAXARGS) && ARG[which]; which++)
    766 		    if (strcasecmp(class, ARG[which]) == 0)
    767 			show++;
    768 	    }
    769 	if (show && (int) strlen(ARG0) > 0) {
    770 	    infile = fopen(ARG0, "r");
    771 	    if (infile) {
    772 		while (fgets(linebuf, sizeof(linebuf), infile) != NULL) {
    773 		    if ((crptr = strchr(linebuf, '\n')) != NULL)
    774 			*crptr = '\0';
    775 		    msg_massage(linebuf, outbuf, sizeof(outbuf));
    776 		    lreply(msgcode, "%s", outbuf);
    777 		}
    778 		fclose(infile);
    779 #ifndef NO_SUCKING_NEWLINES
    780 		lreply(msgcode, "");
    781 #endif
    782 	    }
    783 	}
    784     }
    785 }
    786 
    787 /*************************************************************************/
    788 /* FUNCTION  : show_readme                                               */
    789 /* PURPOSE   : Display a message about a README file to the user if the  */
    790 /*             current conditions are right                              */
    791 /* ARGUMENTS : pointer to ACL buffer, reply code, LOG_IN|C_WD            */
    792 /*************************************************************************/
    793 
    794 void show_readme(int code, int mode)
    795 {
    796     char **filelist, **sfilelist, class[MAXPATHLEN], cwd[MAXPATHLEN];
    797     int show, which, days;
    798     time_t clock;
    799 
    800     struct stat buf;
    801     struct tm *tp;
    802     struct aclmember *entry = NULL;
    803 
    804     if (cwd_beenhere(2) != 0)
    805 	return;
    806 
    807 #ifdef HAVE_GETCWD
    808     (void) getcwd(cwd, MAXPATHLEN - 1);
    809 #else
    810     (void) getwd(cwd);
    811 #endif
    812     (void) acl_getclass(class);
    813 
    814     /* readme  <path> {<when>} */
    815     while (getaclentry("readme", &entry)) {
    816 	if (!ARG0)
    817 	    continue;
    818 	show = 0;
    819 
    820 	if (mode == LOG_IN && (!ARG1 || !strcasecmp(ARG1, "login")))
    821 	    if (!ARG2)
    822 		show++;
    823 	    else {
    824 		for (which = 2; (which < MAXARGS) && ARG[which]; which++)
    825 		    if (strcasecmp(class, ARG[which]) == 0)
    826 			show++;
    827 	    }
    828 	if (mode == C_WD && ARG1 && !strncasecmp(ARG1, "cwd=", 4)
    829 	    && (!strcmp((ARG1) + 4, cwd) || *(ARG1 + 4) == '*' ||
    830 		!wu_fnmatch((ARG1) + 4, cwd, FNM_PATHNAME)))
    831 	    if (!ARG2)
    832 		show++;
    833 	    else {
    834 		for (which = 2; (which < MAXARGS) && ARG[which]; which++)
    835 		    if (strcasecmp(class, ARG[which]) == 0)
    836 			show++;
    837 	    }
    838 	if (show) {
    839 	    globerr = NULL;
    840 	    filelist = ftpglob(ARG0);
    841 	    sfilelist = filelist;	/* save to free later */
    842 	    if (!globerr) {
    843 		while (filelist && *filelist) {
    844 		    errno = 0;
    845 		    if (!stat(*filelist, &buf) &&
    846 			(buf.st_mode & S_IFMT) == S_IFREG) {
    847 			lreply(code, "Please read the file %s", *filelist);
    848 			(void) time(&clock);
    849 			tp = localtime(&clock);
    850 			days = 365 * tp->tm_year + tp->tm_yday;
    851 			tp = localtime((time_t *) & buf.st_mtime);
    852 			days -= 365 * tp->tm_year + tp->tm_yday;
    853 /*
    854    if (days == 0) {
    855    lreply(code, "  it was last modified on %.24s - Today",
    856    ctime((time_t *)&buf.st_mtime));
    857    } else {
    858  */
    859 			lreply(code,
    860 			   "  it was last modified on %.24s - %d day%s ago",
    861 			       ctime((time_t *) & buf.st_mtime), days, days == 1 ? "" : "s");
    862 /*
    863    }
    864  */
    865 		    }
    866 		    filelist++;
    867 		}
    868 	    }
    869 	    if (sfilelist) {
    870 		blkfree(sfilelist);
    871 		free((char *) sfilelist);
    872 	    }
    873 	}
    874     }
    875 }
    876 
    877 /*************************************************************************/
    878 /* FUNCTION  : deny_badxfertype                                          */
    879 /* PURPOSE   : If user is in ASCII transfer mode and tries to retrieve a */
    880 /*             binary file, abort transfer and display appropriate error */
    881 /* ARGUMENTS : message code to use for denial, path of file to check for */
    882 /*             binary contents or NULL to assume binary file             */
    883 /*************************************************************************/
    884 
    885 int deny_badasciixfer(int msgcode, char *filepath)
    886 {
    887 
    888     if (type == TYPE_A && !*filepath) {
    889 	reply(msgcode, "This is a BINARY file, using ASCII mode to transfer will corrupt it.");
    890 	return (1);
    891     }
    892     /* The hooks are here to prevent transfers of actual binary files, not
    893      * just TAR or COMPRESS mode files... */
    894     return (0);
    895 }
    896 
    897 /*************************************************************************/
    898 /* FUNCTION  : is_shutdown                                               */
    899 /* PURPOSE   : Check to see if the server is shutting down, if it is     */
    900 /*             arrange for the shutdown message to be sent in the next   */
    901 /*             reply to the user                                         */
    902 /* ARGUMENTS : whether to arrange for a shutdown message to be sent, new */
    903 /*             or existing connection                                    */
    904 /* RETURNS   : 1 if shutting down, 0 if not                              */
    905 /*************************************************************************/
    906 
    907 int is_shutdown(int quiet, int new)
    908 {
    909     static struct tm tmbuf;
    910     static struct stat s_last;
    911     static time_t last = 0, shut, deny, disc;
    912     static int valid;
    913     static char text[2048];
    914     struct stat s_cur;
    915 
    916     extern char *autospout, Shutdown[];
    917 
    918     FILE *fp;
    919 
    920     int deny_off, disc_off;
    921 
    922     time_t curtime = time(NULL);
    923 
    924     char buf[1024], linebuf[1024];
    925 
    926     if (Shutdown[0] == '\0' || stat(Shutdown, &s_cur))
    927 	return (0);
    928 
    929     if (s_last.st_mtime != s_cur.st_mtime) {
    930 	valid = 0;
    931 
    932 	fp = fopen(Shutdown, "r");
    933 	if (fp == NULL)
    934 	    return (0);
    935 	s_last = s_cur;
    936 	fgets(buf, sizeof(buf), fp);
    937 	if (sscanf(buf, "%d %d %d %d %d %ld %ld", &tmbuf.tm_year, &tmbuf.tm_mon,
    938 	&tmbuf.tm_mday, &tmbuf.tm_hour, &tmbuf.tm_min, &deny, &disc) != 7) {
    939 	    (void) fclose(fp);
    940 	    return (0);
    941 	}
    942 	valid = 1;
    943 	deny_off = 3600 * (deny / 100) + 60 * (deny % 100);
    944 	disc_off = 3600 * (disc / 100) + 60 * (disc % 100);
    945 
    946 	tmbuf.tm_year -= 1900;
    947 	tmbuf.tm_isdst = -1;
    948 	shut = mktime(&tmbuf);
    949 	strcpy(shuttime, ctime(&shut));
    950 
    951 	disc = shut - disc_off;
    952 	strcpy(disctime, ctime(&disc));
    953 
    954 	deny = shut - deny_off;
    955 	strcpy(denytime, ctime(&deny));
    956 
    957 	text[0] = '\0';
    958 
    959 	while (fgets(buf, sizeof(buf), fp) != NULL) {
    960 	    msg_massage(buf, linebuf, sizeof(linebuf));
    961 	    if ((strlen(text) + strlen(linebuf)) < sizeof(text))
    962 		strcat(text, linebuf);
    963 	}
    964 
    965 	(void) fclose(fp);
    966     }
    967     if (!valid)
    968 	return (0);
    969 
    970     /* if last == 0, then is_shutdown() only called with quiet == 1 so far */
    971     if (last == 0 && !quiet) {
    972 	autospout = text;	/* warn them for the first time */
    973 	autospout_free = 0;
    974 	last = curtime;
    975     }
    976     /* if a new connection and past deny time, tell caller to drop 'em */
    977     if (new && curtime > deny)
    978 	return (1);
    979 
    980     /* if past disconnect time, tell caller to drop 'em */
    981     if (curtime > disc)
    982 	return (1);
    983 
    984     /* if less than 60 seconds to disconnection, warn 'em continuously */
    985     if (curtime > (disc - 60) && !quiet) {
    986 	autospout = text;
    987 	autospout_free = 0;
    988 	last = curtime;
    989     }
    990     /* if less than 15 minutes to disconnection, warn 'em every 5 mins */
    991     if (curtime > (disc - 60 * 15)) {
    992 	if ((curtime - last) > (60 * 5) && !quiet) {
    993 	    autospout = text;
    994 	    autospout_free = 0;
    995 	    last = curtime;
    996 	}
    997     }
    998     /* if less than 24 hours to disconnection, warn 'em every 30 mins */
    999     if (curtime < (disc - 24 * 60 * 60) && !quiet) {
   1000 	if ((curtime - last) > (60 * 30)) {
   1001 	    autospout = text;
   1002 	    autospout_free = 0;
   1003 	    last = curtime;
   1004 	}
   1005     }
   1006     /* if more than 24 hours to disconnection, warn 'em every 60 mins */
   1007     if (curtime > (disc - 24 * 60 * 60) && !quiet) {
   1008 	if ((curtime - last) >= (24 * 60 * 60)) {
   1009 	    autospout = text;
   1010 	    autospout_free = 0;
   1011 	    last = curtime;
   1012 	}
   1013     }
   1014     return (0);
   1015 }
   1016 
   1017 #ifdef SITE_NEWER
   1018 void newer(char *date, char *path, int showlots)
   1019 {
   1020     struct tm tm;
   1021 
   1022     if (sscanf(date, "%04d%02d%02d%02d%02d%02d",
   1023 	       &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
   1024 	       &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
   1025 
   1026 	tm.tm_year -= 1900;
   1027 	tm.tm_mon--;
   1028 	tm.tm_isdst = -1;
   1029 	newer_time = mktime(&tm);
   1030 	dout = dataconn("file list", (off_t) - 1, "w");
   1031 
   1032 	if (dout != NULL) {
   1033 	    /* As ftw allocates storage it needs a chance to cleanup, setting
   1034 	     * ftwflag prevents myoob from calling longjmp, incrementing
   1035 	     * ftwflag instead which causes check_newer to return non-zero
   1036 	     * which makes ftw return. */
   1037 	    ftwflag = 1;
   1038 	    transflag++;
   1039 	    show_fullinfo = showlots;
   1040 #if defined(HAVE_FTW)
   1041 	    ftw(path, check_newer, -1);
   1042 #else
   1043 	    treewalk(path, check_newer, -1, NULL);
   1044 #endif
   1045 
   1046 	    /* don't send a reply if myoob has already replied */
   1047 	    if (ftwflag == 1) {
   1048 		if (ferror(dout) != 0)
   1049 		    perror_reply(550, "Data connection");
   1050 		else
   1051 		    reply(226, "Transfer complete.");
   1052 	    }
   1053 
   1054 	    (void) fclose(dout);
   1055 	    data = -1;
   1056 	    pdata = -1;
   1057 	    transflag = 0;
   1058 	    ftwflag = 0;
   1059 	}
   1060     }
   1061     else
   1062 	reply(501, "Bad DATE format");
   1063 }
   1064 #endif
   1065 
   1066 int type_match(char *typelist)
   1067 {
   1068     char *start, *p;
   1069     int len;
   1070 
   1071     if (typelist == NULL)
   1072 	return (0);
   1073 
   1074     for (p = start = typelist; *start != '\0'; start = p) {
   1075 	while (*p != '\0' && *p != ',')
   1076 	    p++;
   1077 	len = p - start;
   1078 	if (*p != '\0')
   1079 	    p++;
   1080 	if (len == 9 && anonymous && strncasecmp(start, "anonymous", 9) == 0)
   1081 	    return (1);
   1082 	if (len == 5 && guest && strncasecmp(start, "guest", 5) == 0)
   1083 	    return (1);
   1084 	if (len == 4 && !guest && !anonymous &&
   1085 	    strncasecmp(start, "real", 4) == 0)
   1086 	    return (1);
   1087 
   1088 	if (len > 6 && strncasecmp(start, "class=", 6) == 0) {
   1089 	    char class[1024];
   1090 
   1091 	    if ((acl_getclass(class) == 1) && (strlen(class) == len - 6) &&
   1092 		(strncasecmp(start + 6, class, len - 6) == 0))
   1093 		return (1);
   1094 	}
   1095     }
   1096     return (0);
   1097 }
   1098 
   1099 int path_compare(char *p1, char *p2)
   1100 {
   1101     if ((strcmp(p1, "*") == 0) || (wu_fnmatch(p1, p2, FNM_PATHNAME) == 0))	/* 0 means they matched */
   1102 	return (strlen(p1));
   1103     else
   1104 	return (-2);
   1105 }
   1106 
   1107 void expand_id(void)
   1108 {
   1109     char class[1024];
   1110     struct aclmember *entry = NULL;
   1111     (void) acl_getclass(class);
   1112     while (getaclentry("upload", &entry)) {
   1113 	char *q;
   1114 	int i = 0;
   1115 	int options = 1;
   1116 	int classfound = 0;
   1117 	int classmatched = 0;
   1118 	while (options
   1119 	       && (i < MAXARGS)
   1120 	       && ((q = entry->arg[i]) != (char *) NULL)
   1121 	       && (q[0] != '\0')) {
   1122 	    if (strcasecmp(q, "absolute") == 0)
   1123 		i++;
   1124 	    else if (strcasecmp(q, "relative") == 0)
   1125 		i++;
   1126 	    else if (strncasecmp(q, "class=", 6) == 0) {
   1127 		i++;
   1128 		classfound = 1;
   1129 		if (strcasecmp(q + 6, class) == 0)
   1130 		    classmatched = 1;
   1131 	    }
   1132 	    else if (strcmp(q, "-") == 0) {
   1133 		i++;
   1134 		options = 0;
   1135 	    }
   1136 	    else
   1137 		options = 0;
   1138 	}
   1139 	if (!classfound || classmatched) {
   1140 	    char buf[BUFSIZ];
   1141 	    /*
   1142 	       *  UID
   1143 	     */
   1144 	    if (((i + 3) < MAXARGS)
   1145 		&& ((q = entry->arg[i + 3]) != (char *) NULL)
   1146 		&& (q[0] != '\0')
   1147 		&& (strcmp(q, "*") != 0)) {
   1148 		if (q[0] == '%')
   1149 		    sprintf(buf, "%s", q + 1);
   1150 		else {
   1151 		    struct passwd *pwent = getpwnam(q);
   1152 		    if (pwent)
   1153 			sprintf(buf, "%" PW_UID_FORMAT, pwent->pw_uid);
   1154 		    else
   1155 			sprintf(buf, "%d", 0);
   1156 		}
   1157 		entry->arg[i + 3] = (char *) malloc(strlen(buf) + 1);
   1158 		if (entry->arg[i + 3] == NULL) {
   1159 		    syslog(LOG_ERR, "calloc error in expand_id");
   1160 		    dologout(1);
   1161 		}
   1162 		strcpy(entry->arg[i + 3], buf);
   1163 	    }
   1164 	    /*
   1165 	       *  GID
   1166 	     */
   1167 	    if (((i + 4) < MAXARGS)
   1168 		&& ((q = entry->arg[i + 4]) != (char *) NULL)
   1169 		&& (q[0] != '\0')
   1170 		&& (strcmp(q, "*") != 0)) {
   1171 		if (q[0] == '%')
   1172 		    sprintf(buf, "%s", q + 1);
   1173 		else {
   1174 		    struct group *grent = getgrnam(q);
   1175 		    if (grent)
   1176 			sprintf(buf, "%" GR_GID_FORMAT, grent->gr_gid);
   1177 		    else
   1178 			sprintf(buf, "%d", 0);
   1179 		    endgrent();
   1180 		}
   1181 		entry->arg[i + 4] = (char *) malloc(strlen(buf) + 1);
   1182 		if (entry->arg[i + 4] == NULL) {
   1183 		    syslog(LOG_ERR, "calloc error in expand_id");
   1184 		    dologout(1);
   1185 		}
   1186 		strcpy(entry->arg[i + 4], buf);
   1187 	    }
   1188 	}
   1189     }
   1190 }
   1191 
   1192 int fn_check(char *name)
   1193 {
   1194     /* check to see if this is a valid file name... path-filter <type>
   1195      * <message_file> <allowed_charset> <disallowed> */
   1196 
   1197     struct aclmember *entry = NULL;
   1198     int j;
   1199     char *path;
   1200 #if ! defined(HAVE_REGEXEC)
   1201     char *sp;
   1202 #endif
   1203 
   1204 #ifdef M_UNIX
   1205 #ifdef HAVE_REGEX
   1206     char *regp;
   1207 #endif
   1208 #endif
   1209 
   1210 #ifdef HAVE_REGEXEC
   1211     regex_t regexbuf;
   1212     regmatch_t regmatchbuf;
   1213     int rval;
   1214 #endif
   1215 
   1216 #ifdef LINUX
   1217     re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
   1218 #endif
   1219 
   1220     while (getaclentry("path-filter", &entry) && ARG0 != NULL) {
   1221 	if (type_match(ARG0) && ARG1 && ARG2) {
   1222 
   1223 	    /*
   1224 	     * check *only* the basename
   1225 	     */
   1226 
   1227 	    if ((path = strrchr(name, '/')))
   1228 		++path;
   1229 	    else
   1230 		path = name;
   1231 
   1232 	    /* is it in the allowed character set? */
   1233 #if defined(HAVE_REGEXEC)
   1234 	    if (regcomp(&regexbuf, ARG2, REG_EXTENDED) != 0) {
   1235 		reply(550, "HAVE_REGEX error");
   1236 #elif defined(HAVE_REGEX)
   1237 		if ((sp = regcmp(ARG2, (char *) 0)) == NULL) {
   1238 		    reply(550, "HAVE_REGEX error");
   1239 #else
   1240 	    if ((sp = re_comp(ARG2)) != 0) {
   1241 		perror_reply(550, sp);
   1242 #endif
   1243 		return (0);
   1244 	    }
   1245 #if defined(HAVE_REGEXEC)
   1246 	    rval = regexec(&regexbuf, path, 1, &regmatchbuf, 0);
   1247 	    regfree(&regexbuf);
   1248 	    if (rval != 0) {
   1249 #elif defined(HAVE_REGEX)
   1250 #ifdef M_UNIX
   1251 		regp = regex(sp, path);
   1252 		free(sp);
   1253 		if (regp == NULL) {
   1254 #else
   1255 		if ((regex(sp, path)) == NULL) {
   1256 #endif
   1257 #else
   1258 	    if ((re_exec(path)) != 1) {
   1259 #endif
   1260 		pr_mesg(550, ARG1);
   1261 		reply(550, "%s: Permission denied on server. (Filename (accept))", name);
   1262 		return (0);
   1263 	    }
   1264 	    /* is it in any of the disallowed regexps */
   1265 
   1266 	    for (j = 3; j < MAXARGS; ++j) {
   1267 		/* ARGj == entry->arg[j] */
   1268 		if (entry->arg[j]) {
   1269 #if defined(HAVE_REGEXEC)
   1270 		    if (regcomp(&regexbuf, entry->arg[j], REG_EXTENDED) != 0) {
   1271 			reply(550, "HAVE_REGEX error");
   1272 #elif defined(HAVE_REGEX)
   1273 			if ((sp = regcmp(entry->arg[j], (char *) 0)) == NULL) {
   1274 			    reply(550, "HAVE_REGEX error");
   1275 #else
   1276 		    if ((sp = re_comp(entry->arg[j])) != 0) {
   1277 			perror_reply(550, sp);
   1278 #endif
   1279 			return (0);
   1280 		    }
   1281 #if defined(HAVE_REGEXEC)
   1282 		    rval = regexec(&regexbuf, path, 1, &regmatchbuf, 0);
   1283 		    regfree(&regexbuf);
   1284 		    if (rval == 0) {
   1285 #elif defined(HAVE_REGEX)
   1286 #ifdef M_UNIX
   1287 			regp = regex(sp, path);
   1288 			free(sp);
   1289 			if (regp != NULL) {
   1290 #else
   1291 			if ((regex(sp, path)) != NULL) {
   1292 #endif
   1293 #else
   1294 		    if ((re_exec(path)) == 1) {
   1295 #endif
   1296 			pr_mesg(550, ARG1);
   1297 			reply(550, "%s: Permission denied on server. (Filename (deny))", name);
   1298 			return (0);
   1299 		    }
   1300 		}
   1301 	    }
   1302 	}
   1303     }
   1304     return (1);
   1305 }
   1306 
   1307 int dir_check(char *name, uid_t * uid, gid_t * gid, int *d_mode, int *valid)
   1308 {
   1309     struct aclmember *entry = NULL;
   1310     int match_value = -1;
   1311     char *ap2 = NULL;
   1312     char *ap3 = NULL;
   1313     char *ap4 = NULL;
   1314     char *ap5 = NULL;
   1315     char *ap6 = NULL;
   1316     char *ap7 = NULL;
   1317     char cwdir[MAXPATHLEN];
   1318     char *pwdir;
   1319     char abspwdir[MAXPATHLEN];
   1320     char relpwdir[MAXPATHLEN];
   1321     char path[MAXPATHLEN];
   1322     char *sp;
   1323     struct stat stbuf;
   1324     int stat_result = -1;
   1325     char class[1024];
   1326     extern char *home;
   1327 
   1328     (void) acl_getclass(class);
   1329 
   1330     *valid = 0;
   1331     /* what's our current directory? */
   1332 
   1333     /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
   1334        out with an error. The rest of wu is so crufy that a long path might
   1335        just blow up later */
   1336 
   1337     if ((strlen(name) + 1) > sizeof(path)) {
   1338 	perror_reply(550, "Path too long");
   1339 	return (-1);
   1340     }
   1341 
   1342     strcpy(path, name);
   1343     sp = strrchr(path, '/');
   1344     if (sp)
   1345 	*sp = '\0';
   1346     else
   1347 	strcpy(path, ".");
   1348 
   1349     if ((fb_realpath(path, cwdir)) == NULL) {
   1350 	perror_reply(550, "Could not determine cwdir");
   1351 	return (-1);
   1352     }
   1353 
   1354     if ((fb_realpath(home, relpwdir)) == NULL) {
   1355 	perror_reply(550, "Could not determine pwdir");
   1356 	return (-1);
   1357     }
   1358 
   1359     if ((wu_realpath(home, abspwdir, chroot_path)) == NULL) {
   1360 	perror_reply(550, "Could not determine pwdir");
   1361 	return (-1);
   1362     }
   1363 
   1364     while (getaclentry("upload", &entry)) {
   1365 	char *q;
   1366 	int i = 0;
   1367 	int options = 1;
   1368 	int classfound = 0;
   1369 	int classmatched = 0;
   1370 	pwdir = abspwdir;
   1371 	while (options
   1372 	       && (i < MAXARGS)
   1373 	       && ((q = entry->arg[i]) != (char *) NULL)
   1374 	       && (q[0] != '\0')) {
   1375 	    if (strcasecmp(q, "absolute") == 0) {
   1376 		i++;
   1377 		pwdir = abspwdir;
   1378 	    }
   1379 	    else if (strcasecmp(q, "relative") == 0) {
   1380 		i++;
   1381 		pwdir = relpwdir;
   1382 	    }
   1383 	    else if (strncasecmp(q, "class=", 6) == 0) {
   1384 		i++;
   1385 		classfound = 1;
   1386 		if (strcasecmp(q + 6, class) == 0)
   1387 		    classmatched = 1;
   1388 	    }
   1389 	    else if (strcmp(q, "-") == 0) {
   1390 		i++;
   1391 		options = 0;
   1392 	    }
   1393 	    else
   1394 		options = 0;
   1395 	}
   1396 	if (!classfound || classmatched) {
   1397 	    int j;
   1398 	    if (((i + 1) < MAXARGS)
   1399 		&& ((q = entry->arg[i]) != (char *) NULL)
   1400 		&& (q[0] != '\0')
   1401 		&& (0 < path_compare(q, pwdir))
   1402 		&& ((j = path_compare(entry->arg[i + 1], cwdir)) >= match_value)) {
   1403 		match_value = j;
   1404 
   1405 		ap2 = NULL;
   1406 		if (((i + 2) < MAXARGS)
   1407 		    && ((q = entry->arg[i + 2]) != (char *) NULL)
   1408 		    && (q[0] != '\0'))
   1409 		    ap2 = q;
   1410 
   1411 		ap3 = NULL;
   1412 		if (((i + 3) < MAXARGS)
   1413 		    && ((q = entry->arg[i + 3]) != (char *) NULL)
   1414 		    && (q[0] != '\0'))
   1415 		    ap3 = q;
   1416 
   1417 		ap4 = NULL;
   1418 		if (((i + 4) < MAXARGS)
   1419 		    && ((q = entry->arg[i + 4]) != (char *) NULL)
   1420 		    && (q[0] != '\0'))
   1421 		    ap4 = q;
   1422 
   1423 		ap5 = NULL;
   1424 		if (((i + 5) < MAXARGS)
   1425 		    && ((q = entry->arg[i + 5]) != (char *) NULL)
   1426 		    && (q[0] != '\0'))
   1427 		    ap5 = q;
   1428 
   1429 		ap6 = NULL;
   1430 		if (((i + 6) < MAXARGS)
   1431 		    && ((q = entry->arg[i + 6]) != (char *) NULL)
   1432 		    && (q[0] != '\0'))
   1433 		    ap6 = q;
   1434 
   1435 		ap7 = NULL;
   1436 		if (((i + 7) < MAXARGS)
   1437 		    && ((q = entry->arg[i + 7]) != (char *) NULL)
   1438 		    && (q[0] != '\0'))
   1439 		    ap7 = q;
   1440 	    }
   1441 	}
   1442     }
   1443 
   1444     if (anonymous && (match_value < 0)) {
   1445 	reply(550, "%s: Permission denied on server. (Upload dirs)", name);
   1446 	return (0);
   1447     }
   1448     if ((ap2 && !strcasecmp(ap2, "no"))
   1449 	|| (ap3 && !strcasecmp(ap3, "nodirs"))
   1450 	|| (ap6 && !strcasecmp(ap6, "nodirs"))) {
   1451 	reply(550, "%s: Permission denied on server. (Upload dirs)", name);
   1452 	return (0);
   1453     }
   1454     if ((ap3 && *ap3 == '*') || (ap4 && *ap4 == '*'))
   1455 	stat_result = stat(path, &stbuf);
   1456     if (ap3) {
   1457 	if ((ap3[0] != '*') || (ap3[1] != '\0'))
   1458 	    *uid = atoi(ap3);	/* the uid  */
   1459 	else if (stat_result == 0)
   1460 	    *uid = stbuf.st_uid;
   1461     }
   1462     if (ap4) {
   1463 	if ((ap4[0] != '*') || (ap4[1] != '\0'))
   1464 	    *gid = atoi(ap4);	/* the gid */
   1465 	else if (stat_result == 0)
   1466 	    *gid = stbuf.st_gid;
   1467     }
   1468     if (ap7) {
   1469 	sscanf(ap7, "%o", d_mode);
   1470 	*valid = 1;
   1471     }
   1472     else if (ap5) {
   1473 	sscanf(ap5, "%o", d_mode);
   1474 	if (*d_mode & 0600)
   1475 	    *d_mode |= 0100;
   1476 	if (*d_mode & 0060)
   1477 	    *d_mode |= 0010;
   1478 	if (*d_mode & 0006)
   1479 	    *d_mode |= 0001;
   1480 	*valid = 1;
   1481     }
   1482     return (1);
   1483 }
   1484 
   1485 int upl_check(char *name, uid_t * uid, gid_t * gid, int *f_mode, int *valid)
   1486 {
   1487     int match_value = -1;
   1488     char cwdir[MAXPATHLEN];
   1489     char *pwdir;
   1490     char abspwdir[MAXPATHLEN];
   1491     char relpwdir[MAXPATHLEN];
   1492     char path[MAXPATHLEN];
   1493     char *sp;
   1494     struct stat stbuf;
   1495     int stat_result = -1;
   1496     char *ap2 = NULL;
   1497     char *ap3 = NULL;
   1498     char *ap4 = NULL;
   1499     char *ap5 = NULL;
   1500     struct aclmember *entry = NULL;
   1501     char class[1024];
   1502     extern char *home;
   1503 
   1504     *valid = 0;
   1505     (void) acl_getclass(class);
   1506 
   1507     /* what's our current directory? */
   1508 
   1509     /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
   1510        out with an error. The rest of wu is so crufy that a long path might
   1511        just blow up later */
   1512 
   1513     if ((strlen(name) + 1) > sizeof(path)) {
   1514 	perror_reply(553, "Path too long");
   1515 	return (-1);
   1516     }
   1517 
   1518     strcpy(path, name);
   1519     sp = strrchr(path, '/');
   1520     if (sp)
   1521 	*sp = '\0';
   1522     else
   1523 	strcpy(path, ".");
   1524 
   1525     if ((fb_realpath(path, cwdir)) == NULL) {
   1526 	perror_reply(553, "Could not determine cwdir");
   1527 	return (-1);
   1528     }
   1529 
   1530     if ((wu_realpath(home, abspwdir, chroot_path)) == NULL) {
   1531 	perror_reply(553, "Could not determine pwdir");
   1532 	return (-1);
   1533     }
   1534 
   1535     if ((fb_realpath(home, relpwdir)) == NULL) {
   1536 	perror_reply(553, "Could not determine pwdir");
   1537 	return (-1);
   1538     }
   1539 
   1540     /*
   1541        *  we are doing a "best match"... ..so we keep track of what "match
   1542        *  value" we have received so far...
   1543      */
   1544     while (getaclentry("upload", &entry)) {
   1545 	char *q;
   1546 	int i = 0;
   1547 	int options = 1;
   1548 	int classfound = 0;
   1549 	int classmatched = 0;
   1550 	pwdir = abspwdir;
   1551 	while (options
   1552 	       && (i < MAXARGS)
   1553 	       && ((q = entry->arg[i]) != (char *) NULL)
   1554 	       && (q[0] != '\0')) {
   1555 	    if (strcasecmp(q, "absolute") == 0) {
   1556 		i++;
   1557 		pwdir = abspwdir;
   1558 	    }
   1559 	    else if (strcasecmp(q, "relative") == 0) {
   1560 		i++;
   1561 		pwdir = relpwdir;
   1562 	    }
   1563 	    else if (strncasecmp(q, "class=", 6) == 0) {
   1564 		i++;
   1565 		classfound = 1;
   1566 		if (strcasecmp(q + 6, class) == 0)
   1567 		    classmatched = 1;
   1568 	    }
   1569 	    else if (strcmp(q, "-") == 0) {
   1570 		i++;
   1571 		options = 0;
   1572 	    }
   1573 	    else
   1574 		options = 0;
   1575 	}
   1576 	if (!classfound || classmatched) {
   1577 	    int j;
   1578 	    if (((i + 1) < MAXARGS)
   1579 		&& ((q = entry->arg[i]) != (char *) NULL)
   1580 		&& (q[0] != '\0')
   1581 		&& (0 < path_compare(q, pwdir))
   1582 		&& ((j = path_compare(entry->arg[i + 1], cwdir)) >= match_value)) {
   1583 		match_value = j;
   1584 
   1585 		ap2 = NULL;
   1586 		if (((i + 2) < MAXARGS)
   1587 		    && ((q = entry->arg[i + 2]) != (char *) NULL)
   1588 		    && (q[0] != '\0'))
   1589 		    ap2 = q;
   1590 
   1591 		ap3 = NULL;
   1592 		if (((i + 3) < MAXARGS)
   1593 		    && ((q = entry->arg[i + 3]) != (char *) NULL)
   1594 		    && (q[0] != '\0'))
   1595 		    ap3 = q;
   1596 
   1597 		ap4 = NULL;
   1598 		if (((i + 4) < MAXARGS)
   1599 		    && ((q = entry->arg[i + 4]) != (char *) NULL)
   1600 		    && (q[0] != '\0'))
   1601 		    ap4 = q;
   1602 
   1603 		ap5 = NULL;
   1604 		if (((i + 5) < MAXARGS)
   1605 		    && ((q = entry->arg[i + 5]) != (char *) NULL)
   1606 		    && (q[0] != '\0'))
   1607 		    ap5 = q;
   1608 	    }
   1609 	}
   1610     }
   1611 
   1612     if (ap3
   1613 	&& ((!strcasecmp("dirs", ap3))
   1614 	    || (!strcasecmp("nodirs", ap3))))
   1615 	ap3 = NULL;
   1616 
   1617     /*
   1618        *  if we did get matches ... else don't do any of this stuff
   1619      */
   1620     if (match_value >= 0) {
   1621 	if (!strcasecmp(ap2, "yes")) {
   1622 	    if ((ap3 && *ap3 == '*') || (ap4 && *ap4 == '*'))
   1623 		stat_result = stat(path, &stbuf);
   1624 	    if (ap3) {
   1625 		if ((ap3[0] != '*') || (ap3[1] != '\0'))
   1626 		    *uid = atoi(ap3);	/* the uid  */
   1627 		else if (stat_result == 0)
   1628 		    *uid = stbuf.st_uid;
   1629 	    }
   1630 	    if (ap4) {
   1631 		if ((ap4[0] != '*') || (ap4[1] != '\0'))
   1632 		    *gid = atoi(ap4);	/* the gid  */
   1633 		else if (stat_result == 0)
   1634 		    *gid = stbuf.st_gid;
   1635 		*valid = 1;
   1636 	    }
   1637 	    if (ap5)
   1638 		sscanf(ap5, "%o", f_mode);	/* the mode */
   1639 	}
   1640 	else {
   1641 	    reply(553, "%s: Permission denied on server. (Upload)", name);
   1642 	    return (-1);
   1643 	}
   1644     }
   1645     else {
   1646 	/*
   1647 	   *  upload defaults to "permitted"
   1648 	 */
   1649 	/* Not if anonymous */
   1650 	if (anonymous) {
   1651 	    reply(553, "%s: Permission denied on server. (Upload)", name);
   1652 	    return (-1);
   1653 	}
   1654 	return (1);
   1655     }
   1656 
   1657     return (match_value);
   1658 }
   1659 
   1660 int del_check(char *name)
   1661 {
   1662     int pdelete = (anonymous ? 0 : 1);
   1663     struct aclmember *entry = NULL;
   1664 
   1665     while (getaclentry("delete", &entry) && ARG0 && ARG1 != NULL) {
   1666 	if (type_match(ARG1))
   1667 	    if (anonymous) {
   1668 		if (*ARG0 == 'y')
   1669 		    pdelete = 1;
   1670 	    }
   1671 	    else if (*ARG0 == 'n')
   1672 		pdelete = 0;
   1673     }
   1674 
   1675 /* H* fix: no deletion, period. You put a file here, I get to look at it. */
   1676 #ifdef PARANOID
   1677     pdelete = 0;
   1678 #endif
   1679 
   1680     if (!pdelete) {
   1681 	reply(553, "%s: Permission denied on server. (Delete)", name);
   1682 	return (0);
   1683     }
   1684     else {
   1685 	return (1);
   1686     }
   1687 }
   1688 
   1689 /* The following is from the Debian add-ons. */
   1690 
   1691 #define lbasename(x) (strrchr(x,'/')?1+strrchr(x,'/'):x)
   1692 
   1693 int regexmatch(char *name, char *rgexp)
   1694 {
   1695 
   1696 #ifdef M_UNIX
   1697 #ifdef HAVE_REGEX
   1698     char *regp;
   1699 #endif
   1700 #endif
   1701 
   1702 #ifdef HAVE_REGEXEC
   1703     regex_t regexbuf;
   1704     regmatch_t regmatchbuf;
   1705     int rval;
   1706 #else
   1707     char *sp;
   1708 #endif
   1709 
   1710 #if defined(HAVE_REGEXEC)
   1711     if (regcomp(&regexbuf, rgexp, REG_EXTENDED) != 0) {
   1712 	reply(553, "HAVE_REGEX error");
   1713 #elif defined(HAVE_REGEX)
   1714 	if ((sp = regcmp(rgexp, (char *) 0)) == NULL) {
   1715 	    reply(553, "HAVE_REGEX error");
   1716 #else
   1717     if ((sp = re_comp(rgexp)) != 0) {
   1718 	perror_reply(553, sp);
   1719 #endif
   1720 	return (0);
   1721     }
   1722 
   1723 #if defined(HAVE_REGEXEC)
   1724     rval = regexec(&regexbuf, name, 1, &regmatchbuf, 0);
   1725     regfree(&regexbuf);
   1726     if (rval != 0) {
   1727 #elif defined(HAVE_REGEX)
   1728 #ifdef M_UNIX
   1729 	regp = regex(sp, name);
   1730 	free(sp);
   1731 	if (regp == NULL) {
   1732 #else
   1733 	if ((regex(sp, name)) == NULL) {
   1734 #endif
   1735 #else
   1736     if ((re_exec(name)) != 1) {
   1737 #endif
   1738 	return (0);
   1739     }
   1740     return (1);
   1741 }
   1742 
   1743 static int allow_retrieve(char *name)
   1744 {
   1745     char realname[MAXPATHLEN + 1];
   1746     char localname[MAXPATHLEN + 1];
   1747     char *whichname;
   1748     int i;
   1749     struct aclmember *entry = NULL;
   1750     char *p, *q;
   1751     int options;
   1752     int classfound;
   1753     int classmatched;
   1754     char class[1024];
   1755 
   1756     (void) acl_getclass(class);
   1757     if ((name == (char *) NULL)
   1758 	|| (*name == '\0'))
   1759 	return 0;
   1760     fb_realpath(name, localname);
   1761     wu_realpath(name, realname, chroot_path);
   1762     while (getaclentry("allow-retrieve", &entry)) {
   1763 	whichname = realname;
   1764 	i = 0;
   1765 	options = 1;
   1766 	classfound = 0;
   1767 	classmatched = 0;
   1768 	while (options
   1769 	       && (i < MAXARGS)
   1770 	       && ((q = entry->arg[i]) != (char *) NULL)
   1771 	       && (q[0] != '\0')) {
   1772 	    if (strcasecmp(q, "absolute") == 0) {
   1773 		i++;
   1774 		whichname = realname;
   1775 	    }
   1776 	    else if (strcasecmp(q, "relative") == 0) {
   1777 		i++;
   1778 		whichname = localname;
   1779 	    }
   1780 	    else if (strncasecmp(q, "class=", 6) == 0) {
   1781 		i++;
   1782 		classfound = 1;
   1783 		if (strcasecmp(q + 6, class) == 0)
   1784 		    classmatched = 1;
   1785 	    }
   1786 	    else if (strcmp(q, "-") == 0) {
   1787 		i++;
   1788 		options = 0;
   1789 	    }
   1790 	    else
   1791 		options = 0;
   1792 	}
   1793 	if (!classfound || classmatched) {
   1794 	    for (; (i < MAXARGS) && ((q = entry->arg[i]) != (char *) NULL) && (q[0] != '\0'); i++) {
   1795 		p = (q[0] == '/') ? whichname : lbasename(whichname);
   1796 		if (!wu_fnmatch(q, p, FNM_PATHNAME | FNM_LEADING_DIR)) {
   1797 		    return 1;
   1798 		}
   1799 	    }
   1800 	}
   1801     }
   1802     return 0;
   1803 }
   1804 
   1805 int checknoretrieve(char *name)
   1806 {
   1807     char realname[MAXPATHLEN + 1];
   1808     char localname[MAXPATHLEN + 1];
   1809     char *whichname;
   1810     int i;
   1811     struct aclmember *entry = NULL;
   1812     char *p, *q;
   1813     int options;
   1814     int classfound;
   1815     int classmatched;
   1816     char class[1024];
   1817 
   1818     extern struct passwd *pw;
   1819     extern char *remoteident;
   1820 
   1821     (void) acl_getclass(class);
   1822     if ((name == (char *) NULL)
   1823 	|| (*name == '\0'))
   1824 	return 0;
   1825     fb_realpath(name, localname);
   1826     wu_realpath(name, realname, chroot_path);
   1827     while (getaclentry("noretrieve", &entry)) {
   1828 	whichname = realname;
   1829 	i = 0;
   1830 	options = 1;
   1831 	classfound = 0;
   1832 	classmatched = 0;
   1833 	while (options
   1834 	       && (i < MAXARGS)
   1835 	       && ((q = entry->arg[i]) != (char *) NULL)
   1836 	       && (q[0] != '\0')) {
   1837 	    if (strcasecmp(q, "absolute") == 0) {
   1838 		i++;
   1839 		whichname = realname;
   1840 	    }
   1841 	    else if (strcasecmp(q, "relative") == 0) {
   1842 		i++;
   1843 		whichname = localname;
   1844 	    }
   1845 	    else if (strncasecmp(q, "class=", 6) == 0) {
   1846 		i++;
   1847 		classfound = 1;
   1848 		if (strcasecmp(q + 6, class) == 0)
   1849 		    classmatched = 1;
   1850 	    }
   1851 	    else if (strcmp(q, "-") == 0) {
   1852 		i++;
   1853 		options = 0;
   1854 	    }
   1855 	    else
   1856 		options = 0;
   1857 	}
   1858 	if (!classfound || classmatched) {
   1859 	    for (; (i < MAXARGS) && ((q = entry->arg[i]) != (char *) NULL) && (q[0] != '\0'); i++) {
   1860 		p = (q[0] == '/') ? whichname : lbasename(whichname);
   1861 		if (!wu_fnmatch(q, p, FNM_PATHNAME | FNM_LEADING_DIR)) {
   1862 		    if (!allow_retrieve(name)) {
   1863 			reply(550, "%s is marked unretrievable", localname);
   1864 			return 1;
   1865 		    }
   1866 		}
   1867 	    }
   1868 	}
   1869     }
   1870     return 0;
   1871 }
   1872 
   1873 #ifdef QUOTA
   1874 
   1875 #ifndef MNTMAXSTR
   1876 #define MNTMAXSTR 2048		/* And hope it's enough */
   1877 #endif
   1878 
   1879 #ifdef QUOTA_DEVICE
   1880 
   1881 int path_to_device(char *pathname, char *result)
   1882 {
   1883     FILE *fp;
   1884 #ifdef HAS_OLDSTYLE_GETMNTENT
   1885     struct mnttab static_mp;
   1886     struct mnttab *mp = &static_mp;
   1887 #else
   1888     struct mntent *mp;
   1889 #endif
   1890     struct mount_ent {
   1891 	char mnt_fsname[MNTMAXSTR], mnt_dir[MNTMAXSTR];
   1892 	struct mount_ent *next;
   1893     } mountent;
   1894     struct mount_ent *current, *start, *new;
   1895     char path[1024], mnt_dir[1024], *pos;
   1896     int flag = 1;
   1897 
   1898     start = current = NULL;
   1899 #ifdef HAS_OLDSTYLE_GETMNTENT
   1900     fp = fopen(MNTTAB, "r");
   1901 #else
   1902     fp = setmntent(MNTTAB, "r");
   1903 #endif
   1904     if (fp == NULL)
   1905 	return 0;
   1906 #ifdef HAS_OLDSTYLE_GETMNTENT
   1907     while (getmntent(fp, &static_mp) == 0)
   1908 #else
   1909     while (mp = getmntent(fp))
   1910 #endif
   1911     {
   1912 	if (!(new = (struct mount_ent *) malloc(sizeof(mountent)))) {
   1913 	    perror("malloc");
   1914 	    flag = 0;
   1915 	    break;
   1916 	}
   1917 
   1918 	if (!start)
   1919 	    start = current = new;
   1920 	else
   1921 	    current = current->next = new;
   1922 
   1923 #ifdef HAS_OLDSTYLE_GETMNTENT
   1924 	strncpy(current->mnt_fsname, mp->mnt_special, strlen(mp->mnt_special) + 1);
   1925 	strncpy(current->mnt_dir, mp->mnt_mountp, strlen(mp->mnt_mountp) + 1);
   1926 #else
   1927 	strncpy(current->mnt_fsname, mp->mnt_fsname, strlen(mp->mnt_fsname) + 1);
   1928 	strncpy(current->mnt_dir, mp->mnt_dir, strlen(mp->mnt_dir) + 1);
   1929 #endif
   1930     }
   1931 #ifdef HAS_OLDSTYLE_GETMNTENT
   1932     fclose(fp);
   1933 #else
   1934     endmntent(fp);
   1935 #endif
   1936     current->next = NULL;
   1937 
   1938     wu_realpath(pathname, path, chroot_path);
   1939 
   1940     while (*path && flag) {
   1941 	current = start;
   1942 	while (current && flag) {
   1943 	    if (strcmp(current->mnt_dir, "swap")) {
   1944 		wu_realpath(current->mnt_dir, mnt_dir, chroot_path);
   1945 		if (!strcmp(mnt_dir, path)) {
   1946 		    flag = 0;
   1947 		    /* no support for remote quota yet */
   1948 		    if (!strchr(current->mnt_fsname, ':'))
   1949 			strcpy(result, current->mnt_fsname);
   1950 		}
   1951 	    }
   1952 	    current = current->next;
   1953 	}
   1954 	if (!((pos = strrchr(path, '/')) - path) && strlen(path) > 1)
   1955 	    strcpy(path, "/");
   1956 	else
   1957 	    path[pos - path] = '\0';
   1958     }
   1959     while (current) {
   1960 	new = current->next;
   1961 	free(current);
   1962 	current = new;
   1963     }
   1964     return 1;
   1965 }
   1966 #endif
   1967 
   1968 void get_quota(char *fs, int uid)
   1969 {
   1970     char mnt_fsname[MNTMAXSTR];
   1971 #ifdef HAS_NO_QUOTACTL
   1972     int dirfd;
   1973     struct quotctl qp;
   1974 #endif
   1975 
   1976     /*
   1977      * Getting file system quota information can take a noticeable amount
   1978      * of time, so only get quota information for specified users.
   1979      * quota-info <uid-range> [<uid-range> ...]
   1980      */
   1981     if (!uid_match("quota-info", uid))
   1982 	return;
   1983 
   1984 #ifdef HAS_NO_QUOTACTL
   1985     if (path_to_device(fs, mnt_fsname)) {
   1986 	dirfd = open(fs, O_RDONLY);
   1987 	qp.op = Q_GETQUOTA;
   1988 	qp.uid = uid;
   1989 	qp.addr = (char *) &quota;
   1990 	ioctl(dirfd, Q_QUOTACTL, &qp);
   1991 	close(dirfd);
   1992     }
   1993 #else
   1994 #ifdef QUOTA_DEVICE
   1995 
   1996     if (path_to_device(fs, mnt_fsname))
   1997 #ifdef QCMD
   1998 	quotactl(QCMD(Q_GETQUOTA, USRQUOTA), mnt_fsname, uid, (char *) &quota);
   1999 #else
   2000 	quotactl(Q_GETQUOTA, mnt_fsname, uid, (char *) &quota);
   2001 #endif
   2002 #else
   2003     quotactl(fs, QCMD(Q_GETQUOTA, USRQUOTA), uid, (char *) &quota);
   2004 #endif
   2005 #endif /* HAS_NO_QUOTACTL */
   2006 }
   2007 
   2008 char *time_quota(long curstate, long softlimit, long timelimit, char *timeleft)
   2009 {
   2010     struct timeval tv;
   2011 
   2012     gettimeofday(&tv, NULL);
   2013     if (softlimit && curstate >= softlimit) {
   2014 	if (timelimit == 0) {
   2015 	    strcpy(timeleft, "NOT STARTED");
   2016 	}
   2017 	else if (timelimit > tv.tv_sec) {
   2018 	    fmttime(timeleft, timelimit - tv.tv_sec);
   2019 	}
   2020 	else {
   2021 	    strcpy(timeleft, "EXPIRED");
   2022 	}
   2023     }
   2024     else {
   2025 	timeleft[0] = '\0';
   2026     }
   2027     return (timeleft);
   2028 }
   2029 
   2030 void fmttime(char *buf, register long time)
   2031 {
   2032     int i;
   2033     static struct {
   2034 	int c_secs;		/* conversion units in secs */
   2035 	char *c_str;		/* unit string */
   2036     } cunits[] = {
   2037 	{
   2038 	    60 *60 * 24 * 28, "months"
   2039 	} ,
   2040 	{
   2041 	    60 *60 * 24 * 7, "weeks"
   2042 	} ,
   2043 	{
   2044 	    60 *60 * 24, "days"
   2045 	} ,
   2046 	{
   2047 	    60 *60, "hours"
   2048 	} ,
   2049 	{
   2050 	    60, "mins"
   2051 	} ,
   2052 	{
   2053 	    1, "secs"
   2054 	}
   2055     };
   2056 
   2057     if (time <= 0) {
   2058 	strcpy(buf, "EXPIRED");
   2059 	return;
   2060     }
   2061     for (i = 0; i < sizeof(cunits) / sizeof(cunits[0]); i++) {
   2062 	if (time >= cunits[i].c_secs)
   2063 	    break;
   2064     }
   2065     sprintf(buf, "%.1f %s", (double) time / cunits[i].c_secs, cunits[i].c_str);
   2066 }
   2067 
   2068 #endif
   2069 
   2070 #ifdef THROUGHPUT
   2071 
   2072 int file_compare(char *patterns, char *file)
   2073 {
   2074     char buf[MAXPATHLEN+1];
   2075     char *cp;
   2076     char *cp2;
   2077     int i;
   2078     int matches = 0;
   2079 
   2080     strncpy(buf, patterns, sizeof(buf) - 1);
   2081     buf[sizeof(buf) - 2] = '\0';
   2082     i = strlen(buf);
   2083     buf[i++] = ',';
   2084     buf[i++] = '\0';
   2085 
   2086     cp = buf;
   2087     while ((cp2 = strchr(cp, ',')) != NULL) {
   2088 	*cp2++ = '\0';
   2089 	if (wu_fnmatch(cp, file, FNM_PATHNAME) == 0) {
   2090 	    matches = 1;
   2091 	    break;
   2092 	}
   2093 	cp = cp2;
   2094     }
   2095     return matches;
   2096 }
   2097 
   2098 int remote_compare(char *patterns)
   2099 {
   2100     char buf[MAXPATHLEN+1];
   2101     char *cp;
   2102     char *cp2;
   2103     int i;
   2104     int matches = 0;
   2105 
   2106     strncpy(buf, patterns, sizeof(buf) - 1);
   2107     buf[sizeof(buf) - 2] = '\0';
   2108     i = strlen(buf);
   2109     buf[i++] = ',';
   2110     buf[i++] = '\0';
   2111 
   2112     cp = buf;
   2113     while ((cp2 = strchr(cp, ',')) != NULL) {
   2114 	*cp2++ = '\0';
   2115 	if (hostmatch(cp, remoteaddr, remotehost)) {
   2116 	    matches = 1;
   2117 	    break;
   2118 	}
   2119 	cp = cp2;
   2120     }
   2121     return matches;
   2122 }
   2123 
   2124 void throughput_calc(char *name, int *bps, double *bpsmult)
   2125 {
   2126     int match_value = -1;
   2127     char cwdir[MAXPATHLEN];
   2128     char pwdir[MAXPATHLEN];
   2129     char path[MAXPATHLEN];
   2130     char file[MAXPATHLEN];
   2131     char *ap3 = NULL, *ap4 = NULL;
   2132     struct aclmember *entry = NULL;
   2133     extern char *home;
   2134     char *sp;
   2135     int i;
   2136 
   2137     /* default is maximum throughput */
   2138     *bps = -1;
   2139     *bpsmult = 1.0;
   2140 
   2141     /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
   2142        out with an error. The rest of wu is so crufy that a long path might
   2143        just blow up later */
   2144 
   2145     if ((strlen(name) + 1) > sizeof(path)) {
   2146 	return;
   2147     }
   2148 
   2149     /* what's our current directory? */
   2150     strcpy(path, name);
   2151     if ((sp = strrchr(path, '/')))
   2152 	*sp = '\0';
   2153     else
   2154 	strcpy(path, ".");
   2155     if ((sp = strrchr(name, '/')))
   2156 	strcpy(file, sp + 1);
   2157     else
   2158 	strcpy(file, name);
   2159     if ((fb_realpath(path, cwdir)) == NULL) {
   2160 	return;
   2161     }
   2162 
   2163     wu_realpath(home, pwdir, chroot_path);
   2164 
   2165     /* find best matching entry */
   2166     while (getaclentry("throughput", &entry) && ARG0 && ARG1 && ARG2 && ARG3 && ARG4 && ARG5 != NULL) {
   2167 	if ((0 < path_compare(ARG0, pwdir))
   2168 	    && ((i = path_compare(ARG1, cwdir)) >= match_value)
   2169 	    ) {
   2170 	    if (file_compare(ARG2, file)) {
   2171 		if (remote_compare(ARG5)) {
   2172 		    match_value = i;
   2173 		    ap3 = ARG3;
   2174 		    ap4 = ARG4;
   2175 		}
   2176 	    }
   2177 	}
   2178     }
   2179 
   2180     /* if we did get matches */
   2181     if (match_value >= 0) {
   2182 	if (strcasecmp(ap3, "oo") == 0)
   2183 	    *bps = -1;
   2184 	else
   2185 	    *bps = atoi(ap3);
   2186 	if (strcmp(ap4, "-") == 0)
   2187 	    *bpsmult = 1.0;
   2188 	else
   2189 	    *bpsmult = atof(ap4);
   2190     }
   2191     return;
   2192 }
   2193 
   2194 void throughput_adjust(char *name)
   2195 {
   2196     int match_value = -1;
   2197     char pwdir[MAXPATHLEN];
   2198     char cwdir[MAXPATHLEN];
   2199     char path[MAXPATHLEN];
   2200     char file[MAXPATHLEN];
   2201     char buf[MAXPATHLEN];
   2202     char *ap3 = NULL, *ap4 = NULL;
   2203     char **pap;
   2204     struct aclmember *entry = NULL;
   2205     extern char *home;
   2206     char *sp;
   2207     int i;
   2208 
   2209     /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
   2210        out with an error. The rest of wu is so crufy that a long path might
   2211        just blow up later */
   2212 
   2213     if ((strlen(name) + 1) > sizeof(path)) {
   2214 	return;
   2215     }
   2216 
   2217     /* what's our current directory? */
   2218     strcpy(path, name);
   2219     if ((sp = strrchr(path, '/')))
   2220 	*sp = '\0';
   2221     else
   2222 	strcpy(path, ".");
   2223     if ((sp = strrchr(name, '/')))
   2224 	strcpy(file, sp + 1);
   2225     else
   2226 	strcpy(file, name);
   2227     if ((fb_realpath(path, cwdir)) == NULL) {
   2228 	return;
   2229     }
   2230 
   2231     wu_realpath(home, pwdir, chroot_path);
   2232 
   2233     /* find best matching entry */
   2234     while (getaclentry("throughput", &entry) && ARG0 && ARG1 && ARG2 && ARG3 && ARG4 && ARG5 != NULL) {
   2235 	if ((0 < path_compare(ARG0, pwdir))
   2236 	    && ((i = path_compare(ARG1, cwdir)) >= match_value)
   2237 	    ) {
   2238 	    if (file_compare(ARG2, file)) {
   2239 		if (remote_compare(ARG5)) {
   2240 		    match_value = i;
   2241 		    ap3 = ARG3;
   2242 		    pap = ARG;
   2243 		    ap4 = ARG4;
   2244 		}
   2245 	    }
   2246 	}
   2247     }
   2248 
   2249     /* if we did get matches */
   2250     if (match_value >= 0) {
   2251 	if (strcasecmp(ap3, "oo") != 0) {
   2252 	    if (strcmp(ap4, "-") != 0) {
   2253 		sprintf(buf, "%.0f", atoi(ap3) * atof(ap4));
   2254 		pap[3] = (char *) malloc(strlen(buf) + 1);
   2255 		if (pap[3] == NULL) {
   2256 		    syslog(LOG_ERR, "malloc error in throughput_adjust");
   2257 		    dologout(1);
   2258 		}
   2259 		/* Use ARG6 to keep track of malloced memory */
   2260 		if (pap[6])
   2261 		    free(pap[6]);
   2262 		pap[6] = pap[3];
   2263 		strcpy(pap[3], buf);
   2264 	    }
   2265 	}
   2266     }
   2267     return;
   2268 }
   2269 
   2270 #endif
   2271 
   2272 #ifdef SOLARIS_2
   2273 static int CheckMethod = 1;
   2274 #else
   2275 static int CheckMethod = 0;
   2276 #endif
   2277 
   2278 void SetCheckMethod(const char *method)
   2279 {
   2280     if ((strcasecmp(method, "md5") == 0)
   2281 	|| (strcasecmp(method, "rfc1321") == 0))
   2282 	CheckMethod = 0;
   2283     else if ((strcasecmp(method, "crc") == 0)
   2284 	     || (strcasecmp(method, "posix") == 0))
   2285 	CheckMethod = 1;
   2286     else {
   2287 	reply(500, "Unrecognized checksum method");
   2288 	return;
   2289     }
   2290     switch (CheckMethod) {
   2291     default:
   2292 	reply(200, "Checksum method is now: MD5 (RFC1321)");
   2293 	break;
   2294     case 1:
   2295 	reply(200, "Checksum method is now: CRC (POSIX)");
   2296 	break;
   2297     }
   2298 }
   2299 
   2300 void ShowCheckMethod(void)
   2301 {
   2302     switch (CheckMethod) {
   2303     default:
   2304 	reply(200, "Current checksum method: MD5 (RFC1321)");
   2305 	break;
   2306     case 1:
   2307 	reply(200, "Current checksum method: CRC (POSIX)");
   2308 	break;
   2309     }
   2310 }
   2311 
   2312 void CheckSum(char *pathname)
   2313 {
   2314     char *cmd;
   2315     char buf[MAXPATHLEN];
   2316     FILE *cmdf;
   2317     struct stat st;
   2318 
   2319     if (stat(pathname, &st) == 0) {
   2320 	if ((st.st_mode & S_IFMT) != S_IFREG) {
   2321 	    reply(500, "%s: not a plain file.", pathname);
   2322 	    return;
   2323 	}
   2324     }
   2325     else {
   2326 	perror_reply(550, pathname);
   2327 	return;
   2328     }
   2329 
   2330     switch (CheckMethod) {
   2331     default:
   2332 	cmd = "/bin/md5sum";
   2333 	break;
   2334     case 1:
   2335 	cmd = "/bin/cksum";
   2336 	break;
   2337     }
   2338 
   2339     if (strlen(cmd) + 1 + strlen(pathname) + 1 > sizeof(buf)) {
   2340 	reply(500, "Pathname too long");
   2341 	return;
   2342     }
   2343     sprintf(buf, "%s %s", cmd, pathname);
   2344 
   2345     cmdf = ftpd_popen(buf, "r", 0);
   2346     if (!cmdf) {
   2347 	perror_reply(550, cmd);
   2348     }
   2349     else {
   2350 	if (fgets(buf, sizeof buf, cmdf)) {
   2351 	    char *crptr = strchr(buf, '\n');
   2352 	    if (crptr != NULL)
   2353 		*crptr = '\0';
   2354 	    reply(200, "%s", buf);
   2355 	}
   2356 	ftpd_pclose(cmdf);
   2357     }
   2358 }
   2359 
   2360 void CheckSumLastFile(void)
   2361 {
   2362     extern char LastFileTransferred[];
   2363 
   2364     if (LastFileTransferred[0] == '\0')
   2365 	reply(500, "Nothing transferred yet");
   2366     else
   2367 	CheckSum(LastFileTransferred);
   2368 }
   2369