ftp.nice.ch/pub/next/unix/network/filetransfer/wu-ftpd.2.4.2-beta-13.NIHS.bs.tar.gz#/wu-ftpd-2.4.2-beta-13.NIHS.bs/src/ftpd.c

This is ftpd.c in view mode; [Download] [Up]

/* Copyright (c) 1985, 1988, 1990 Regents of the University of California.
 * 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
 * University of California, Berkeley 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 THE REGENTS 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 THE REGENTS 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
char copyright[] =
"@(#) Copyright (c) 1985, 1988, 1990 Regents of the University of California.\n\
 All rights reserved.\n";
#endif /* not lint */

#ifndef lint
static char sccsid[] = "@(#)$Id: ftpd.c,v 1.29 1997/03/03 09:39:42 sob Exp sob $ based on ftpd.c  5.40 (Berkeley) 7/2/91";
#endif /* not lint */

#define SPT_NONE	0	/* don't use it at all */
#define SPT_REUSEARGV	1	/* cover argv with title information */
#define SPT_BUILTIN	2	/* use libc builtin */
#define SPT_PSTAT	3	/* use pstat(PSTAT_SETCMD, ...) */
#define SPT_PSSTRINGS	4	/* use PS_STRINGS->... */
#define SPT_SYSMIPS	5	/* use sysmips() supported by NEWS-OS 6 */
#define SPT_SCO		6	/* write kernel u. area */

/* FTP server. */
#include "config.h"

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

#ifdef AIX
#include <sys/id.h>
#include <sys/priv.h>
#endif

#ifdef AUX
#include <compat.h>
#endif

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>

#define FTP_NAMES
#include <arpa/ftp.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>

#include <ctype.h>
#include <stdio.h>
#include <signal.h>
#include <pwd.h>
#include <setjmp.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>

/*
 *  Arrange to use either varargs or stdargs
 *
 */

#ifdef __STDC__

#include <stdarg.h>

#define VA_LOCAL_DECL	va_list ap;
#define VA_START(f)	va_start(ap, f)
#define VA_END		va_end(ap)

#else

#include <varargs.h>

#define VA_LOCAL_DECL	va_list ap;
#define VA_START(f)	va_start(ap)
#define VA_END		va_end(ap)

#endif


#ifdef SYSSYSLOG
#include <sys/syslog.h>
#else
#include <syslog.h>
#endif
#include <time.h>
#include "conversions.h"
#include "extensions.h"
#include "pathnames.h"

#ifdef M_UNIX
#include <arpa/nameser.h>
#include <resolv.h>
#endif

#if defined(SVR4) || defined(ISC)
#include <fcntl.h>
#endif

#ifdef HAVE_SYSINFO
#include <sys/systeminfo.h>
#endif

#ifdef SHADOW_PASSWORD
#include <shadow.h>
#endif

#ifdef KERBEROS
#include <sys/types.h>
#include <auth.h>
#include <krb.h>
#endif

#ifdef ULTRIX_AUTH
#include <auth.h>
#include <sys/svcinfo.h>
#endif

#ifndef HAVE_SYMLINK
#define lstat stat
#endif

#ifdef HAVE_DIRENT
#include <dirent.h>
#else
#include <sys/dir.h>
#endif

#if (defined(_BSDI_VERSION) && (_BSDI_VERSION < 199501)) /* before version 2 */
#define LONGOFF_T  /* sizeof(off_t) == sizeof(long) */
#endif

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64  /* may be too big */
#endif

#ifndef TRUE
#define  TRUE   1
#endif

#ifndef FALSE
#define  FALSE  !TRUE
#endif

#if defined(_SCO_DS) && !defined(SIGURG)
#define SIGURG	SIGUSR1
#endif

/* File containing login names NOT to be used on this machine. Commonly used
 * to disallow uucp. */
extern int errno;
extern int pidfd;
#ifdef __STDC__
extern char *ctime(const time_t *);
#ifndef NO_CRYPT_PROTO
extern char *crypt(const char *, const char *);
#endif
extern FILE *ftpd_popen(char *program, char *type, int closestderr),
 *fopen(const char *, const char *),
 *freopen(const char *, const char *, FILE *);
extern int ftpd_pclose(FILE *iop),
  fclose(FILE *);
extern char *getline(),
 *realpath(const char *pathname, char *result);
#else
extern char *ctime();
#ifndef NO_CRYPT_PROTO
#ifdef _M_UNIX
    extern char *crypt(const char *, const char *);
#else
    extern char *crypt();
#endif
#endif
extern FILE *ftpd_popen(), *fopen(),*freopen();
extern int ftpd_pclose(), fclose();
extern char *getline(), *realpath();
#endif
extern char version[];
extern char *home;              /* pointer to home directory for glob */
extern char cbuf[];
extern off_t restart_point;
extern yyerrorcalled;

struct sockaddr_in ctrl_addr;
struct sockaddr_in data_source;
struct sockaddr_in data_dest;
struct sockaddr_in his_addr;
struct sockaddr_in pasv_addr;

#ifdef VIRTUAL
int virtual_mode=0;
char virtual_root[MAXPATHLEN];
char virtual_banner[MAXPATHLEN];
#endif

int data;
jmp_buf errcatch,
  urgcatch;
int logged_in = 0;
struct passwd *pw;
int debug;
int timeout = 900;              /* timeout after 15 minutes of inactivity */
int maxtimeout = 7200;          /* don't allow idle time to be set beyond 2
                                 * hours */

/* previously defaulted to 1, and -l or -L set them to 1, so that there was
   no way to turn them *off*!  Changed so that the manpage reflects common
   sense.  -L is way noisy; -l we'll change to be "just right".  _H*/
int logging = 0;
int log_commands = 0;

#ifdef SECUREOSF
#define SecureWare
#include <prot.h>
#endif

int anonymous = 1;
int guest;
int type;
int form;
int stru;                       /* avoid C keyword */
int mode;
int usedefault = 1;             /* for data transfers */
int pdata = -1;                 /* for passive mode */
int transflag;
off_t file_size;
off_t byte_count;

#if !defined(CMASK) || CMASK == 0
#undef CMASK
#define CMASK 002
#endif
mode_t defumask = CMASK;           /* default umask value */
char tmpline[7];
char hostname[MAXHOSTNAMELEN];
char remotehost[MAXHOSTNAMELEN];
char remoteaddr[MAXHOSTNAMELEN];

/* log failures 	27-apr-93 ehk/bm */
#ifdef LOG_FAILED
#define MAXUSERNAMELEN	32
char the_user[MAXUSERNAMELEN];
#endif

/* Access control and logging passwords */
/* OFF by default.  _H*/
int use_accessfile = 0;
char guestpw[MAXHOSTNAMELEN];
char privatepw[MAXHOSTNAMELEN];
int nameserved = 0;
extern char authuser[];
extern int authenticated;

/* File transfer logging */
int xferlog = 0;
int log_outbound_xfers = 0;
int log_incoming_xfers = 0;
char logfile[MAXPATHLEN];

/* Allow use of lreply(); this is here since some older FTP clients don't
 * support continuation messages.  In violation of the RFCs... */
int dolreplies = 1;

/* Spontaneous reply text.  To be sent along with next reply to user */
char *autospout = NULL;
int autospout_free = 0;

/* allowed on-the-fly file manipulations (compress, tar) */
int mangleopts = 0;

/* number of login failures before attempts are logged and FTP *EXITS* */
int lgi_failure_threshold = 5;

/* Timeout intervals for retrying connections to hosts that don't accept PORT
 * cmds.  This is a kludge, but given the problems with TCP... */
#define SWAITMAX    90          /* wait at most 90 seconds */
#define SWAITINT    5           /* interval between retries */

int swaitmax = SWAITMAX;
int swaitint = SWAITINT;

#ifdef __STDC__
SIGNAL_TYPE lostconn(int sig);
SIGNAL_TYPE randomsig(int sig);
SIGNAL_TYPE myoob(int sig);
FILE *getdatasock(char *mode),
 *dataconn(char *name, off_t size, char *mode);
void setproctitle(const char *fmt, ...);
void reply(int, char *fmt, ...);
void lreply(int, char *fmt, ...);
#else
SIGNAL_TYPE lostconn();
SIGNAL_TYPE randomsig();
SIGNAL_TYPE myoob();
FILE *getdatasock(), *dataconn();
void setproctitle();
void reply();
void lreply();
#endif

#ifdef NEED_SIGFIX
extern sigset_t block_sigmask;  /* defined in sigfix.c */
#endif

char **Argv = NULL;             /* pointer to argument vector */
char *LastArgv = NULL;          /* end of argv */
char proctitle[BUFSIZ];         /* initial part of title */

#ifdef SKEY
#include <skey.h>
int	pwok = 0;
#endif

#ifdef KERBEROS
void init_krb();
void end_krb();
char krb_ticket_name[100];
#endif /* KERBEROS */

#ifdef ULTRIX_AUTH
int ultrix_check_pass(char *passwd, char *xpasswd);
#endif

/* ls program commands and options for lreplies on and off */
char  ls_long[50];
char  ls_short[50];
struct aclmember *entry = NULL;

#ifdef __STDC__
void end_login(void);
void send_data(FILE *, FILE *, off_t);
void dolog(struct sockaddr_in *);
void dologout(int);
void perror_reply(int, char *);
#else
void end_login();
void send_data();
void dolog();
void dologout();
void perror_reply();
#endif


void
#ifdef __STDC__
main(int argc, char **argv, char **envp)
#else
main(argc,argv,envp)
int argc; char **argv; char **envp;
#endif
{
    int addrlen,
      on = 1;
#ifdef IPTOS_LOWDELAY
    int tos;
#endif
    int c;
    extern int optopt;
    extern char *optarg;
    struct hostent *shp;
#ifdef VIRTUAL
    int virtual_len;
    struct sockaddr_in virtual_addr;
    struct sockaddr_in *virtual_ptr;
#endif

#ifdef AUX
    setcompat(COMPAT_POSIX | COMPAT_BSDSETUGID);
#endif

#ifdef FACILITY
    openlog("ftpd", LOG_PID | LOG_NDELAY, FACILITY);
#else
    openlog("ftpd", LOG_PID);
#endif

#ifdef SecureWare
    setluid(1);                         /* make sure there is a valid luid */
    set_auth_parameters(argc,argv);
    setreuid(0, 0);
#endif
#if defined(M_UNIX) && !defined(_M_UNIX)
    res_init();                         /* bug in old (1.1.1) resolver     */
    _res.retrans = 20;                  /* because of fake syslog in 3.2.2 */
    setlogmask(LOG_UPTO(LOG_INFO));
#endif

    addrlen = sizeof(his_addr);
    if (getpeername(0, (struct sockaddr *) &his_addr, &addrlen) < 0) {
        syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
#ifndef DEBUG
        exit(1);
#endif
    }
    addrlen = sizeof(ctrl_addr);
    if (getsockname(0, (struct sockaddr *) &ctrl_addr, &addrlen) < 0) {
        syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
#ifndef DEBUG
        exit(1);
#endif
    }
#ifdef IPTOS_LOWDELAY
    tos = IPTOS_LOWDELAY;
    if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
          syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
#endif

    data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
    debug = 0;

    /* Save start and extent of argv for setproctitle. */
    Argv = argv;
    while (*envp)
        envp++;
    LastArgv = envp[-1] + strlen(envp[-1]);

    while ((c = getopt(argc, argv, ":aAvdlLiot:T:u:")) != -1) {
        switch (c) {

        case 'a':
            use_accessfile = 1;
            break;

        case 'A':
            use_accessfile = 0;
            break;

        case 'v':
            debug = 1;
            break;

        case 'd':
            debug = 1;
            break;

        case 'l':
            logging = 1;
            break;

        case 'L':
            log_commands = 1;
            break;

        case 'i':
            log_incoming_xfers = 1;
            break;

        case 'o':
            log_outbound_xfers = 1;
            break;

        case 't':
            timeout = atoi(optarg);
            if (maxtimeout < timeout)
                maxtimeout = timeout;
            break;

        case 'T':
            maxtimeout = atoi(optarg);
            if (timeout > maxtimeout)
                timeout = maxtimeout;
            break;

        case 'u':
            {
                unsigned int val = 0;

                while (*optarg && *optarg >= '0' && *optarg <= '9')
                    val = val * 8 + *optarg++ - '0';
                if (*optarg || val > 0777)
                    syslog(LOG_ERR, "bad value for -u");
                else
                    defumask = val;
                break;
            }

        case ':':
            syslog(LOG_ERR, "option -%c requires an argument", optopt);
            break;

        default:
            syslog(LOG_ERR, "unknown option -%c ignored", optopt);
            break;
        }
    }
    (void) freopen(_PATH_DEVNULL, "w", stderr);

    /* Checking for random signals ... */
#ifdef NEED_SIGFIX
sigemptyset(&block_sigmask);
#endif
#ifndef SIG_DEBUG
#ifdef SIGHUP
    (void) signal(SIGHUP, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGHUP);
#endif
#endif
#ifdef SIGINT
    (void) signal(SIGINT, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGINT);
#endif
#endif
#ifdef SIGQUIT
    (void) signal(SIGQUIT, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGQUIT);
#endif
#endif
#ifdef SIGILL
    (void) signal(SIGILL, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGILL);
#endif
#endif
#ifdef SIGTRAP
    (void) signal(SIGTRAP, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGTRAP);
#endif
#endif
#ifdef SIGIOT
    (void) signal(SIGIOT, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGIOT);
#endif
#endif
#ifdef SIGEMT
    (void) signal(SIGEMT, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGEMT);
#endif
#endif
#ifdef SIGFPE
    (void) signal(SIGFPE, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGFPE);
#endif
#endif
#ifdef SIGKILL
    (void) signal(SIGKILL, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGKILL);
#endif
#endif
#ifdef SIGBUS
    (void) signal(SIGBUS, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGBUS);
#endif
#endif
#ifdef SIGSEGV
    (void) signal(SIGSEGV, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGSEGV);
#endif
#endif
#ifdef SIGSYS
    (void) signal(SIGSYS, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGSYS);
#endif
#endif
#ifdef SIGALRM
    (void) signal(SIGALRM, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGALRM);
#endif
#endif
#ifdef SIGSTOP
    (void) signal(SIGSTOP, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGSTOP);
#endif
#endif
#ifdef SIGTSTP
    (void) signal(SIGTSTP, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGTSTP);
#endif
#endif
#ifdef SIGTTIN
    (void) signal(SIGTTIN, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGTTIN);
#endif
#endif
#ifdef SIGTTOU
    (void) signal(SIGTTOU, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGTTOU);
#endif
#endif
#ifdef SIGIO
    (void) signal(SIGIO, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGIO);
#endif
#endif
#ifdef SIGXCPU
    (void) signal(SIGXCPU, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGXCPU);
#endif
#endif
#ifdef SIGXFSZ
    (void) signal(SIGXFSZ, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGXFSZ);
#endif
#endif
#ifdef SIGWINCH
    (void) signal(SIGWINCH, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGWINCH);
#endif
#endif
#ifdef SIGVTALRM
    (void) signal(SIGVTALRM, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGVTALRM);
#endif
#endif
#ifdef SIGPROF
    (void) signal(SIGPROF, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGPROF);
#endif
#endif
#ifdef SIGUSR1
    (void) signal(SIGUSR1, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGUSR1);
#endif
#endif
#ifdef SIGUSR2
    (void) signal(SIGUSR2, randomsig);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGUSR2);
