/* Copyright (c) 1993, 1994  Washington University in Saint Louis
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer. 2.
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution. 3. All advertising
 * materials mentioning features or use of this software must display the
 * following acknowledgement: This product includes software developed by the
 * Washington University in Saint Louis and its contributors. 4. Neither the
 * name of the University nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific
 * prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY WASHINGTON UNIVERSITY AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASHINGTON
 * UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef lint
static char rcsid[] = "@(#)$Id: extensions.c,v 1.1 1999/06/30 17:19:32 he Exp $";
#endif /* not lint */

#include "config.h"

#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef SYSSYSLOG
#include <sys/syslog.h>
#else
#include <syslog.h>
#endif
#include <time.h>
#include <pwd.h>
#include <setjmp.h>
#include <grp.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/param.h>

#if defined(HAVE_STATVFS)
#include <sys/statvfs.h>
#elif defined(HAVE_SYS_VFS)
#include <sys/vfs.h>
#elif defined(HAVE_SYS_MOUNT)
#include <sys/mount.h>
#endif

#include <arpa/ftp.h>

#include "pathnames.h"
#include "extensions.h"

#if defined(HAVE_FTW)
#include <ftw.h>
#else
#include "support/ftw.h"
#endif

#ifdef HAVE_REGEX_H
#include <regex.h>
#endif

#if defined(REGEX) && defined(SVR4) && !(defined(__hpux))
#include <libgen.h>
#endif

extern int fnmatch(),
  type,
  transflag,
  ftwflag,
  authenticated,
  autospout_free,
  data,
  pdata,
  anonymous,
  guest;
#ifdef __STDC__
extern char **ftpglob(register char *v),
#else
extern char **ftpglob(),
#endif
 *globerr,
  remotehost[],
  hostname[],
  authuser[],
 *autospout,
  Shutdown[];

char shuttime[30],
  denytime[30],
  disctime[30];

#ifdef __STDC__
extern char *realpath(const char *pathname, char *result);
#else
extern char *realpath();
#endif

#ifndef REGEX
char *re_comp();
#elif defined(M_UNIX)
extern char *regcmp(), *regex();
#endif

#ifdef __STDC__
extern FILE *dataconn(char *name, off_t size, char *mode);
#else
extern FILE *dataconn();
#endif
FILE *dout;

time_t newer_time;

int show_fullinfo;

int
#ifdef __STDC__
check_newer(char *path, struct stat *st, int flag)
#else
check_newer(path,st,flag)
char *path;
struct stat *st;
int flag;
#endif
{

    if (st->st_mtime > newer_time) {
        if (show_fullinfo != 0) {
            if (flag == FTW_F || flag == FTW_D) {
                fprintf(dout, "%s %d %d %s\n", flag == FTW_F ? "F" : "D",
                        st->st_size, st->st_mtime, path);
            }
        } else if (flag == FTW_F)
            fprintf(dout, "%s\n", path);
    }
    /* When an ABOR has been received (which sets ftwflag > 1) return a
     * non-zero value which causes ftw to stop tree traversal and return. */
    return (ftwflag > 1 ? 1 : 0);
}

#if defined(HAVE_STATVFS)
int getSize(s)
char *s;
{
    int c;
    struct statvfs buf;

    if (( c = statvfs(s, &buf)) != 0)
        return(0);

    return(buf.f_bavail * buf.f_frsize / 1024);
}
#elif defined(HAVE_SYS_VFS) || defined (HAVE_SYS_MOUNT)
int getSize(s)
char *s;
{
    int c;
    struct statfs buf;

    if (( c = statfs(s, &buf)) != 0)
        return(0);

    return(buf.f_bavail * buf.f_bsize / 1024);
}
#endif

/*************************************************************************/
/* FUNCTION  : msg_massage                                               */
/* PURPOSE   : Scan a message line for magic cookies, replacing them as  */
/*             needed.                                                   */
/* ARGUMENTS : pointer input and output buffers                          */
/*************************************************************************/