#endif
#endif

#ifdef SIGPIPE
    (void) signal(SIGPIPE, lostconn);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGPIPE);
#endif
#endif
#ifdef SIGCHLD
    (void) signal(SIGCHLD, SIG_IGN);
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGCHLD);
#endif
#endif

#ifdef SIGURG
    if ((int) signal(SIGURG, myoob) < 0)
        syslog(LOG_ERR, "signal: %m");
#ifdef NEED_SIGFIX
    sigaddset(&block_sigmask, SIGURG);
#endif
#endif
#endif /* SIG_DEBUG */
    /* Try to handle urgent data inline */
#ifdef SO_OOBINLINE
    if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(int)) < 0)
        syslog(LOG_ERR, "setsockopt (SO_OOBINLINE): %m");
#endif

#ifdef  F_SETOWN
    if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
        syslog(LOG_ERR, "fcntl F_SETOWN: %m");
#elif defined(SIOCSPGRP)
    {
        int pid;
        pid = getpid();
        if (ioctl(fileno(stdin), SIOCSPGRP, &pid) == -1)
            syslog(LOG_ERR, "ioctl SIOCSPGRP: %m");
    }
#endif
    dolog(&his_addr);
    /* Set up default state */
    data = -1;
    type = TYPE_A;
    form = FORM_N;
    stru = STRU_F;
    mode = MODE_S;
    tmpline[0] = '\0';
    yyerrorcalled = 0;

#ifdef HAVE_SYSINFO
    sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
#else
    (void) gethostname(hostname, sizeof (hostname));
#endif
/* set the FQDN here */
    shp = gethostbyname(hostname);
    if (shp != NULL)
      (void) strncpy(hostname, shp->h_name, sizeof(hostname));

    access_init();
    authenticate();
    conv_init();

#ifdef VIRTUAL
    virtual_len = sizeof(virtual_addr);
    if (getsockname(0, (struct sockaddr *) &virtual_addr, &virtual_len) == 0) {
        virtual_ptr = (struct sockaddr_in *) &virtual_addr;
        entry = (struct aclmember *) NULL;
        while (getaclentry("virtual", &entry)) {
            if (!ARG0 || !ARG1 || !ARG2)
                continue;
            if (!strcmp(ARG0, inet_ntoa(virtual_ptr->sin_addr))) {
                if(!strcmp(ARG1, "root")) {
		    syslog(LOG_NOTICE, "VirtualFTP Connect to: %s",
                           inet_ntoa(virtual_ptr->sin_addr));
                    virtual_mode = 1;
                    strncpy(virtual_root, ARG2, MAXPATHLEN);
		    /* reset hostname to this virtual name */
		    shp = gethostbyaddr((char *) &virtual_ptr->sin_addr,
                                sizeof (struct in_addr), AF_INET);
		    if (shp != NULL)
		      (void) strncpy(hostname, shp->h_name, sizeof(hostname));
                }
                if(!strcmp(ARG1, "banner")) 
                    strncpy(virtual_banner, ARG2, MAXPATHLEN);
                if(!strcmp(ARG1, "logfile")) 
                    strncpy(logfile, ARG2, MAXPATHLEN);
            }
        }
    }
    if (!virtual_mode || logfile[0] == '\0')
#endif
    strcpy(logfile, _PATH_XFERLOG);

    if (is_shutdown(1, 1) != 0) {
        syslog(LOG_INFO, "connection refused (server shut down) from %s [%s]",
               remotehost, remoteaddr);
        reply(500, "%s FTP server shut down -- please try again later.",
              hostname);
        exit(0);
    }

    show_banner(220);

    entry = (struct aclmember *) NULL;
    if (getaclentry("lslong", &entry) && ARG0 && (int)strlen(ARG0) > 0) {
          strcpy(ls_long,ARG0);
      if (ARG1 && strlen(ARG1)) {
             strcat(ls_long," ");
         strcat(ls_long,ARG1);
          }
    } else {
#if defined(SVR4) || defined(ISC)
#ifndef AIX
          strcpy(ls_long,"/bin/ls -la");
#else
          strcpy(ls_long,"/bin/ls -lA");
#endif
#else
          strcpy(ls_long,"/bin/ls -lgA");
#endif
    }
    strcat(ls_long," %s");

    entry = (struct aclmember *) NULL;
    if (getaclentry("lsshort", &entry) && ARG0 && (int)strlen(ARG0) > 0) {
          strcpy(ls_short,ARG0);
      if (ARG1 && strlen(ARG1)) {
             strcat(ls_short," ");
             strcat(ls_short,ARG1);
      }
    } else {
#if defined(SVR4) || defined(ISC)
#ifndef AIX
          strcpy(ls_short,"/bin/ls -la");
#else
          strcpy(ls_short,"/bin/ls -lA");
#endif
#else
          strcpy(ls_short,"/bin/ls -lgA");
#endif
    }
    strcat(ls_short," %s");

    reply(220, "%s FTP server (%s) ready.", hostname, version);
    (void) setjmp(errcatch);

    for (;;)
        (void) yyparse();
    /* NOTREACHED */
}

SIGNAL_TYPE
#ifdef __STDC__
randomsig(int sig)
#else
randomsig(sig)
int sig;
#endif
{
#ifdef HAVE_SIGLIST
    syslog(LOG_ERR, "exiting on signal %d: %s", sig, sys_siglist[sig] );
#else
    syslog(LOG_ERR, "exiting on signal %d", sig);
#endif
    chdir("/");
    signal(SIGIOT, SIG_DFL);
    signal(SIGILL, SIG_DFL);
    exit (1);
    /* dologout(-1); *//* NOTREACHED */
}

SIGNAL_TYPE
#ifdef __STDC__
lostconn(int sig)
#else
lostconn(sig)
int sig;
#endif
{
    if (debug)
        syslog(LOG_DEBUG, "lost connection to %s [%s]", remotehost, remoteaddr);
    dologout(-1);
}

static char ttyline[20];

/* Helper function for sgetpwnam(). */
char *
#ifdef __STDC__
sgetsave(char *s)
#else
sgetsave(s)
char *s;
#endif
{
    char *new;
    
    new = (char *) malloc(strlen(s) + 1);

    if (new == NULL) {
        perror_reply(421, "Local resource failure: malloc");
        dologout(1);
        /* NOTREACHED */
    }
    (void) strcpy(new, s);
    return (new);
}

/* Save the result of a getpwnam.  Used for USER command, since the data
 * returned must not be clobbered by any other command (e.g., globbing). */
struct passwd *
#ifdef __STDC__
sgetpwnam(char *name)
#else
sgetpwnam(name)
char *name;
#endif
{
    static struct passwd save;
    register struct passwd *p;
#ifdef M_UNIX
    struct passwd *ret = (struct passwd *) NULL;
#endif
#ifdef __STDC__
    char *sgetsave(char *s);
#else
    char *sgetsave();
#endif
#ifdef KERBEROS
    register struct authorization *q;
#endif /* KERBEROS */

#ifdef SecureWare
    struct pr_passwd *pr;
#endif

#ifdef KERBEROS
    init_krb();
    q = getauthuid(p->pw_uid);
    end_krb();
#endif /* KERBEROS */

#ifdef M_UNIX
# ifdef SecureWare
    if ((pr = getprpwnam(name)) == NULL)
        goto DONE;
# endif /* SecureWare */
    if ((p = getpwnam(name)) == NULL)
        goto DONE;
#else   /* M_UNIX */
# ifdef SecureWare
    if ((pr = getprpwnam(name)) == NULL)
        return((struct passwd *) pr);
# endif /* SecureWare */
    if ((p = getpwnam(name)) == NULL)
        return (p);
#endif  /* M_UNIX */

    if (save.pw_name)   free(save.pw_name);
    if (save.pw_gecos)  free(save.pw_gecos);
    if (save.pw_dir)    free(save.pw_dir);
    if (save.pw_shell)  free(save.pw_shell);

    save = *p;

    save.pw_name = sgetsave(p->pw_name);

#ifdef KERBEROS
    save.pw_passwd = sgetsave(q->a_password);
#elif defined(SecureWare)
    if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt)
       save.pw_passwd = sgetsave(pr->ufld.fd_encrypt);
    else
       save.pw_passwd = sgetsave("");
#else
    save.pw_passwd = sgetsave(p->pw_passwd);
#endif
#ifdef SHADOW_PASSWORD
        if (p) {
           struct spwd *spw;
	   setspent();
           if ((spw = getspnam(p->pw_name)) != NULL) {
               /* TODO: check if password has expired etc.  */
	       free(save.pw_passwd);
               save.pw_passwd = sgetsave(spw->sp_pwdp);
           }
/* Shadow passwords are optional on Linux.  --marekm */
#ifndef LINUX 
           else{
	     free(save.pw_passwd);
	     save.pw_passwd = sgetsave("");
	   }
#endif
/* marekm's fix for linux proc file system shadow passwd exposure problem */
	   endspent();		
        }
#endif
    save.pw_gecos = sgetsave(p->pw_gecos);
    save.pw_dir = sgetsave(p->pw_dir);
    save.pw_shell = sgetsave(p->pw_shell);
#ifdef M_UNIX
    ret = &save;
DONE:
    endpwent();
#endif
#ifdef SecureWare
    endprpwent();
#endif
#ifdef M_UNIX
    return(ret);
#else
    return(&save);
#endif
}
#ifdef SKEY
/*
 * From Wietse Venema, Eindhoven University of Technology. 
 */
/* skey_challenge - additional password prompt stuff */
#ifdef __STDC__
char   *skey_challenge(char *name, struct passwd *pwd, int pwok)
#else
char   *skey_challenge(name, pwd, pwok)
char   *name;
struct passwd *pwd;
int    pwok;
#endif
{
    static char buf[128];
    char sbuf[40];
    struct skey skey;

    /* Display s/key challenge where appropriate. */

    if (pwd == NULL || skeychallenge(&skey, pwd->pw_name, sbuf))
	sprintf(buf, "Password required for %s.", name);
    else
	sprintf(buf, "[%s] %s for %s.", sbuf,
		pwok ? "allowed" : "required", name);
    return (buf);
}
#endif
int login_attempts;             /* number of failed login attempts */
int askpasswd;                  /* had user command, ask for passwd */

/* USER command. Sets global passwd pointer pw if named account exists and is
 * acceptable; sets askpasswd if a PASS command is expected.  If logged in
 * previously, need to reset state.  If name is "ftp" or "anonymous", the
 * name is not in _PATH_FTPUSERS, and ftp account exists, set anonymous and
 * pw, then just return.  If account doesn't exist, ask for passwd anyway.
 * Otherwise, check user requesting login privileges.  Disallow anyone who
 * does not have a standard shell as returned by getusershell().  Disallow
 * anyone mentioned in the file _PATH_FTPUSERS to allow people such as root
 * and uucp to be avoided. */

void
#ifdef __STDC__
user(char *name)
#else
user(name)
char *name;
#endif
{
    register char *cp;
    char *shell;
    char *getusershell();
    int   why = 0;

/* H* fix: if we're logged in at all, we can't log in again. */
    if (logged_in) {
	reply(530, "Already logged in.");
	return;
    }

#ifdef HOST_ACCESS                     /* 19-Mar-93    BM              */
    if (!rhost_ok(name, remotehost, remoteaddr))
    {
            reply(530, "User %s access denied.", name);
            syslog(LOG_NOTICE,
                    "FTP LOGIN REFUSED (name in %s) FROM %s [%s], %s",
                     _PATH_FTPHOSTS, remotehost, remoteaddr, name);
            return;
    }
#endif

#ifdef LOG_FAILED                       /* 06-Nov-92    EHK             */
    strncpy(the_user, name, MAXUSERNAMELEN - 1);
#endif

    if (logged_in) {			/* Now a no-op.  _H*/
        if (anonymous || guest) {
            reply(530, "Can't change user from guest login.");
            return;
        }
        end_login();
    }

    anonymous = 0;
    acl_remove();

    if (!strcasecmp(name, "ftp") || !strcasecmp(name, "anonymous")) {
      struct aclmember *entry = NULL;
      int machineok=1;
      char guestservername[MAXHOSTNAMELEN];
      guestservername[0]='\0';

      if (checkuser("ftp") || checkuser("anonymous")) {
          reply(530, "User %s access denied.", name);
          syslog(LOG_NOTICE,
	       "FTP LOGIN REFUSED (ftp in /etc/ftpusers) FROM %s [%s], %s",
	       remotehost, remoteaddr, name);
          return;
          
        /*
        ** Algorithm used:
        ** - if no "guestserver" directive is present,
        **     anonymous access is allowed, for backward compatibility.
        ** - if a "guestserver" directive is present,
        **     anonymous access is restricted to the machines listed,
        **     usually the machine whose CNAME on the current domain
        **     is "ftp"...
        **
        ** the format of the "guestserver" line is
        ** guestserver [<machine1> [<machineN>]]
        ** that is, "guestserver" will forbid anonymous access on all machines
        ** while "guestserver ftp inf" will allow anonymous access on
        ** the two machines whose CNAMES are "ftp.enst.fr" and "inf.enst.fr".
        **
        ** if anonymous access is denied on the current machine,
        ** the user will be asked to use the first machine listed (if any)
        ** on the "guestserver" line instead:
        ** 530- Guest login not allowed on this machine,
        **      connect to ftp.enst.fr instead.
        **
        ** -- <Nicolas.Pioch@enst.fr>
        */
      } else if (getaclentry("guestserver", &entry)
                 && ARG0 && (int)strlen(ARG0) > 0) {
        struct hostent *tmphostent;

        /*
        ** if a "guestserver" line is present,
        ** default is not to allow guest logins
        */
        machineok=0;

        if (hostname[0]
            && ((tmphostent=gethostbyname(hostname)))) {

          /*
          ** hostname is the only first part of the FQDN
          ** this may or may not correspond to the h_name value
          ** (machines with more than one IP#, CNAMEs...)
          ** -> need to fix that, calling gethostbyname on hostname
          **
          ** WARNING!
          ** for SunOS 4.x, you need to have a working resolver in the libc
          ** for CNAMES to work properly.
          ** If you don't, add "-lresolv" to the libraries before compiling!
          */
          char dns_localhost[MAXHOSTNAMELEN];
          int machinecount;

          strncpy(dns_localhost,
                  tmphostent->h_name,
                  sizeof(dns_localhost));
          dns_localhost[sizeof(dns_localhost)-1]='\0';

          for (machinecount=0;
               entry->arg[machinecount] && (entry->arg[machinecount])[0];
               machinecount++) {

            if ((tmphostent=gethostbyname(entry->arg[machinecount]))) {
              /*
              ** remember the name of the first machine for redirection
              */

              if ((!machinecount) && tmphostent->h_name) {
                strncpy(guestservername, entry->arg[machinecount],
                        sizeof(guestservername));
                guestservername[sizeof(guestservername)-1]='\0';
              }

              if (!strcasecmp(tmphostent->h_name, dns_localhost)) {
                machineok++;
                break;
              }
            }
          }
        }
      }
      if (!machineok) {
        if (guestservername[0])
          reply(530,
             "Guest login not allowed on this machine, connect to %s instead.",
                guestservername);
        else
          reply(530,
                "Guest login not allowed on this machine.");
        syslog(LOG_NOTICE,
               "FTP LOGIN REFUSED (localhost not in guestservers) FROM %s [%s], %s",
               remotehost, remoteaddr, name);
        /* End of the big patch -- Nap */

        } else if ((pw = sgetpwnam("ftp")) != NULL) {
            anonymous = 1;      /* for the access_ok call */
            if ((why = access_ok(530)) == 1) {
                askpasswd = 1;
/* H* fix: obey use_accessfile a little better.  This way, things set on the
   command line [like xferlog stuff] don't get stupidly overridden.
   XXX: all these checks maybe should be in acl.c and access.c */
		if (use_accessfile)
                    acl_setfunctions();
                reply(331, "Guest login ok, send your complete e-mail address as password.");
            } else if (why == 0) {
                reply(530, "User %s access denied..", name);
                syslog(LOG_NOTICE,
                       "FTP LOGIN REFUSED (access denied) FROM %s [%s], %s",
                       remotehost, remoteaddr, name);
                anonymous = 0;
            } else {
                reply(530, "User %s access denied.", name);
                syslog(LOG_NOTICE,
                       "FTP LOGIN REFUSED (access denied) FROM %s [%s], %s",
                       remotehost, remoteaddr, name);
                dologout(0);
            }
        } else {
            reply(530, "User %s unknown.", name);
            syslog(LOG_NOTICE,
              "FTP LOGIN REFUSED (ftp not in /etc/passwd) FROM %s [%s], %s",
                   remotehost, remoteaddr, name);
        }
        return;
    }
#ifdef ANON_ONLY
/* H* fix: define the above to completely DISABLE logins by real users,
   despite ftpusers, shells, or any of that rot.  You can always hang your
   "real" server off some other port, and access-control it. */

    else {  /* "ftp" or "anon" -- MARK your conditionals, okay?! */
      reply(530, "User %s unknown.", name);
      syslog (LOG_NOTICE,
	"FTP LOGIN REFUSED (not anonymous) FROM %s [%s], %s",
	  remotehost, remoteaddr, name);
      return;
    }
/* fall here if username okay in any case */
#endif /* ANON_ONLY */

    if ((pw = sgetpwnam(name)) != NULL) {
        if ((shell = pw->pw_shell) == NULL || *shell == 0)
            shell = _PATH_BSHELL;
        while ((cp = getusershell()) != NULL)
            if (strcmp(cp, shell) == 0)
                break;
        endusershell();
        if (cp == NULL || checkuser(name)) {
            reply(530, "User %s access denied...(bad shell)", name);
/*            if (logging)	-- inconsistent, removed.  _H*/
                syslog(LOG_NOTICE,
                       "FTP LOGIN REFUSED (bad shell) FROM %s [%s], %s",
                       remotehost, remoteaddr, name);
            pw = (struct passwd *) NULL;
            return;
        }
        /* if user is a member of any of the guestgroups, cause a chroot() */
        /* after they log in successfully                                  */
	if (use_accessfile)		/* see above.  _H*/
            guest = acl_guestgroup(pw);
    }
    if (access_ok(530) < 1) {
        reply(530, "User %s access denied....", name);
        syslog(LOG_NOTICE, "FTP LOGIN REFUSED (access denied) FROM %s [%s], %s",
               remotehost, remoteaddr, name);
        return;
    } else
	if (use_accessfile)		/* see above.  _H*/
            acl_setfunctions();

#ifdef SKEY
    pwok = skeyaccess(name, NULL, remotehost, remoteaddr);
    reply(331, "%s", skey_challenge(name, pw, pwok));
#else
    reply(331, "Password required for %s.", name);
#endif
    askpasswd = 1;
    /* Delay before reading passwd after first failed attempt to slow down
     * passwd-guessing programs. */
    if (login_attempts)
        sleep((unsigned) login_attempts);
    return;
}

/* Check if a user is in the file _PATH_FTPUSERS */

int
#ifdef __STDC__
checkuser(char *name)
#else
checkuser(name)
char *name;
#endif
{
    register FILE *fd;
    register char *p;
    char line[BUFSIZ];

    if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
        while (fgets(line, sizeof(line), fd) != NULL)
            if ((p = strchr(line, '\n')) != NULL) {
                *p = '\0';
                if (line[0] == '#')
                    continue;
                if (strcmp(line, name) == 0) {
                    (void) fclose(fd);
                    return (1);
                }
            }
        (void) fclose(fd);
    }
    return (0);
}

/* Terminate login as previous user, if any, resetting state; used when USER
 * command is given or login fails. */

void
#ifdef __STDC__
end_login(void)
#else
end_login()
#endif
{

    delay_signaling(); /* we can't allow any signals while euid==0: kinch */
    (void) seteuid((uid_t) 0);
    if (logged_in)
        logwtmp(ttyline, "", "");
    pw = NULL;
    logged_in = 0;
    anonymous = 0;
    guest = 0;
}

int
#ifdef __STDC__
validate_eaddr(char *eaddr)
#else
validate_eaddr(eaddr)
char *eaddr;
#endif
{
    int i,
      host,
      state;

    for (i = host = state = 0; eaddr[i] != '\0'; i++) {
        switch (eaddr[i]) {
        case '.':
            if (!host)
                return 0;
            if (state == 2)
                state = 3;
            host = 0;
            break;
        case '@':
            if (!host || state > 1 || !strncasecmp("ftp", eaddr + i - host, host))
                return 0;
            state = 2;
            host = 0;
            break;
        case '!':
        case '%':
            if (!host || state > 1)
                return 0;
            state = 1;
            host = 0;
            break;
        case '-':
            break;
        default:
            host++;
        }
    }
    if (((state == 3) && host > 1) || ((state == 2) && !host) ||
        ((state == 1) && host > 1))
        return 1;
    else
        return 0;
}