void
#ifdef __STDC__
msg_massage(char *inbuf, char *outbuf)
#else
msg_massage(inbuf,outbuf)
char *inbuf;
char *outbuf;
#endif
{
    char *inptr = inbuf;
    char *outptr = outbuf;
    char buffer[MAXPATHLEN];
    time_t curtime;
    int limit;
    extern struct passwd *pw;
    struct aclmember *entry;

    (void) acl_getclass(buffer);
    limit = acl_getlimit(buffer, NULL);

    while (*inptr) {
        if (*inptr != '%')
            *outptr++ = *inptr;
        else {
            entry = NULL;
            switch (*++inptr) {
            case 'E':
                if ( (getaclentry("email", &entry)) && ARG0 )
                    sprintf(outptr, "%s", ARG0); 
                else
                    *outptr = '\0';
                break;
            case 'N': 
                sprintf(outptr, "%d", acl_countusers(buffer)); 
                break; 
            case 'M':
	        if (limit > 0){
                  sprintf(outptr, "%d", limit);
  	        }else{
		  strcpy(outptr,"unlimited");
		}
                break;
            case 'T':
 	        (void) time(&curtime);
                strncpy(outptr, ctime(&curtime), 24);
                *(outptr + 24) = '\0';
                break;

            case 'F':
#if defined(HAVE_STATVFS) || defined(HAVE_SYS_VFS) || defined(HAVE_SYS_MOUNT)
                sprintf(outptr, "%lu", getSize("."));
#endif
                break;

            case 'C':
#ifdef HAVE_GETCWD
                (void) getcwd(outptr, MAXPATHLEN);
#else
                (void) getwd(outptr);
#endif
                break;

            case 'R':
                strcpy(outptr, remotehost);
                break;

            case 'L':
                strcpy(outptr, hostname);
                break;

            case 'U':
                if (pw)
		  strcpy(outptr, pw->pw_name);
		else
		  strcpy(outptr, "[unknown]");
                break;

            case 's':
                strncpy(outptr, shuttime, 24);
                *(outptr + 24) = '\0';
                break;

            case 'd':
                strncpy(outptr, disctime, 24);
                *(outptr + 24) = '\0';
                break;

            case 'r':
                strncpy(outptr, denytime, 24);
                *(outptr + 24) = '\0';
                break;

/* KH : cookie %u for RFC931 name */
            case 'u':
                if (authenticated) strncpy(outptr, authuser, 24);
                else strcpy(outptr,"[unknown]");
                *(outptr + 24) = '\0'; 
                break;

            case '%':
                *outptr++ = '%';
                *outptr = '\0';
                break;

            default:
                *outptr++ = '%';
                *outptr++ = '?';
                *outptr = '\0';
                break;
            }
            while (*outptr)
                outptr++;
        }
        inptr++;
    }
    *outptr = '\0';
}

/*************************************************************************/
/* FUNCTION  : cwd_beenhere                                              */
/* PURPOSE   : Return 1 if the user has already visited this directory   */
/*             via C_WD.                                                 */
/* ARGUMENTS : a power-of-two directory function code (README, MESSAGE)  */
/*************************************************************************/

int
#ifdef __STDC__
cwd_beenhere(int dircode)
#else
cwd_beenhere(dircode)
int dircode;
#endif
{
    struct dirlist {
        struct dirlist *next;
        int dircode;
        char dirname[1];
    };

    static struct dirlist *head = NULL;
    struct dirlist *curptr;
    char cwd[MAXPATHLEN];

    (void) realpath(".", cwd);

    for (curptr = head; curptr != NULL; curptr = curptr->next)
        if (strcmp(curptr->dirname, cwd) == 0) {
            if (!(curptr->dircode & dircode)) {
                curptr->dircode |= dircode;
                return (0);
            }
            return (1);
        }
    curptr = (struct dirlist *) malloc(strlen(cwd) + 1 + sizeof(struct dirlist));

    if (curptr != NULL) {
        curptr->next = head;
        head = curptr;
        curptr->dircode = dircode;
        strcpy(curptr->dirname, cwd);
    }
    return (0);
}