void
#ifdef __STDC__
pass(char *passwd)
#else
pass(passwd)
char *passwd;
#endif
{
    char *xpasswd,
     *salt;

#ifdef ULTRIX_AUTH
    int numfails;
#endif /* ULTRIX_AUTH */
    if (logged_in || askpasswd == 0) {
        reply(503, "Login with USER first.");
        return;
    }
    askpasswd = 0;

    /* Disable lreply() if the first character of the password is '-' since
     * some hosts don't understand continuation messages and hang... */

    if (*passwd == '-')
        dolreplies = 0;
    else
        dolreplies = 1;
/* ******** REGULAR/GUEST USER PASSWORD PROCESSING ********** */
    if (!anonymous) {    /* "ftp" is only account allowed no password */
        if (*passwd == '-')
            passwd++;
        *guestpw = '\0';
        if (pw == NULL)
            salt = "xx"; /* XXX */
        else
            salt = pw->pw_passwd;
#ifdef SECUREOSF
        xpasswd = bigcrypt(passwd, salt);
#else
#ifdef KERBEROS
        xpasswd = crypt16(passwd, salt);
#else
#ifdef SKEY
	xpasswd = skey_crypt(passwd, salt, pw, pwok);
	pwok = 0;
#else
        xpasswd = crypt(passwd, salt);
#endif
#endif
#endif
#ifdef ULTRIX_AUTH
        if ((numfails = ultrix_check_pass(passwd, xpasswd)) < 0) {
#else
        /* The strcmp does not catch null passwords! */
      if (pw == NULL || *pw->pw_passwd == '\0' ||
            strcmp(xpasswd, pw->pw_passwd)) {
#endif
            reply(530, "Login incorrect.");

#ifdef LOG_FAILED                       /* 27-Apr-93    EHK/BM             */
/* H* add-on: yell about attempts to use the trojan.  This may alarm you
   if you're "stringsing" the binary and you see "NULL" pop out in just
   about the same place as it would have in 2.2c! */
	    if (! strcmp (passwd, "NULL"))
		syslog(LOG_NOTICE, "REFUSED \"NULL\" from %s [%s], %s",
			remotehost, remoteaddr, the_user);
	    else
            syslog(LOG_INFO, "failed login from %s [%s], %s",
                              remotehost, remoteaddr, the_user);
#endif
            acl_remove();

            pw = NULL;
            if (++login_attempts >= lgi_failure_threshold) {
                syslog(LOG_NOTICE, "repeated login failures from %s [%s]",
                       remotehost, remoteaddr);
                exit(0);
            }
            return;
        }
/* ANONYMOUS USER PROCESSING STARTS HERE */
    } else { 
        char *pwin,
         *pwout = guestpw;
        struct aclmember *entry = NULL;
        int valid;

        if (getaclentry("passwd-check", &entry) &&
            ARG0 && strcasecmp(ARG0, "none")) {

            if (!strcasecmp(ARG0, "rfc822"))
                valid = validate_eaddr(passwd);
            else if (!strcasecmp(ARG0, "trivial"))
                valid = (strchr(passwd, '@') == NULL) ? 0 : 1;
            else
                valid = 1;

            if (!valid && ARG1 && !strcasecmp(ARG1, "enforce")) {
                lreply(530, "The response '%s' is not valid", passwd);
                lreply(530, "Please use your e-mail address as your password");
                lreply(530, "   for example: %s@%s or %s@",
                       authenticated ? authuser : "joe", remotehost,
                       authenticated ? authuser : "joe");
                lreply(530, "[%s will be added if password ends with @]",
                       remotehost);
                reply(530, "Login incorrect.");
		acl_remove();	
                if (++login_attempts >= lgi_failure_threshold) {
                    syslog(LOG_NOTICE, "repeated login failures from %s [%s]",
                           remotehost, remoteaddr);
                    exit(0);
                }
                return;
            } else if (!valid) {
                lreply(230, "The response '%s' is not valid", passwd);
                lreply(230,
                "Next time please use your e-mail address as your password");
                lreply(230, "        for example: %s@%s",
                       authenticated ? authuser : "joe", remotehost);
            }
        }
        if (!*passwd) {
            strcpy(guestpw, "[none_given]");
        } else {
            int cnt = sizeof(guestpw) - 2;

            for (pwin = passwd; *pwin && cnt--; pwin++)
                if (!isgraph(*pwin))
                    *pwout++ = '_';
                else
                    *pwout++ = *pwin;
        }
    }

    /* if logging is enabled, open logfile before chroot or set group ID */
    if (log_outbound_xfers || log_incoming_xfers) {
        xferlog = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0660);
        if (xferlog < 0) {
            syslog(LOG_ERR, "cannot open logfile %s: %s", logfile,
                   strerror(errno));
            xferlog = 0;
        }
    }

#ifdef DEBUG
/* I had a lot of trouble getting xferlog working, because of two factors:
   acl_setfunctions making stupid assumptions, and sprintf LOSING.  _H*/
/* 
 * Actually, sprintf was not losing, but the rules changed... next release
 * this will be fixed the correct way, but right now, it works well enough
 * -- sob 
 */
      syslog (LOG_INFO, "-i %d,-o %d,xferlog %s: %d", 
	log_incoming_xfers, log_outbound_xfers, logfile, xferlog);
#endif
    enable_signaling(); /* we can allow signals once again: kinch */
    /* if autogroup command applies to user's class change pw->pw_gid */
    if (anonymous && use_accessfile)	/* see above.  _H*/
        (void) acl_autogroup(pw);
/* END AUTHENTICATION */
    login_attempts = 0;         /* this time successful */
/* SET GROUP ID STARTS HERE */
#ifndef AIX
    (void) setegid((gid_t) pw->pw_gid);
#else
    (void) setgid((gid_t)pw->pw_gid);
#endif
     (void) initgroups(pw->pw_name, pw->pw_gid);
#ifdef DEBUG
      syslog (LOG_DEBUG, "initgroups has been called");
#endif
/* WTMP PROCESSING STARTS HERE */
    /* open wtmp before chroot */
#if (defined(BSD) && (BSD >= 199103))
    (void) sprintf(ttyline, "ftp%ld", getpid());
#else
    (void) sprintf(ttyline, "ftpd%d", getpid());
#endif
#ifdef DEBUG
      syslog (LOG_DEBUG, "about to call wtmp");
#endif
    logwtmp(ttyline, pw->pw_name, remotehost);
    logged_in = 1;

    expand_id();

    if (anonymous || guest) {
        /* We MUST do a chdir() after the chroot. Otherwise the old current
         * directory will be accessible as "." outside the new root! */
#ifdef VIRTUAL
        if (virtual_mode && !guest) {
            if (pw->pw_dir)
                free(pw->pw_dir);
            pw->pw_dir = sgetsave(virtual_root);
        }
#endif 
        if (anonymous) {
            if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
                reply(550, "Can't set guest privileges.");
                goto bad;
            }
        } else if (guest) {
            char *sp;

            /* determine root and home directory */

            if ((sp = strstr(pw->pw_dir, "/./")) == NULL) {
                if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
                    reply(550, "Can't set guest privileges.");
                    goto bad;
                }
            } else {
                *sp++ = '\0';

                if (chroot(pw->pw_dir) < 0 || chdir(++sp) < 0) {
                    reply(550, "Can't set guest privileges.");
                    goto bad;
                }
            }
        }
    }
#ifdef AIX
    {
       /* AIX 3 lossage.  Don't ask.  It's undocumented.  */
       priv_t priv;

       priv.pv_priv[0] = 0;
       priv.pv_priv[1] = 0;
/*       setgroups(NULL, NULL);*/
       if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
                   &priv, sizeof(priv_t)) < 0 ||
           setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 ||
           seteuid((uid_t)pw->pw_uid) < 0) {
               reply(550, "Can't set uid (AIX3).");
               goto bad;
       }
    }
#ifdef UID_DEBUG
    lreply(230, "ruid=%d, euid=%d, suid=%d, luid=%d", getuidx(ID_REAL),
         getuidx(ID_EFFECTIVE), getuidx(ID_SAVED), getuidx(ID_LOGIN));
    lreply(230, "rgid=%d, egid=%d, sgid=%d, lgid=%d", getgidx(ID_REAL),
         getgidx(ID_EFFECTIVE), getgidx(ID_SAVED), getgidx(ID_LOGIN));
#endif
#else
#ifdef HAVE_SETREUID
    if (setreuid(-1, (uid_t) pw->pw_uid) < 0) {
#else
    if (seteuid((uid_t) pw->pw_uid) < 0) {
#endif
        reply(550, "Can't set uid.");
        goto bad;
    }
#endif
     if (!anonymous && !guest) {
        if (chdir(pw->pw_dir) < 0) {
            if (chdir("/") < 0) {
                reply(530, "User %s: can't change directory to %s.",
                      pw->pw_name, pw->pw_dir);
                goto bad;
            } else
                lreply(230, "No directory! Logging in with home=/");
	  }
      }


    /* following two lines were inside the next scope... */

    show_message(230, LOG_IN);
    show_readme(230, LOG_IN);

#ifdef ULTRIX_AUTH
    if (!anonymous && numfails > 0) {
        lreply(230,
            "There have been %d unsuccessful login attempts on your account",
            numfails);
    }
#endif /* ULTRIX_AUTH */    

    if (anonymous) {
        (void) is_shutdown(0, 0);  /* display any shutdown messages now */

        reply(230, "Guest login ok, access restrictions apply.");
        sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
                    (int) (sizeof(proctitle) - sizeof(remotehost) -
                    sizeof(": anonymous/")), passwd);
        setproctitle("%s", proctitle);
        if (logging)
            syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s [%s], %s",
                   remotehost, remoteaddr, passwd);
    } else {
        reply(230, "User %s logged in.%s", pw->pw_name, guest ?
              "  Access restrictions apply." : "");
        sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
        setproctitle(proctitle);
        if (logging)
            syslog(LOG_INFO, "FTP LOGIN FROM %s [%s], %s",
                   remotehost, remoteaddr, pw->pw_name);
/* H* mod: if non-anonymous user, copy it to "authuser" so everyone can
   see it, since whoever he was @foreign-host is now largely irrelevant. */
	strcpy (authuser, pw->pw_name);
    } /* anonymous */
    home = pw->pw_dir;          /* home dir for globbing */
    (void) umask(defumask);
    return;
  bad:
    /* Forget all about it... */
    if (xferlog)
        close(xferlog);
    xferlog = 0;
    end_login();
    return;
}

char *
#ifdef __STDC__
opt_string(int options)
#else
opt_string(options)
int options;
#endif
{
    static char buf[100];
    char *ptr = buf;

    if ((options & O_COMPRESS) != 0)	/* debian fixes: NULL -> 0 */
        *ptr++ = 'C';
    if ((options & O_TAR) != 0)
        *ptr++ = 'T';
    if ((options & O_UNCOMPRESS) != 0)
        *ptr++ = 'U';
    if (options == 0)
        *ptr++ = '_';
    *ptr++ = '\0';
    return (buf);
}

void
#ifdef __STDC__
retrieve(char *cmd, char *name)
#else
retrieve(cmd,name)
char *cmd;
char *name;
#endif
{    FILE *fin,
     *dout;
    struct stat st,
      junk;
    int (*closefunc) () = NULL;
    int options = 0;
    time_t start_time = time(NULL);
    char *logname;
    char namebuf[MAXPATHLEN];
    char fnbuf[MAXPATHLEN];
    struct convert *cptr;
#ifdef __STDC__
    extern int checknoretrieve (char *);
#else
    extern int checknoretrieve ();
#endif
    int stat_ret;

    if (cmd == NULL && (stat_ret = stat(name, &st)) == 0)
        /* there isn't a command and the file exists */
	if (use_accessfile && checknoretrieve(name))	/* see above.  _H*/
	    return;
    logname = (char *)NULL;
    if (cmd == NULL && stat_ret != 0) { /* file does not exist */
         char *ptr;

        cptr = cvtptr;                 /* get ready to try conversions */

        if (cptr == NULL) {
            reply(550, "%s: No such file OR directory.", name);
            return;
        }

        do {
            if (!(mangleopts & O_COMPRESS) && (cptr->options & O_COMPRESS))
                continue;
            if (!(mangleopts & O_UNCOMPRESS) && (cptr->options & O_UNCOMPRESS))
                continue;
            if (!(mangleopts & O_TAR) && (cptr->options & O_TAR))
                continue;

            if ( (cptr->stripfix) && (cptr->postfix) ) {
                int pfxlen = strlen(cptr->postfix);
		int sfxlen = strlen(cptr->stripfix);
                int namelen = strlen(name);
                (void) strcpy(fnbuf, name);

                if (namelen <= pfxlen)
                    continue;
		if ((namelen - pfxlen + sfxlen) >= sizeof(fnbuf))
		    continue;

		if (strcmp(fnbuf + namelen - pfxlen, cptr->postfix))
		    continue;
                *(fnbuf + namelen - pfxlen) = '\0';
                (void) strcat(fnbuf, cptr->stripfix);
                if (stat(fnbuf, &st) != 0) 
                    continue;
            } else if (cptr->postfix) {
                int pfxlen = strlen(cptr->postfix);
                int namelen = strlen(name);

                if (namelen <= pfxlen)
                    continue;
                (void) strcpy(fnbuf, name);
                if (strcmp(fnbuf + namelen - pfxlen, cptr->postfix))
                    continue;
                *(fnbuf + namelen - pfxlen) = (char) NULL;
                if (stat(fnbuf, &st) != 0)
                    continue;
            } else if (cptr->stripfix) {
                (void) strcpy(fnbuf, name);
                (void) strcat(fnbuf, cptr->stripfix);
                if (stat(fnbuf, &st) != 0)
                    continue;
            } else {
                reply(550, "%s: No such file OR directory.", name);
                return;
            }

            if (S_ISDIR(st.st_mode)) {
                if (!(cptr->types & T_DIR)) {
                    reply(550, "Cannot %s directories.", cptr->name);
                    return;
                }
                if (cptr->options & O_TAR) {
                    strcpy(namebuf, fnbuf);
                    strcat(namebuf, "/.notar");
                    if (stat(namebuf, &junk) == 0) {
                        reply(550, "Sorry, you may not TAR that directory.");
                        return;
                    }
                }
            }
/* XXX: checknoretrieve() test is weak in that if I can't get /etc/passwd
   but I can tar /etc or /, I still win.  Be careful out there... _H*
   but you could put .notar in / and /etc and stop that ! */
	    if (use_accessfile && checknoretrieve(fnbuf)) return;

            if (S_ISREG(st.st_mode) && (cptr->types & T_REG) == 0) {
                reply(550, "Cannot %s plain files.", cptr->name);
                return;
            }
            if (S_ISREG(st.st_mode) != 0 && S_ISDIR(st.st_mode) != 0) {
                reply(550, "Cannot %s special files.", cptr->name);
                return;
            }
            if (!(cptr->types & T_ASCII) && deny_badasciixfer(550, ""))
                return;

            logname = &fnbuf[0];
            options |= cptr->options;

            strcpy(namebuf, cptr->external_cmd);
            if ((ptr = strchr(namebuf, ' ')) != NULL)
                *ptr = '\0';
            if (stat(namebuf, &st) != 0) {
                syslog(LOG_ERR, "external command %s not found",
                       namebuf);
                reply(550,
                "Local error: conversion program not found. Cannot %s file.",
                             cptr->name);
                return;
            }
            (void) retrieve(cptr->external_cmd, logname);

            goto dolog;  /* transfer of converted file completed */
        } while ( (cptr = cptr->next) != NULL );

    } 

    if (cmd == NULL) { /* no command */
        fin = fopen(name, "r"), closefunc = fclose;
        st.st_size = 0;
    } else {           /* run command */
        static char line[BUFSIZ];

        (void) sprintf(line, cmd, name), name = line;
        fin = ftpd_popen(line, "r", 1), closefunc = ftpd_pclose;
        st.st_size = -1;
#ifdef HAVE_ST_BLKSIZE
        st.st_blksize = BUFSIZ;
#endif
    }
    if (fin == NULL) {
        if (errno != 0)
            perror_reply(550, name);
        return;
    }
    if (cmd == NULL &&
        (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
        reply(550, "%s: not a plain file.", name);
        goto done;
    }
    if (restart_point) {
        if (type == TYPE_A) {
            register int i,
              n,
              c;

            n = restart_point;
            i = 0;
            while (i++ < n) {
                if ((c = getc(fin)) == EOF) {
                    perror_reply(550, name);
                    goto done;
                }
                if (c == '\n')
                    i++;
            }
        } else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
            perror_reply(550, name);
            goto done;
        }
    }
    dout = dataconn(name, st.st_size, "w");
    if (dout == NULL)
        goto done;
#ifdef HAVE_ST_BLKSIZE
    send_data(fin, dout, st.st_blksize*2);
#else
    send_data(fin, dout, BUFSIZ);
#endif
    (void) fclose(dout);

  dolog:
    if (log_outbound_xfers && xferlog && (cmd == 0)) {
        char msg[MAXPATHLEN];
	char *msg2;		/* for stupid_sprintf */
        int xfertime = time(NULL) - start_time;
        time_t curtime = time(NULL);
        int loop;

        if (!xfertime)
            xfertime++;
        realpath((logname != NULL) ? logname : name, &namebuf[0]); 
        for (loop = 0; namebuf[loop]; loop++)
            if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
                namebuf[loop] = '_';

#ifdef STUPID_SPRINTF
/* Some sprintfs can't deal with a lot of arguments, so we split this */
#if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
        sprintf(msg, "%.24s %d %s %qd ",
#else
        sprintf(msg, "%.24s %d %s %d ",
#endif
                ctime(&curtime),
                xfertime,
                remotehost,
                byte_count
	    );
	msg2 = msg + strlen(msg);	/* sigh */
        sprintf(msg2, "%s %c %s %c %c %s ftp %d %s\n",
                namebuf,
                (type == TYPE_A) ? 'a' : 'b',
                opt_string(options),
                'o',
                anonymous ? 'a' : (guest ? 'g' : 'r'),
                anonymous ? guestpw : pw->pw_name,
                authenticated,
                authenticated ? authuser : "*"
            );
#else
#if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
        sprintf(msg, "%.24s %d %s %qd %s %c %s %c %c %s ftp %d %s\n",
#else
        sprintf(msg, "%.24s %d %s %d %s %c %s %c %c %s ftp %d %s\n",
#endif
                ctime(&curtime),
                xfertime,
                remotehost,
                byte_count,
                namebuf,
                (type == TYPE_A) ? 'a' : 'b',
                opt_string(options),
                'o',
                anonymous ? 'a' : (guest ? 'g' : 'r'),
                anonymous ? guestpw : pw->pw_name,
                authenticated,
                authenticated ? authuser : "*"
            );
#endif /* stupid_sprintf */
        write(xferlog, msg, strlen(msg));
    }
    data = -1;
    pdata = -1;
  done:
    if (closefunc)
        (*closefunc) (fin);
}

void
#ifdef __STDC__
store(char *name, char *mode, int unique)
#else
store(name,mode,unique)
char *name;
char *mode;
int unique;
#endif
{
    FILE *fout, *din;
    struct stat st;
    int (*closefunc) ();
#ifdef __STDC__
    char *gunique(char *local);
#else
    char *gunique();
#endif
    time_t start_time = time(NULL);

    struct aclmember *entry = NULL;

    int fdout;

#ifdef OVERWRITE
    int overwrite = 1;

#endif /* OVERWRITE */

#ifdef UPLOAD
    int open_flags = (O_RDWR | O_CREAT |
		      ((mode != NULL && *mode == 'a') ? O_APPEND : O_TRUNC));

    mode_t oldmask;
    int f_mode = -1,
      dir_mode,
      match_value = -1;
    uid_t uid;
    gid_t gid;
    uid_t oldid;
    int valid = 0;

#endif /* UPLOAD */

    if (unique && stat(name, &st) == 0 &&
        (name = gunique(name)) == NULL)
        return;

    /*
     * check the filename, is it legal?
     */
    if ( (fn_check(name)) <= 0 )
        return;

#ifdef OVERWRITE
    /* if overwrite permission denied and file exists... then deny the user
     * permission to write the file. */
    while (getaclentry("overwrite", &entry) && ARG0 && ARG1 != NULL) {
        if (type_match(ARG1))
            if (strcmp(ARG0, "yes") != 0) {
                overwrite = 0;
                open_flags |= O_EXCL;
            }
    }

#ifdef PARANOID
    overwrite = 0;
#endif
    if (!overwrite && !stat(name, &st)) {
        reply(553, "%s: Permission denied. (Overwrite)", name);
        return;
    }
#endif /* OVERWRITE */

#ifdef UPLOAD
    if ( (match_value = upl_check(name, &uid, &gid, &f_mode, &valid)) < 0 )
        return;

    /* do not truncate the file if we are restarting */
    if (restart_point)
        open_flags &= ~O_TRUNC;

    /* if the user has an explicit new file mode, than open the file using
     * that mode.  We must take care to not let the umask affect the file
     * mode.
     * 
     * else open the file and let the default umask determine the file mode. */
    if (f_mode >= 0) {
        oldmask = umask(0000);
        fdout = open(name, open_flags, f_mode);
        umask(oldmask);
    } else
        fdout = open(name, open_flags, 0666);

    if (fdout < 0) {
        perror_reply(553, name);
        return;
    }
    /* if we have a uid and gid, then use them. */

    if (valid > 0) {
        oldid = geteuid();
        delay_signaling(); /* we can't allow any signals while euid==0: kinch */
        (void) seteuid((uid_t) 0);
        if ((fchown(fdout, uid, gid)) < 0) {
            (void) seteuid(oldid);
            enable_signaling(); /* we can allow signals once again: kinch */
            perror_reply(550, "fchown");
            return;
        }
        (void) seteuid(oldid);
        enable_signaling(); /* we can allow signals once again: kinch */
    }
#endif /* UPLOAD */

    if (restart_point && (open_flags & O_APPEND) == 0)
        mode = "r+";

#ifdef UPLOAD
    fout = fdopen(fdout, mode);
#else
    fout = fopen(name, mode);
#endif /* UPLOAD */

    closefunc = fclose;
    if (fout == NULL) {
        perror_reply(553, name);
        return;
    }
    if (restart_point) {
        if (type == TYPE_A) {
            register int i,
              n,
              c;

            n = restart_point;
            i = 0;
            while (i++ < n) {
                if ((c = getc(fout)) == EOF) {
                    perror_reply(550, name);
                    goto done;
                }
                if (c == '\n')
                    i++;
            }
            /* We must do this seek to "current" position because we are
             * changing from reading to writing. */
            if (fseek(fout, 0L, L_INCR) < 0) {
                perror_reply(550, name);
                goto done;
            }
        } else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
            perror_reply(550, name);
            goto done;
        }
    }
    din = dataconn(name, (off_t) - 1, "r");
    if (din == NULL)
        goto done;
    if (receive_data(din, fout) == 0) {
        if (unique)
            reply(226, "Transfer complete (unique file name:%s).",
                  name);
        else
            reply(226, "Transfer complete.");
    }
    (void) fclose(din);

  dolog:
    if (log_incoming_xfers && xferlog) {
        char namebuf[MAXPATHLEN],
          msg[MAXPATHLEN];
	char *msg2;		/* for stupid_sprintf */
        int xfertime = time(NULL) - start_time;
        time_t curtime = time(NULL);
        int loop;

        if (!xfertime)
            xfertime++;
        realpath(name, namebuf);
        for (loop = 0; namebuf[loop]; loop++)
            if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
                namebuf[loop] = '_';

#ifdef STUPID_SPRINTF
/* see above */
#if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
        sprintf(msg, "%.24s %d %s %qd ",
#else
        sprintf(msg, "%.24s %d %s %d ",
#endif
                ctime(&curtime),
                xfertime,
                remotehost,
                byte_count
	    );
	msg2 = msg + strlen(msg);	/* sigh */
        sprintf(msg2, "%s %c %s %c %c %s ftp %d %s\n",
                namebuf,
                (type == TYPE_A) ? 'a' : 'b',
                opt_string(0),
                'i',
                anonymous ? 'a' : (guest ? 'g' : 'r'),
                anonymous ? guestpw : pw->pw_name,
                authenticated,
                authenticated ? authuser : "*"
            );
#else
#if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
        sprintf(msg, "%.24s %d %s %qd %s %c %s %c %c %s ftp %d %s\n",
#else
        sprintf(msg, "%.24s %d %s %d %s %c %s %c %c %s ftp %d %s\n",
#endif
                ctime(&curtime),
                xfertime,
                remotehost,
                byte_count,
                namebuf,
                (type == TYPE_A) ? 'a' : 'b',
                opt_string(0),
                'i',
                anonymous ? 'a' : (guest ? 'g' : 'r'),
                anonymous ? guestpw : pw->pw_name,
                authenticated,
                authenticated ? authuser : "*"
            );
#endif /* STUPID_SPRINTF */
        write(xferlog, msg, strlen(msg));
    }
    data = -1;
    pdata = -1;
  done:
    (*closefunc) (fout);
}

FILE *
#ifdef __STDC__
getdatasock(char *mode)
#else
getdatasock(mode)
char *mode;
#endif
{
    int s,
      on = 1,
      tries;

    if (data >= 0)
        return (fdopen(data, mode));
    delay_signaling(); /* we can't allow any signals while euid==0: kinch */
    (void) seteuid((uid_t) 0);
    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0)
        goto bad;
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
                   (char *) &on, sizeof(on)) < 0)
        goto bad;
    /* anchor socket to avoid multi-homing problems */
    data_source.sin_family = AF_INET;
    data_source.sin_addr = ctrl_addr.sin_addr;

#if defined(VIRTUAL) && defined(CANT_BIND) /* can't bind to virtual address */
    data_source.sin_addr.s_addr = htonl(INADDR_ANY);
#endif
    for (tries = 1;; tries++) {
        if (bind(s, (struct sockaddr *) &data_source,
                 sizeof(data_source)) >= 0)
            break;
        if (errno != EADDRINUSE || tries > 10)
            goto bad;
        sleep(tries);
    }
#if defined(M_UNIX) && !defined(_M_UNIX)  /* bug in old TCP/IP release */
    {
        struct linger li;
        li.l_onoff = 1;
        li.l_linger = 900;
        if (setsockopt(s, SOL_SOCKET, SO_LINGER,
          (char *)&li, sizeof(struct linger)) < 0) {
            syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
            goto bad;
        }
    }
#endif
    (void) seteuid((uid_t) pw->pw_uid);
    enable_signaling(); /* we can allow signals once again: kinch */

#ifdef IPTOS_THROUGHPUT
    on = IPTOS_THROUGHPUT;
    if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
          syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
#endif
#ifdef TCP_NOPUSH
	/*
	 * Turn off push flag to keep sender TCP from sending short packets
	 * at the boundaries of each write().  Should probably do a SO_SNDBUF
	 * to set the send buffer size as well, but that may not be desirable
	 * in heavy-load situations.
	 */
	on = 1;
	if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0)
		syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