/*************************************************************************/
/* FUNCTION  : show_banner                                               */
/* PURPOSE   : Display a banner on the user's terminal before login      */
/* ARGUMENTS : reply code to use                                         */
/*************************************************************************/
 
void
#ifdef __STDC__
show_banner(int msgcode)
#else
show_banner(msgcode)
int msgcode;
#endif
{
    char *crptr,
      linebuf[1024],
      outbuf[1024];
    struct aclmember *entry = NULL;
    FILE *infile;

#ifdef VIRTUAL
    extern int virtual_mode;
    extern char virtual_banner[];

    if (virtual_mode) {
        infile = fopen(virtual_banner, "r");
	if (infile) {
 	    while (fgets(linebuf, 255, infile) != NULL) {
	           if ((crptr = strchr(linebuf, '\n')) != NULL)
		        *crptr = '\0';
		   msg_massage(linebuf, outbuf);
		   lreply(msgcode, "%s", outbuf);
		 }
	    fclose(infile);
	    lreply(msgcode, "");
	  }
      }
    else {
#endif
      /* banner <path> */
      while (getaclentry("banner", &entry)) {
	    infile = fopen(ARG0, "r");
	    if (infile) {
	        while (fgets(linebuf, 255, infile) != NULL) {
		  if ((crptr = strchr(linebuf, '\n')) != NULL)
		    *crptr = '\0';
		  msg_massage(linebuf, outbuf);
		  lreply(msgcode, "%s", outbuf);
		}
		fclose(infile);
		lreply(msgcode, "");
	    }
	 }
#ifdef VIRTUAL
    }
#endif
  }
/*************************************************************************/
/* FUNCTION  : show_message                                              */
/* PURPOSE   : Display a message on the user's terminal if the current   */
/*             conditions are right                                      */
/* ARGUMENTS : reply code to use, LOG_IN|CMD                             */
/*************************************************************************/

void
#ifdef __STDC__
show_message(int msgcode, int mode)
#else
show_message(msgcode,mode)
int msgcode;
int mode;
#endif
{
    char *crptr,
      linebuf[1024],
      outbuf[1024],
      class[MAXPATHLEN],
      cwd[MAXPATHLEN];
    int show,
      which;
    struct aclmember *entry = NULL;
    FILE *infile;

    if (cwd_beenhere(1) != 0)
        return;

#ifdef HAVE_GETCWD
    (void) getcwd(cwd,MAXPATHLEN-1);
#else
    (void) getwd(cwd);
#endif
    (void) acl_getclass(class);

    /* message <path> [<when> [<class>]] */
    while (getaclentry("message", &entry)) {
        if (!ARG0)
            continue;
        show = 0;

        if (mode == LOG_IN && (!ARG1 || !strcasecmp(ARG1, "login")))
            if (!ARG2)
                show++;
            else {
                for (which = 2; (which < MAXARGS) && ARG[which]; which++)
                    if (strcasecmp(class, ARG[which]) == 0)
                        show++;
            }
        if (mode == C_WD && ARG1 && !strncasecmp(ARG1, "cwd=", 4) &&
            (!strcmp((ARG1) + 4, cwd) || *(ARG1 + 4) == '*' ||
            !fnmatch((ARG1) + 4, cwd, FNM_PATHNAME)))
            if (!ARG2)
                show++;
            else {
                for (which = 2; (which < MAXARGS) && ARG[which]; which++)
                    if (strcasecmp(class, ARG[which]) == 0)
                        show++;
            }
        if (show && (int)strlen(ARG0) > 0) {
            infile = fopen(ARG0, "r");
            if (infile) {
                while (fgets(linebuf, 255, infile) != NULL) {
                    if ((crptr = strchr(linebuf, '\n')) != NULL)
                        *crptr = '\0';
                    msg_massage(linebuf, outbuf);
                    lreply(msgcode, "%s", outbuf);
                }
                fclose(infile);
                lreply(msgcode, "");
            }
        }
    }
}

/*************************************************************************/
/* FUNCTION  : show_readme                                               */
/* PURPOSE   : Display a message about a README file to the user if the  */
/*             current conditions are right                              */
/* ARGUMENTS : pointer to ACL buffer, reply code, LOG_IN|C_WD            */
/*************************************************************************/

void
#ifdef __STDC__
show_readme(int code, int mode)
#else
show_readme(code,mode)
int code;
int mode;
#endif
{
    char **filelist,
      **sfilelist,
      class[MAXPATHLEN],
      cwd[MAXPATHLEN];
    int show,
      which,
      days;
    time_t clock;

    struct stat buf;
    struct tm *tp;
    struct aclmember *entry = NULL;

    if (cwd_beenhere(2) != 0)
        return;

#ifdef HAVE_GETCWD
    (void) getcwd(cwd,MAXPATHLEN-1);
#else
    (void) getwd(cwd);
#endif
    (void) acl_getclass(class);

    /* readme  <path> {<when>} */
    while (getaclentry("readme", &entry)) {
        if (!ARG0)
            continue;
        show = 0;

        if (mode == LOG_IN && (!ARG1 || !strcasecmp(ARG1, "login")))
            if (!ARG2)
                show++;
            else {
                for (which = 2; (which < MAXARGS) && ARG[which]; which++)
                    if (strcasecmp(class, ARG[which]) == 0)
                        show++;
            }
        if (mode == C_WD && ARG1 && !strncasecmp(ARG1, "cwd=", 4)
            && (!strcmp((ARG1) + 4, cwd) || *(ARG1 + 4) == '*' ||
                !fnmatch((ARG1) + 4, cwd, FNM_PATHNAME)))
            if (!ARG2)
                show++;
            else {
                for (which = 2; (which < MAXARGS) && ARG[which]; which++)
                    if (strcasecmp(class, ARG[which]) == 0)
                        show++;
            }
        if (show) {
            globerr = NULL;
            filelist = ftpglob(ARG0);
            sfilelist = filelist;  /* save to free later */
            if (!globerr) {
                while (filelist && *filelist) {
                   errno = 0;
                   if (!stat(*filelist, &buf) &&
                       (buf.st_mode & S_IFMT) == S_IFREG) {
                       lreply(code, "Please read the file %s", *filelist);
                       (void) time(&clock);
                       tp = localtime(&clock);
                       days = 365 * tp->tm_year + tp->tm_yday;
                       tp = localtime((time_t *)&buf.st_mtime);
                       days -= 365 * tp->tm_year + tp->tm_yday;
/*
                       if (days == 0) {
                         lreply(code, "  it was last modified on %.24s - Today",
                           ctime((time_t *)&buf.st_mtime));
                       } else {
*/
                         lreply(code, 
                           "  it was last modified on %.24s - %d day%s ago",
                           ctime((time_t *)&buf.st_mtime), days, days == 1 ? "" : "s");
/*
                       }
*/
                   }
                   filelist++;
                }
            }
            if (sfilelist) {
                blkfree(sfilelist);
                free((char *) sfilelist);
            }
        }
    }
}

/*************************************************************************/
/* FUNCTION  : deny_badxfertype                                          */
/* PURPOSE   : If user is in ASCII transfer mode and tries to retrieve a */
/*             binary file, abort transfer and display appropriate error */
/* ARGUMENTS : message code to use for denial, path of file to check for */
/*             binary contents or NULL to assume binary file             */
/*************************************************************************/

int
#ifdef __STDC__
deny_badasciixfer(int msgcode, char *filepath)
#else
deny_badasciixfer(msgcode,filepath)
int msgcode;
char *filepath;
#endif
{

    if (type == TYPE_A && !*filepath) {
        reply(msgcode, "This is a BINARY file, using ASCII mode to transfer will corrupt it.");
        return (1);
    }
    /* The hooks are here to prevent transfers of actual binary files, not
     * just TAR or COMPRESS mode files... */
    return (0);
}