#endif

    return (fdopen(s, mode));
  bad:
    (void) seteuid((uid_t) pw->pw_uid);
    enable_signaling(); /* we can allow signals once again: kinch */
    (void) close(s);
    return (NULL);
}

FILE *
#ifdef __STDC__
dataconn(char *name, off_t size, char *mode)
#else
dataconn(name,size,mode)
char *name;
off_t size; 
char *mode;
#endif
{
    char sizebuf[32];
    FILE *file;
    int retry = 0;
#ifdef IPTOS_LOWDELAY
    int tos;
#endif

    file_size = size;
    byte_count = 0;
    if (size != (off_t) - 1)
#if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
        (void) sprintf(sizebuf, " (%qd bytes)", size);
#else
	(void) sprintf(sizebuf, " (%d bytes)", size);
#endif
    else
        (void) strcpy(sizebuf, "");
    if (pdata >= 0) {
      struct sockaddr_in from;
        int s, fromlen = sizeof(from);
#ifdef FD_ZERO
        struct timeval timeout;
        fd_set set;
 
        FD_ZERO(&set);
        FD_SET(pdata, &set);
 
        timeout.tv_usec = 0;
        timeout.tv_sec = 120;
        if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) == 0 ||
            (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) {
#else      
        alarm(120);
        s = accept(pdata, (struct sockaddr *) &from, &fromlen);
        alarm(0);
        if (s < 0) {
#endif
            reply(425, "Can't open data connection.");
            (void) close(pdata);
            pdata = -1;
            return (NULL);
        }
        (void) close(pdata);
        pdata = s;
#ifdef IPTOS_LOWDELAY
        tos = IPTOS_LOWDELAY;
        (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
                          sizeof(int));

#endif
        reply(150, "Opening %s mode data connection for %s%s.",
              type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
        return (fdopen(pdata, mode));
    }
    if (data >= 0) {
        reply(125, "Using existing data connection for %s%s.",
              name, sizebuf);
        usedefault = 1;
        return (fdopen(data, mode));
    }
    if (usedefault)
        data_dest = his_addr;
    usedefault = 1;
    file = getdatasock(mode);
    if (file == NULL) {
        reply(425, "Can't create data socket (%s,%d): %s.",
              inet_ntoa(data_source.sin_addr),
              ntohs(data_source.sin_port), strerror(errno));
        return (NULL);
    }
    data = fileno(file);
    while (connect(data, (struct sockaddr *) &data_dest,
                   sizeof(data_dest)) < 0) {
        if ((errno == EADDRINUSE || errno == EINTR) && retry < swaitmax) {
            sleep((unsigned) swaitint);
            retry += swaitint;
            continue;
        }
        perror_reply(425, "Can't build data connection");
        (void) fclose(file);
        data = -1;
        return (NULL);
    }
    reply(150, "Opening %s mode data connection for %s%s.",
          type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
    return (file);
}

/* Tranfer the contents of "instr" to "outstr" peer using the appropriate
 * encapsulation of the data subject to Mode, Structure, and Type.
 *
 * NB: Form isn't handled. */

void
#ifdef __STDC__
send_data(FILE *instr, FILE *outstr, off_t blksize)
#else
send_data(instr,outstr,blksize)
FILE *instr;
FILE *outstr;
off_t blksize;
#endif
{
    register int c,
      cnt;
    register char *buf;
    int netfd,
      filefd;

    transflag++;
    if (setjmp(urgcatch)) {
        transflag = 0;
        return;
    }
    switch (type) {

    case TYPE_A:
        while ((c = getc(instr)) != EOF) {
            byte_count++;
            if (c == '\n') {
                if (ferror(outstr))
                    goto data_err;
                (void) putc('\r', outstr);
            }
            (void) putc(c, outstr);
        }
        fflush(outstr);
        transflag = 0;
        if (ferror(instr))
            goto file_err;
        if (ferror(outstr))
            goto data_err;
        reply(226, "Transfer complete.");
        return;

    case TYPE_I:
    case TYPE_L:
        if ((buf = (char *) malloc((u_int) blksize)) == NULL) {
            transflag = 0;
            perror_reply(451, "Local resource failure: malloc");
            return;
        }
        netfd = fileno(outstr);
        filefd = fileno(instr);
/* Debian fix: this seems gratuitous somehow, testing ... XXX: */
#ifdef bogus__linux__
	while ((cnt = read(filefd, buf, (u_int)blksize)) > 0)
	{
	int outcnt=0, newcnt=0;
	while ((outcnt=write(netfd, buf+newcnt, cnt-newcnt))!= cnt-newcnt)
		newcnt+=outcnt;
	byte_count += cnt;				
	}
#else
        while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 &&
               write(netfd, buf, cnt) == cnt)
            byte_count += cnt;
#endif
        transflag = 0;
        (void) free(buf);
        if (cnt != 0) {
            if (cnt < 0)
                goto file_err;
            goto data_err;
        }
        reply(226, "Transfer complete.");
        return;
    default:
        transflag = 0;
        reply(550, "Unimplemented TYPE %d in send_data", type);
        return;
    }

  data_err:
    transflag = 0;
    perror_reply(426, "Data connection");
    return;

  file_err:
    transflag = 0;
    perror_reply(551, "Error on input file");
}

/* Transfer data from peer to "outstr" using the appropriate encapulation of
 * the data subject to Mode, Structure, and Type.
 *
 * N.B.: Form isn't handled. */

int
#ifdef __STDC__
receive_data(FILE *instr, FILE *outstr)
#else
receive_data(instr,outstr)
FILE *instr;
FILE *outstr;
#endif
{
    register int c;
    int cnt,
      bare_lfs = 0;
    char buf[BUFSIZ];

    transflag++;
    if (setjmp(urgcatch)) {
        transflag = 0;
        return (-1);
    }
    switch (type) {

    case TYPE_I:
    case TYPE_L:
        while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
            if (write(fileno(outstr), buf, cnt) != cnt)
                goto file_err;
            byte_count += cnt;
        }
        if (cnt < 0)
            goto data_err;
        transflag = 0;
        return (0);

    case TYPE_E:
        reply(553, "TYPE E not implemented.");
        transflag = 0;
        return (-1);

    case TYPE_A:
        while ((c = getc(instr)) != EOF) {
            byte_count++;
            if (c == '\n')
                bare_lfs++;
            while (c == '\r') {
                if (ferror(outstr))
                    goto data_err;
                if ((c = getc(instr)) != '\n') {
                    (void) putc('\r', outstr);
                    if (c == EOF) /* null byte fix, noid@cyborg.larc.nasa.gov */
                        goto contin2;
                }
            }
            (void) putc(c, outstr);
          contin2:;
        }
        fflush(outstr);
        if (ferror(instr))
            goto data_err;
        if (ferror(outstr))
            goto file_err;
        transflag = 0;
        if (bare_lfs) {
            lreply(226, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
            printf("   File may not have transferred correctly.\r\n");
        }
        return (0);
    default:
        reply(550, "Unimplemented TYPE %d in receive_data", type);
        transflag = 0;
        return (-1);
    }

  data_err:
    transflag = 0;
    perror_reply(426, "Data Connection");
    return (-1);

  file_err:
    transflag = 0;
    perror_reply(452, "Error writing file");
    return (-1);
}

void
#ifdef __STDC__
statfilecmd(char *filename)
#else
statfilecmd(filename)
char *filename;
#endif
{
    char line[BUFSIZ];
    FILE *fin;
    int c;

    if (anonymous && dolreplies)
        (void) snprintf(line, sizeof(line), ls_long, filename);
    else
        (void) snprintf(line, sizeof(line), ls_short, filename);
    fin = ftpd_popen(line, "r", 0);
    lreply(213, "status of %s:", filename);
    while ((c = getc(fin)) != EOF) {
        if (c == '\n') {
            if (ferror(stdout)) {
                perror_reply(421, "control connection");
                (void) ftpd_pclose(fin);
                dologout(1);
                /* NOTREACHED */
            }
            if (ferror(fin)) {
                perror_reply(551, filename);
                (void) ftpd_pclose(fin);
                return;
            }
            (void) putc('\r', stdout);
        }
        (void) putc(c, stdout);
    }
    (void) ftpd_pclose(fin);
    reply(213, "End of Status");
}

void
#ifdef __STDC__
statcmd(void)
#else
statcmd()
#endif
{
    struct sockaddr_in *sin;
    u_char *a,
     *p;

    lreply(211, "%s FTP server status:", hostname);
    printf("     %s\r\n", version);
    printf("     Connected to %s", remotehost);
    if (!isdigit(remotehost[0]))
        printf(" (%s)", inet_ntoa(his_addr.sin_addr));
    printf("\r\n");
    if (logged_in) {
        if (anonymous)
            printf("     Logged in anonymously\r\n");
        else
            printf("     Logged in as %s\r\n", pw->pw_name);
    } else if (askpasswd)
        printf("     Waiting for password\r\n");
    else
        printf("     Waiting for user name\r\n");
    printf("     TYPE: %s", typenames[type]);
    if (type == TYPE_A || type == TYPE_E)
        printf(", FORM: %s", formnames[form]);
    if (type == TYPE_L)
#ifdef NBBY 
        printf(" %d", NBBY);
#else
        printf(" %d", bytesize);/* need definition! */
#endif
    printf("; STRUcture: %s; transfer MODE: %s\r\n",
           strunames[stru], modenames[mode]);
    if (data != -1)
        printf("     Data connection open\r\n");
    else if (pdata != -1) {
        printf("     in Passive mode");
        sin = &pasv_addr;
        goto printaddr;
    } else if (usedefault == 0) {
        printf("     PORT");
        sin = &data_dest;
      printaddr:
        a = (u_char *) & sin->sin_addr;
        p = (u_char *) & sin->sin_port;
#define UC(b) (((int) b) & 0xff)
        printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
               UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
#undef UC
    } else
        printf("     No data connection\r\n");
    reply(211, "End of status");
}

void
#ifdef __STDC__
fatal(char *s)
#else
fatal(s)
char *s;
#endif
{
    reply(451, "Error in server: %s\n", s);
    reply(221, "Closing connection due to server error.");
    dologout(0);
    /* NOTREACHED */
}

void
#if defined (HAVE_VPRINTF)
/* VARARGS2 */
#ifdef __STDC__
reply(int n, char *fmt, ...)
#else
reply(n, fmt, va_alist)
     int n;
     char * fmt;
     va_dcl
#endif
{
    VA_LOCAL_DECL
    
    VA_START(fmt); 

    if (autospout != NULL) {
        char *ptr = autospout;

        printf("%d-", n);
        while (*ptr) {
            if (*ptr == '\n') {
                fputs("\r\n", stdout);
                if (*(++ptr))
                    printf("%03d-", n);
            } else {
                putc(*ptr++,stdout);
            }
        }
        if (*(--ptr) != '\n')
            printf("\r\n");
        if (autospout_free) {
            (void) free(autospout);
            autospout_free = 0;
        }
        autospout = 0;
    }
    printf("%d ", n);
    vprintf(fmt, ap);
    printf("\r\n");
    (void) fflush(stdout);

    if (debug) {
        char buf[BUFSIZ];
        (void) vsprintf(buf, fmt, ap);

        syslog(LOG_DEBUG, "<--- %d ", n);
        syslog(LOG_DEBUG, buf);
    }

    VA_END;
}

void
/* VARARGS2 */
#ifdef __STDC__
lreply(int n, char *fmt,...)
#else
lreply(n, fmt, va_alist)
     int n;
     char * fmt;
     va_dcl
#endif
{
    VA_LOCAL_DECL

    VA_START(fmt);

    if (!dolreplies)
        return;
    printf("%d-", n);
    vprintf(fmt, ap);
    printf("\r\n");
    (void) fflush(stdout);

    if (debug) {
        char buf[BUFSIZ];
        (void) vsprintf(buf, fmt, ap);

        syslog(LOG_DEBUG, "<--- %d- ", n);
        syslog(LOG_DEBUG, buf);
    }

    VA_END;
}

#else
/* VARARGS2 */
void
reply(int n, char *fmt, int p0, int p1, int p2, int p3, int p4, int p5)
{
    if (autospout != NULL) {
        char *ptr = autospout;

        printf("%d-", n);
        while (*ptr) {
            if (*ptr == '\n') {
                printf("\r\n");
                if (*(++ptr))
                    printf("%d-", n);
            } else {
                putc(*ptr++,stdout);
            }
        }
        if (*(--ptr) != '\n')
            printf("\r\n");
        if (autospout_free) {
            (void) free(autospout);
            autospout_free = 0;
        }
        autospout = 0;
    }
    printf("%d ", n);
    printf(fmt, p0, p1, p2, p3, p4, p5);
    printf("\r\n");
    (void) fflush(stdout);
    if (debug) {
        syslog(LOG_DEBUG, "<--- %d ", n);
        syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
    }
}

void
/* VARARGS2 */
void
lreply(int n, char *fmt, int p0, int p1, int p2, int p3, int p4, int p5)
{
    if (!dolreplies)
        return;
    printf("%d-", n);
    printf(fmt, p0, p1, p2, p3, p4, p5);
    printf("\r\n");
    (void) fflush(stdout);
    if (debug) {
        syslog(LOG_DEBUG, "<--- %d- ", n);
        syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
    }
}
#endif

void
#ifdef __STDC__
ack(char *s)
#else
ack(s)
char *s;
#endif
{
    reply(250, "%s command successful.", s);
}

void
#ifdef __STDC__
nack(char *s)
#else
nack(s)
char *s;
#endif
{
    reply(502, "%s command not implemented.", s);
}

void
#ifdef __STDC__
yyerror(char *s)
#else
yyerror(s)
char *s;
#endif
{
    char *cp;
    if (s == NULL || yyerrorcalled != 0) return;
    if ((cp = strchr(cbuf, '\n')) != NULL)
        *cp = '\0';
    reply(500, "'%s': command not understood.", cbuf);
    yyerrorcalled = 1;
    return;
}

void
#ifdef __STDC__
delete(char *name)
#else
delete(name)
char *name;
#endif
{
    struct stat st;

    /*
     * delete permission?
     */

    if ( (del_check(name)) == 0 )
        return;

    if (lstat(name, &st) < 0) {
        perror_reply(550, name);
        return;
    }
    if ((st.st_mode & S_IFMT) == S_IFDIR) {
        uid_t uid;
        gid_t gid;
        int valid;

        /*
         * check the directory, can we rmdir here?
         */
        if ( (dir_check(name, &uid, &gid, &valid)) <= 0 )
            return;

        if (rmdir(name) < 0) {
            perror_reply(550, name);
            return;
        }
        goto done;
    }
    if (unlink(name) < 0) {
        perror_reply(550, name);
        return;
    }
  done:
    {
        char path[MAXPATHLEN];

        realpath(name, path);

        if (anonymous) {
            syslog(LOG_NOTICE, "%s of %s [%s] deleted %s", guestpw, remotehost,
                   remoteaddr, path);
        } else {
            syslog(LOG_NOTICE, "%s of %s [%s] deleted %s", pw->pw_name,
                   remotehost, remoteaddr, path);
        }
    }

    ack("DELE");
}

void
#ifdef __STDC__
cwd(char *path)
#else
cwd(path)
char *path;
#endif
{
    struct aclmember *entry = NULL;
    char cdpath[MAXPATHLEN + 1];

    if (chdir(path) < 0) {
        /* alias checking */
        while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL) {
            if (!strcasecmp(ARG0, path)) {
                if (chdir(ARG1) < 0)
                    perror_reply(550, path);
                else {
                    show_message(250, C_WD);
                    show_readme(250, C_WD);
                    ack("CWD");
                }
                return;
            }
        }
    /* check for "cdpath" directories. */
    entry = (struct aclmember *) NULL;
        while (getaclentry("cdpath", &entry) && ARG0 != NULL) {
        strcpy(cdpath,ARG0);
        strcat(cdpath,"/");
        strcat(cdpath,path);
            if (chdir(cdpath) >= 0) {
                show_message(250, C_WD);
                show_readme(250, C_WD);
                ack("CWD");
                return;
            }
        }
        perror_reply(550,path);
    } else {
        show_message(250, C_WD);
        show_readme(250, C_WD);
        ack("CWD");
    }
}