/*************************************************************************/
/* FUNCTION  : is_shutdown                                               */
/* PURPOSE   :                                                           */
/* ARGUMENTS :                                                           */
/*************************************************************************/

int
#ifdef __STDC__
is_shutdown(int quiet, int new)
#else
is_shutdown(quiet, new)
int quiet;
int new;
#endif
{
    static struct tm tmbuf;
    static struct stat s_last;
    static time_t last = 0,
      shut,
      deny,
      disc;
    static int valid;
    static char text[2048];

    struct stat s_cur;

    FILE *fp;

    int deny_off,
      disc_off;

    time_t curtime = time(NULL);

    char buf[1024],
      linebuf[1024];

    if (Shutdown[0] == '\0' || stat(Shutdown, &s_cur))
        return (0);

    if (s_last.st_mtime != s_cur.st_mtime) {
        s_last = s_cur;
        valid = 0;

        fp = fopen(Shutdown, "r");
        if (fp == NULL)
            return (0);
        fgets(buf, sizeof(buf), fp);
        if (sscanf(buf, "%d %d %d %d %d %d %d", &tmbuf.tm_year, &tmbuf.tm_mon,
        &tmbuf.tm_mday, &tmbuf.tm_hour, &tmbuf.tm_min, &deny, &disc) != 7) {
            (void) fclose(fp);
            return (0);
        }
        valid = 1;
        deny_off = 3600 * (deny / 100) + 60 * (deny % 100);
        disc_off = 3600 * (disc / 100) + 60 * (disc % 100);

        tmbuf.tm_year -= 1900;
        tmbuf.tm_isdst = -1;
        shut = mktime(&tmbuf);
        strcpy(shuttime, ctime(&shut));

        disc = shut - disc_off;
        strcpy(disctime, ctime(&disc));

        deny = shut - deny_off;
        strcpy(denytime, ctime(&deny));

        text[0] = '\0';

        while (fgets(buf, sizeof(buf), fp) != NULL) {
            msg_massage(buf, linebuf);
            if ((strlen(text) + strlen(linebuf)) < sizeof(text))
                strcat(text, linebuf);
        }

        (void) fclose(fp);
    }
    if (!valid)
        return (0);

    /* if last == 0, then is_shutdown() only called with quiet == 1 so far */
    if (last == 0 && !quiet) {
        autospout = text;       /* warn them for the first time */
        autospout_free = 0;
        last = curtime;
    }
    /* if a new connection and past deny time, tell caller to drop 'em */
    if (new && curtime > deny)
        return (1);

    /* if past disconnect time, tell caller to drop 'em */
    if (curtime > disc)
        return (1);

    /* if less than 60 seconds to disconnection, warn 'em continuously */
    if (curtime > (disc - 60) && !quiet) {
        autospout = text;
        autospout_free = 0;
        last = curtime;
    }
    /* if less than 15 minutes to disconnection, warn 'em every 5 mins */
    if (curtime > (disc - 60 * 15)) {
        if ((curtime - last) > (60 * 5) && !quiet) {
            autospout = text;
            autospout_free = 0;
            last = curtime;
        }
    }
    /* if less than 24 hours to disconnection, warn 'em every 30 mins */
    if (curtime < (disc - 24 * 60 * 60) && !quiet) {
        if ((curtime - last) > (60 * 30)) {
            autospout = text;
            autospout_free = 0;
            last = curtime;
        }
    }
    /* if more than 24 hours to disconnection, warn 'em every 60 mins */
    if (curtime > (disc - 24 * 60 * 60) && !quiet) {
        if ((curtime - last) >= (24 * 60 * 60)) {
            autospout = text;
            autospout_free = 0;
            last = curtime;
        }
    }
    return (0);
}

void
#ifdef __STDC__
newer(char *date, char *path, int showlots)
#else
newer(date,path,showlots)
char *date;
char *path;
int showlots;
#endif
{
    struct tm tm;

    if (sscanf(date, "%04d%02d%02d%02d%02d%02d",
               &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
               &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {

        tm.tm_year -= 1900;
        tm.tm_mon--;
        tm.tm_isdst = -1;
        newer_time = mktime(&tm);
        dout = dataconn("file list", (off_t) - 1, "w");

        if (dout != NULL) {
            /* As ftw allocates storage it needs a chance to cleanup, setting
             * ftwflag prevents myoob from calling longjmp, incrementing
             * ftwflag instead which causes check_newer to return non-zero
             * which makes ftw return. */
            ftwflag = 1;
            transflag++;
            show_fullinfo = showlots;
#if defined(HAVE_FTW)
            ftw(path, check_newer, -1);
#else
            treewalk(path, check_newer, -1, NULL);
#endif

            /* don't send a reply if myoob has already replied */
            if (ftwflag == 1) {
                if (ferror(dout) != 0)
                    perror_reply(550, "Data connection");
                else
                    reply(226, "Transfer complete.");
            }

            (void) fclose(dout);
            data = -1;
            pdata = -1;
            transflag = 0;
            ftwflag = 0;
        }
    } else
        reply(501, "Bad DATE format");
}

int
#ifdef __STDC__
type_match(char *typelist)
#else
type_match(typelist)
char *typelist;
#endif
{
    if (anonymous && strcasestr(typelist, "anonymous"))
        return (1);
    if (guest && strcasestr(typelist, "guest"))
        return (1);
    if (!guest && !anonymous && strcasestr(typelist, "real"))
        return (1);

    return (0);
}

int
#ifdef __STDC__
path_compare(char *p1, char *p2)
#else
path_compare(p1,p2)
char *p1;
char *p2;
#endif
{
    if ( fnmatch(p1, p2, NULL) == 0 ) /* 0 means they matched */
        return(strlen(p1));
    else
        return(-2);
}

void
#ifdef __STDC__
expand_id(void)
#else
expand_id()
#endif
{
    struct aclmember *entry = NULL;
    struct passwd *pwent;
    struct group *grent;
    char buf[BUFSIZ];

    while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
        if (ARG3 && ARG4) {
            pwent = getpwnam(ARG3);
            grent = getgrnam(ARG4);

            if (pwent)  sprintf(buf, "%d", pwent->pw_uid);
            else        sprintf(buf, "%d", 0);
            ARG3 = (char *) malloc(strlen(buf) + 1);
            strcpy(ARG3, buf);

            if (grent)  sprintf(buf, "%d", grent->gr_gid);
            else        sprintf(buf, "%d", 0);
            ARG4 = (char *) malloc(strlen(buf) + 1);
            strcpy(ARG4, buf);
	    endgrent();
        }
    }
}

int
#ifdef __STDC__
fn_check(char *name)
#else
fn_check(name)
char *name;
#endif
{
  /* check to see if this is a valid file name... path-filter <type>
   * <message_file> <allowed_charset> <disallowed> */

  struct aclmember *entry = NULL;
  int   j;
  char *sp;
  char *path;
#ifdef M_UNIX
# ifdef REGEX
  char *regp;
# endif
#endif

#ifdef REGEXEC
  regex_t regexbuf;
  regmatch_t regmatchbuf;
#endif

  while (getaclentry("path-filter", &entry) && ARG0 != NULL) {
      if (type_match(ARG0) && ARG1 && ARG2) {

		  /*
		   * check *only* the basename
		   */

		  if (path = strrchr(name, '/'))  ++path;
		  else	path = name;

          /* is it in the allowed character set? */
#if defined(REGEXEC)
          if (regcomp(&regexbuf, ARG2, REG_EXTENDED) != 0) {
              reply(553, "REGEX error");
#elif defined(REGEX)
          if ((sp = regcmp(ARG2, (char *) 0)) == NULL) {
              reply(553, "REGEX error");
#else
          if ((sp = re_comp(ARG2)) != 0) {
              perror_reply(553, sp);
#endif
              return(0);
          }
#if defined(REGEXEC)
          if (regexec(&regexbuf, path, 1, &regmatchbuf, 0) != 0) {
#elif defined(REGEX)
# ifdef M_UNIX
          regp = regex(sp, path);
          free(sp);
          if (regp == NULL) {
# else
          if ((regex(sp, path)) == NULL) {
# endif
#else
          if ((re_exec(path)) != 1) {
#endif
              pr_mesg(553, ARG1);
              reply(553, "%s: Permission denied. (Filename (accept))", name);
              return(0);
          }
          /* is it in any of the disallowed regexps */

          for (j = 3; j < MAXARGS; ++j) {
              /* ARGj == entry->arg[j] */
              if (entry->arg[j]) {
#if defined(REGEXEC)
                  if (regcomp(&regexbuf, entry->arg[j], REG_EXTENDED) != 0) {
                      reply(553, "REGEX error");
#elif defined(REGEX)
                  if ((sp = regcmp(entry->arg[j], (char *) 0)) == NULL) {
                      reply(553, "REGEX error");
#else
                  if ((sp = re_comp(entry->arg[j])) != 0) {
                      perror_reply(553, sp);
#endif
                      return(0);
                  }
#if defined(REGEXEC)
                  if (regexec(&regexbuf, path, 1, &regmatchbuf, 0) == 0) {
#elif defined(REGEX)
# ifdef M_UNIX
                  regp = regex(sp, path);
                  free(sp);
                  if (regp != NULL) {
# else
                  if ((regex(sp, path)) != NULL) {
# endif
#else
                  if ((re_exec(path)) == 1) {
#endif
                      pr_mesg(553, ARG1);
                      reply(553, "%s: Permission denied. (Filename (deny))", name);
                      return(0);
                  }
              }
          }
      }
  }
  return(1);
}

int
#ifdef __STDC__
dir_check(char *name, uid_t *uid, gid_t *gid, int *valid)
#else
dir_check(name,uid,gid,valid)
char *name;
uid_t *uid;
gid_t *gid;
int *valid;
#endif
{
  struct aclmember *entry = NULL;

  int i,
    match_value = -1;
  char *ap2 = NULL,
       *ap3 = NULL,
       *ap4 = NULL,
       *ap6 = NULL;
  char cwdir[BUFSIZ];
  char path[BUFSIZ];
  char *sp;
  extern struct passwd *pw;

  *valid = 0;
  /* what's our current directory? */

  strcpy(path, name);
  if (sp = strrchr(path, '/'))  *sp = '\0';
  else strcpy(path, ".");

  if ((realpath(path, cwdir)) == NULL) {
    perror_reply(553, "Could not determine cwdir");
    return(-1);
  }
  while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
      if ( (!strcmp(ARG0, pw->pw_dir)) &&
           ((i = path_compare(ARG1, cwdir)) >= match_value) ) {
          match_value = i;
          ap2 = ARG2;
          if (ARG3)  ap3 = ARG3;
          else       ap3 = NULL;
          if (ARG4)  ap4 = ARG4;
          else       ap4 = NULL;
          if (ARG6)  ap6 = ARG6;
          else       ap6 = NULL;
      }
  }
  if ( (ap3 && !strcasecmp(ap3, "nodirs")) ||
       (ap6 && !strcasecmp(ap6, "nodirs")) ) {
      reply(530, "%s: Permission denied. (Upload dirs)", name);
      return(0);
  }
  if (ap3)
     *uid = atoi(ap3);    /* the uid  */
  if (ap4) {
     *gid = atoi(ap4);    /* the gid */
     *valid = 1;
   }
  return(1);
}

int
#ifdef __STDC__
upl_check(char *name, uid_t *uid, gid_t *gid, int *f_mode, int *valid)
#else
upl_check(name,uid,gid,f_mode,valid)
char *name;
uid_t *uid;
gid_t *gid;
int *f_mode;
int *valid;
#endif
{
  int  match_value = -1;
  char cwdir[BUFSIZ];
  char path[BUFSIZ];
  char *sp;
  int  i;

  char *ap1 = NULL,
   *ap2 = NULL,
   *ap3 = NULL,
   *ap4 = NULL,
   *ap5 = NULL;

  struct aclmember *entry = NULL;
  extern struct passwd *pw;

  *valid = 0;

      /* what's our current directory? */

      strcpy(path, name);
      if (sp = strrchr(path, '/'))  *sp = '\0';
      else strcpy(path, ".");

      if ((realpath(path, cwdir)) == NULL) {
          perror_reply(553, "Could not determine cwdir");
          return(-1);
      }

      /* we are doing a "best match"... ..so we keep track of what "match
       * value" we have received so far... */

      while (getaclentry("upload", &entry) && ARG0 && ARG1 && ARG2 != NULL) {
          if ( (!strcmp(ARG0, pw->pw_dir)) &&
		       ((i = path_compare(ARG1, cwdir)) >= match_value) ) {
              match_value = i;
              ap1 = ARG1;
              ap2 = ARG2;
              if (ARG3) ap3 = ARG3;
              else      ap3 = NULL;
              if (ARG4) ap4 = ARG4;
              else      ap4 = NULL;
              if (ARG5) ap5 = ARG5;
              else      ap5 = NULL;
          }
	}

      if (ap3 && ( (!strcasecmp("dirs",ap3)) || (!strcasecmp("nodirs", ap3)) ))
        ap3 = NULL;

      /* if we did get matches... ..else don't do any of this stuff */
      if (match_value >= 0) {
          if (!strcasecmp(ap2, "yes")) {
              if (ap3)
                  *uid = atoi(ap3);    /* the uid  */
              if (ap4) {
                  *gid = atoi(ap4);    /* the gid  */
		  *valid = 1;
		}
              if (ap5)
                  sscanf(ap5, "%o", f_mode); /* the mode */
          } else {
              reply(553, "%s: Permission denied. (Upload)", name);
              return(-1);
          }
      } else {
          /*
           * upload defaults to "permitted"
           */
          return(1);
      }

  return(match_value);
}

int
#ifdef __STDC_
del_check(char *name)
#else
del_check(name)
char *name;
#endif
{
  int pdelete = 1;
  struct aclmember *entry = NULL;

  while (getaclentry("delete", &entry) && ARG0 && ARG1 != NULL) {
      if (type_match(ARG1))
          if (*ARG0 == 'n')
              pdelete = 0;
  }
  
/* H* fix: no deletion, period. You put a file here, I get to look at it. */
#ifdef PARANOID
  pdelete = 0;
#endif

  if (!pdelete) {
      reply(553, "%s: Permission denied. (Delete)", name);
      return(0);
  } else {
      return(1);
  }
}

/* The following is from the Debian add-ons. */

#define lbasename(x) (strrchr(x,'/')?1+strrchr(x,'/'):x)

int
#ifdef __STDC__
checknoretrieve (char *name)
#else
checknoretrieve (name)
char *name;
#endif
{
  char cwd[MAXPATHLEN+1], realwd[MAXPATHLEN+1], realname[MAXPATHLEN+1];
  int i;
  struct aclmember *entry = NULL;

  if (name == (char *)NULL || *name == '\0')
    return 0; 

#ifdef HAVE_GETCWD
  (void)getcwd (cwd, MAXPATHLEN);
#else
  (void)getwd (cwd);
#endif

  realpath (cwd, realwd);
  realpath (name, realname);

   while (getaclentry("noretrieve", &entry)) {
        if (ARG0 == (char *)NULL)
            continue;
	for (i = 0; i< MAXARGS && 
	     (entry->arg[i] != (char *)NULL) && (*(entry->arg[i]) !='\0'); i++)
	  if (strcmp (((*(entry->arg[i]) == '/') ? realname : 
			lbasename (realname)), entry->arg[i]) == 0)
	  {
	    reply (550, "%s is marked unretrievable", entry->arg[i]);
	    return 1;
	  }
      }
   return 0;
}