void
#ifdef __STDC__
makedir(char *name)
#else
makedir(name)
char *name;
#endif
{
	uid_t uid;
	gid_t gid;
	int   valid;

    /*
     * check the directory, can we mkdir here?
     */
    if ( (dir_check(name, &uid, &gid, &valid)) <= 0 )
        return;

    /*
     * check the filename, is it legal?
     */
    if ( (fn_check(name)) <= 0 )
        return;

    if (mkdir(name, 0777) < 0) {
        if (errno == EEXIST)
            perror_reply(553, name);
        else
            perror_reply(550, name);
	return;
    }
    reply(257, "MKD command successful.");
}

void
#ifdef __STDC__
removedir(char *name)
#else
removedir(name)
char *name;
#endif
{
    uid_t uid;
    gid_t gid;
    int valid;

    /*
     * check the directory, can we rmdir here?
     */
    if ( (dir_check(name, &uid, &gid, &valid)) <= 0 )
        return;

    /*
     * delete permission?
     */

    if ( (del_check(name)) == 0 )
        return;

    if (rmdir(name) < 0) {
        if (errno == EBUSY)
            perror_reply(450, name);
        else
            perror_reply(550, name);
    }
    else
        ack("RMD");
}

void
#ifdef __STDC__
pwd(void)
#else
pwd()
#endif
{
    char path[MAXPATHLEN + 1];
#ifdef HAVE_GETCWD
    extern char *getcwd();
#else
#ifdef __STDC__
    extern char *getwd(char *);
#else
    extern char *getwd();
#endif
#endif

#ifdef HAVE_GETCWD
    if (getcwd(path,MAXPATHLEN) == (char *) NULL)
#else
    if (getwd(path) == (char *) NULL)
#endif
/* Dink!  If you couldn't get the path and the buffer is now likely to
   be undefined, why are you trying to PRINT it?!  _H*
        reply(550, "%s.", path); */
    {
	realpath (".", path);	/* realpath_on_steroids can deal */
    }
    reply(257, "\"%s\" is current directory.", path);
}

char *
#ifdef __STDC__
renamefrom(char *name)
#else
renamefrom(name)
char *name;
#endif
{
    struct stat st;
    struct aclmember *entry = NULL;	/* Added: fixes a bug.  _H*/

    if (lstat(name, &st) < 0) {
        perror_reply(550, name);
        return ((char *) 0);
    }

    /* if rename permission denied and file exists... then deny the user
     * permission to rename the file. 
     */
    while (getaclentry("rename", &entry) && ARG0 && ARG1 != NULL) {
        if (type_match(ARG1))
            if (strcmp(ARG0, "yes")) {
                reply(553, "%s: Permission denied. (rename)", name);
                return ((char *) 0);
            }
    }

    reply(350, "File exists, ready for destination name");
    return (name);
}

void
#ifdef __STDC__
renamecmd(char *from, char *to)
#else
renamecmd(from,to)
char *from;
char *to;
#endif
{
    struct stat st;

    /*
     * check the filename, is it legal?
     */
    if ( (fn_check(to)) == 0 )
        return;

#ifdef PARANOID
/* Almost forgot about this.  Don't allow renaming TO existing files --
   otherwise someone can rename "trivial" to "warez", and "warez" is gone!
   XXX: This part really should do the same "overwrite" check as store(). */
    if (!stat(to, &st)) {
      reply (550, "%s: Permission denied. (rename)", to);
      return;
    }
#endif

    if (rename(from, to) < 0)
        perror_reply(550, "rename");
    else
        ack("RNTO");
}

void
#ifdef __STDC__
dolog(struct sockaddr_in *sin)
#else
dolog(sin)
struct sockaddr_in *sin;
#endif
{
    struct hostent *hp;
	char *blah;

#ifdef	DNS_TRYAGAIN
    int num_dns_tries = 0;
    /*
     * 27-Apr-93    EHK/BM
     * far away connections might take some time to get their IP address
     * resolved. That's why we try again -- maybe our DNS cache has the
     * PTR-RR now. This code is sloppy. Far better is to check what the
     * resolver returned so that in case of error, there's no need to
     * try again.
     */
dns_again:
     hp = gethostbyaddr((char *) &sin->sin_addr,
                                sizeof (struct in_addr), AF_INET);

     if ( !hp && ++num_dns_tries <= 1 ) {
        sleep(3);
        goto dns_again;         /* try DNS lookup once more     */
     }
#else
    hp = gethostbyaddr((char *)&sin->sin_addr, sizeof(struct in_addr), AF_INET);
#endif

    blah = inet_ntoa(sin->sin_addr);

    (void) strncpy(remoteaddr, blah, sizeof(remoteaddr));

    if (!strcmp(remoteaddr, "0.0.0.0")) {
        nameserved = 1;
        strncpy(remotehost, "localhost", sizeof(remotehost));
    } else {
        if (hp) {
            nameserved = 1;
            (void) strncpy(remotehost, hp->h_name, sizeof(remotehost));
        } else {
            nameserved = 0;
            (void) strncpy(remotehost, remoteaddr, sizeof(remotehost));
        }
    }

    sprintf(proctitle, "%s: connected", remotehost);
    setproctitle(proctitle);

#if 0	/* this is redundant unless the caller doesn't do *anything*, and
	   tcpd will pick it up and deal with it better anyways. _H*/
    if (logging)
        syslog(LOG_INFO, "connection from %s [%s]", remotehost,
               remoteaddr);
#endif
}

/* Record logout in wtmp file and exit with supplied status. */

void
#ifdef __STDC__
dologout(int status)
#else
dologout(status)
int status;
#endif
{
     /*
      * Prevent reception of SIGURG from resulting in a resumption
      * back to the main program loop.
      */
     transflag = 0;
 
    if (logged_in) {
        delay_signaling(); /* we can't allow any signals while euid==0: kinch */
        (void) seteuid((uid_t) 0);
        logwtmp(ttyline, "", "");
    }
    if (logging)
	syslog(LOG_INFO, "FTP session closed");
    if (xferlog)
        close(xferlog);
    acl_remove();
    close (data);		/* H* fix: clean up a little better */
    close (pdata);
    /* beware of flushing buffers after a SIGPIPE */
    _exit(status);
}

SIGNAL_TYPE
#ifdef __STDC__
myoob(int sig)
#else
myoob(sig)
int sig;
#endif
{
    char *cp;

    /* only process if transfer occurring */
    if (!transflag)
        return;
    cp = tmpline;
    if (getline(cp, 7, stdin) == NULL) {
        reply(221, "You could at least say goodbye.");
        dologout(0);
    }
    upper(cp);
    if (strcmp(cp, "ABOR\r\n") == 0) {
        tmpline[0] = '\0';
        reply(426, "Transfer aborted. Data connection closed.");
        reply(226, "Abort successful");
        (void) signal(SIGURG, myoob);
        longjmp(urgcatch, 1);
    }
    if (strcmp(cp, "STAT\r\n") == 0) {
        if (file_size != (off_t) - 1)
            reply(213, "Status: %lu of %lu bytes transferred",
                  byte_count, file_size);
        else
            reply(213, "Status: %lu bytes transferred", byte_count);
    }
}

/* Note: a response of 425 is not mentioned as a possible response to the
 * PASV command in RFC959. However, it has been blessed as a legitimate
 * response by Jon Postel in a telephone conversation with Rick Adams on 25
 * Jan 89. */

void
#ifdef __STDC__
passive(void)
#else
passive()
#endif
{
    int len;
    register char *p,
     *a;

/* H* fix: if we already *have* a passive socket, close it first.  Prevents
   a whole variety of entertaining clogging attacks. */
    if (pdata > 0)
	close (pdata);

    pdata = socket(AF_INET, SOCK_STREAM, 0);
    if (pdata < 0) {
        perror_reply(425, "Can't open passive connection");
        return;
    }
    pasv_addr = ctrl_addr;
    pasv_addr.sin_port = 0;
    delay_signaling(); /* we can't allow any signals while euid==0: kinch */
    (void) seteuid((uid_t) 0);		/* XXX: not needed if > 1024 */
    if (bind(pdata, (struct sockaddr *) &pasv_addr, sizeof(pasv_addr)) < 0) {
        (void) seteuid((uid_t) pw->pw_uid);
        enable_signaling(); /* we can allow signals once again: kinch */
        goto pasv_error;
    }
    (void) seteuid((uid_t) pw->pw_uid);
    enable_signaling(); /* we can allow signals once again: kinch */
    len = sizeof(pasv_addr);
    if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
        goto pasv_error;
    if (listen(pdata, 1) < 0)
        goto pasv_error;
    a = (char *) &pasv_addr.sin_addr;
    p = (char *) &pasv_addr.sin_port;

#define UC(b) (((int) b) & 0xff)

    reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
          UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
    return;

  pasv_error:
    (void) close(pdata);
    pdata = -1;
    perror_reply(425, "Can't open passive connection");
    return;
}

/* Generate unique name for file with basename "local". The file named
 * "local" is already known to exist. Generates failure reply on error. */
char *
#ifdef __STDC__
gunique(char *local)
#else
gunique(local)
char *local;
#endif
{
    static char new[MAXPATHLEN];
    struct stat st;
    char *cp = strrchr(local, '/');
    int count = 0;

    if (cp)
        *cp = '\0';
    if (stat(cp ? local : ".", &st) < 0) {
        perror_reply(553, cp ? local : ".");
        return ((char *) 0);
    }
    if (cp)
        *cp = '/';
    (void) strcpy(new, local);
    cp = new + strlen(new);
    *cp++ = '.';
    for (count = 1; count < 100; count++) {
        (void) sprintf(cp, "%d", count);
        if (stat(new, &st) < 0)
            return (new);
    }
    reply(452, "Unique file name cannot be created.");
    return ((char *) 0);
}

/* Format and send reply containing system error number. */

void
#ifdef __STDC__
perror_reply(int code, char *string)
#else
perror_reply(code,string)
int code;
char *string;
#endif
{
    reply(code, "%s: %s.", string, strerror(errno));
}

static char *onefile[] =
{"", 0};

void
#ifdef __STDC__
send_file_list(char *whichfiles)
#else
send_file_list(whichfiles)
char *whichfiles;
#endif
{
    struct stat st;
    DIR *dirp = NULL;

#ifdef HAVE_DIRENT
    struct dirent *dir;
#else
    struct direct *dir;
#endif

    FILE *dout = NULL;
    register char **dirlist,
     *dirname;
    int simple = 0;
#ifdef __STDC__
    char *strpbrk(const char *, const char *);
#else
    char *strpbrk();
#endif
    if (strpbrk(whichfiles, "~{[*?") != NULL) {
#ifdef __STDC__
        extern char **ftpglob(register char *v),
#else
        extern char **ftpglob(),
#endif
         *globerr;

        globerr = NULL;
        dirlist = ftpglob(whichfiles);
        if (globerr != NULL) {
            reply(550, globerr);
            return;
        } else if (dirlist == NULL) {
            errno = ENOENT;
            perror_reply(550, whichfiles);
            return;
        }
    } else {
        onefile[0] = whichfiles;
        dirlist = onefile;
        simple = 1;
    }

    if (setjmp(urgcatch)) {
        transflag = 0;
        return;
    }
    while ((dirname = *dirlist++) != NULL) {
        if (stat(dirname, &st) < 0) {
            /* If user typed "ls -l", etc, and the client used NLST, do what
             * the user meant. */
            if (dirname[0] == '-' && *dirlist == NULL && transflag == 0) {
                retrieve("/bin/ls %s", dirname);
                return;
            }
            perror_reply(550, whichfiles);
            if (dout != NULL) {
                (void) fclose(dout);
                transflag = 0;
                data = -1;
                pdata = -1;
            }
            return;
        }
        if ((st.st_mode & S_IFMT) == S_IFREG) {
            if (dout == NULL) {
                dout = dataconn("file list", (off_t) - 1, "w");
                if (dout == NULL)
                    return;
                transflag++;
            }
            fprintf(dout, "%s%s\n", dirname,
                    type == TYPE_A ? "\r" : "");
            byte_count += strlen(dirname) + 1;
            continue;
        } else if ((st.st_mode & S_IFMT) != S_IFDIR)
            continue;

        if ((dirp = opendir(dirname)) == NULL)
            continue;

        while ((dir = readdir(dirp)) != NULL) {
            char nbuf[MAXPATHLEN];

#ifndef HAVE_DIRENT    /* does not have d_namlen */
            if (dir->d_name[0] == '.' && dir->d_namlen == 1)
#else
            if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1))
#endif
                continue;
#ifndef HAVE_DIRENT    /* does not have d_namlen */
            if (dir->d_namlen == 2 && dir->d_name[0] == '.' &&
                dir->d_name[1] == '.')
#else
            if ((strlen(dir->d_name) == 2) && dir->d_name[0] == '.' &&
                dir->d_name[1] == '.')
#endif
                continue;

            sprintf(nbuf, "%s/%s", dirname, dir->d_name);

            /* We have to do a stat to insure it's not a directory or special
             * file. */
            if (simple || (stat(nbuf, &st) == 0 &&
                           (st.st_mode & S_IFMT) == S_IFREG)) {
                if (dout == NULL) {
                    dout = dataconn("file list", (off_t) - 1,
                                    "w");
                    if (dout == NULL) {
                        (void) closedir(dirp);
                        return;
                    }
                    transflag++;
                }
                if (nbuf[0] == '.' && nbuf[1] == '/')
                    fprintf(dout, "%s%s\n", &nbuf[2],
                            type == TYPE_A ? "\r" : "");
                else
                    fprintf(dout, "%s%s\n", nbuf,
                            type == TYPE_A ? "\r" : "");
                byte_count += strlen(nbuf) + 1;
            }
        }
        (void) closedir(dirp);
    }

    if (dout == NULL)
        reply(550, "No files found.");
    else if (ferror(dout) != 0)
        perror_reply(550, "Data connection");
    else
        reply(226, "Transfer complete.");

    transflag = 0;
    if (dout != NULL)
        (void) fclose(dout);
    data = -1;
    pdata = -1;
}

/*
**  SETPROCTITLE -- set process title for ps (from sendmail 8)
**
**	Parameters:
**		fmt -- a printf style format string.
**		a, b, c -- possible parameters to fmt.
**
**	Returns:
**		none.
**
**	Side Effects:
**		Clobbers argv of our main procedure so ps(1) will
**		display the title.
*/

#ifndef SPT_TYPE
# define SPT_TYPE	SPT_REUSEARGV  /* default type */
#endif

#if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN

#if SPT_TYPE == SPT_PSTAT
#include <sys/pstat.h>
#endif
#if SPT_TYPE == SPT_PSSTRINGS
#include <machine/vmparam.h>
#include <sys/exec.h>
#ifndef PS_STRINGS	/* hmmmm....  apparently not available after all */
#undef SPT_TYPE
#define SPT_TYPE	SPT_REUSEARGV
#else
#ifndef NKPDE			/* FreeBSD 2.0 */
#define NKPDE 63
typedef unsigned int	*pt_entry_t;
#endif
#endif
#endif

#if SPT_TYPE == SPT_PSSTRINGS
#define SETPROC_STATIC	static
#else
#define SETPROC_STATIC
#endif

#if SPT_TYPE == SPT_SYSMIPS
#include <sys/sysmips.h>
#include <sys/sysnews.h>
#endif

#if SPT_TYPE == SPT_SCO
#include <sys/immu.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/fs/s5param.h>
#if PSARGSZ > 2048
#define SPT_BUFSIZE	PSARGSZ
#endif
#endif

#ifndef SPT_PADCHAR
#define SPT_PADCHAR	' '
#endif

#ifndef SPT_BUFSIZE
#define SPT_BUFSIZE	2048
#endif

#endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */

#if SPT_TYPE != SPT_BUILTIN

/*VARARGS1*/
void
#ifdef __STDC__
setproctitle(const char *fmt, ...)
#else
setproctitle(fmt, va_alist)
	const char *fmt;
	va_dcl
#endif
{
#if SPT_TYPE != SPT_NONE
	register char *p;
	register int i;
	SETPROC_STATIC char buf[SPT_BUFSIZE];
	VA_LOCAL_DECL
#if SPT_TYPE == SPT_PSTAT
	union pstun pst;
#endif
#if SPT_TYPE == SPT_SCO
	off_t seek_off;
	static int kmem = -1;
	static int kmempid = -1;
#endif
#if SPT_TYPE == SPT_REUSEARGV
	extern char **Argv;
	extern char *LastArgv;
#endif

	p = buf;

	/* print ftpd: heading for grep */
	(void) strcpy(p, "ftpd: ");
	p += strlen(p);

	/* print the argument string */
	VA_START(fmt);
	(void) vsnprintf(p, sizeof buf - (p - buf), fmt, ap);
	VA_END;

	i = strlen(buf);

#if SPT_TYPE == SPT_PSTAT
	pst.pst_command = buf;
	pstat(PSTAT_SETCMD, pst, i, 0, 0);
#endif
#if SPT_TYPE == SPT_PSSTRINGS
	if (PS_STRINGS->ps_argvstr) {
                static char *ps_argv[2];
                ps_argv[0] = buf;
                ps_argv[1] = NULL;
                PS_STRINGS->ps_nargvstr = 1;
                PS_STRINGS->ps_argvstr = ps_argv;
        } else {
                PS_STRINGS->ps_nargvstr = 1;
                PS_STRINGS->ps_argvstr = buf;
        }
#endif
#if SPT_TYPE == SPT_SYSMIPS
	sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
#endif
#if SPT_TYPE == SPT_SCO
	if (kmem < 0 || kmempid != getpid())
	{
		if (kmem >= 0)
			close(kmem);
		kmem = open(_PATH_KMEM, O_RDWR, 0);
		if (kmem < 0)
			return;
		(void) fcntl(kmem, F_SETFD, 1);
		kmempid = getpid();
	}
	buf[PSARGSZ - 1] = '\0';

	/* Remove trailing carriage returns and new lines */
	while (buf[i - 1] == '\r' || buf[i - 1] == '\n')
		buf[--i] = '\0';

	seek_off = UVUBLK + (off_t) &((struct user *)0)->u_psargs;
	if (lseek(kmem, seek_off, SEEK_SET) == seek_off)
		(void) write(kmem, buf, PSARGSZ);
#endif
#if SPT_TYPE == SPT_REUSEARGV
/* This may fix problems in AIX, I don't really know since I don't have AIX */
/* Maybe someone will check it out and mail me, or someone will get me      */
/* ready access to AIX */
#ifndef AIX
	if (i > LastArgv - Argv[0] - 2)
	{
		i = LastArgv - Argv[0] - 2;
		buf[i] = '\0';
	}
#endif
	(void) strcpy(Argv[0], buf);
#ifndef AIX
	p = &Argv[0][i];
	while (p < LastArgv)
		*p++ = SPT_PADCHAR;
	Argv[1] = NULL;
#endif
#endif
#endif /* SPT_TYPE != SPT_NONE */
}

#endif /* SPT_TYPE != SPT_BUILTIN */

#ifdef KERBEROS
/* thanks to gshapiro@wpi.wpi.edu for the following kerberosities */

void
init_krb()
{
    char hostname[100];

#ifdef HAVE_SYSINFO
    if (sysinfo(SI_HOSTNAME, hostname, sizeof (hostname)) < 0) {
        perror("sysinfo");
#else
    if (gethostname(hostname, sizeof(hostname)) < 0) {
        perror("gethostname");
#endif
        exit(1);
    }
    if (strchr(hostname, '.'))
        *(strchr(hostname, '.')) = 0;

    sprintf(krb_ticket_name, "/var/dss/kerberos/tkt/tkt.%d", getpid());
    krb_set_tkt_string(krb_ticket_name);

    config_auth();

    if (krb_svc_init("hesiod", hostname, (char *) NULL, 0, (char *) NULL,
                     (char *) NULL) != KSUCCESS) {
        fprintf(stderr, "Couldn't initialize Kerberos\n");
        exit(1);
    }
}

void
end_krb()
{
    unlink(krb_ticket_name);
}
#endif /* KERBEROS */

#ifdef ULTRIX_AUTH
static int
ultrix_check_pass(char *passwd, char *xpasswd)
{
    struct svcinfo *svp;
    int auth_status;

    if ((svp = getsvc()) == (struct svcinfo *) NULL) {
        syslog(LOG_WARNING, "getsvc() failed in ultrix_check_pass");
        return -1;
    }
    if (pw == (struct passwd *) NULL) {
        return -1;
    }
    if (((svp->svcauth.seclevel == SEC_UPGRADE) &&
        (!strcmp(pw->pw_passwd, "*")))
        || (svp->svcauth.seclevel == SEC_ENHANCED)) {
        if ((auth_status=authenticate_user(pw, passwd, "/dev/ttypXX")) >= 0) {
            /* Indicate successful validation */
            return auth_status;
        }
        if (auth_status < 0 && errno == EPERM) {
            /* Log some information about the failed login attempt. */
            switch(abs(auth_status)) {
            case A_EBADPASS:
                break;
            case A_ESOFTEXP:
                syslog(LOG_NOTICE, "password will expire soon for user %s",
                    pw->pw_name);
                break;
            case A_EHARDEXP:
                syslog(LOG_NOTICE, "password has expired for user %s",
                    pw->pw_name);
                break;
            case A_ENOLOGIN:
                syslog(LOG_NOTICE, "user %s attempted login to disabled acct",
                    pw->pw_name);
                break;
            }
        }
    }
    else {
        if ((*pw->pw_passwd != '\0') && (!strcmp(xpasswd, pw->pw_passwd))) {
            /* passwd in /etc/passwd isn't empty && encrypted passwd matches */
            return 0;
        }
    }
    return -1;
}
#endif /* ULTRIX_AUTH */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.