muddleftpd-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Muddleftpd-cvs] CVSROOT: /cvsroot/muddleftpd


From: Beau Kuiper
Subject: [Muddleftpd-cvs] CVSROOT: /cvsroot/muddleftpd
Date: Tue, 04 Nov 2003 10:41:23 -0500

Module name:    muddleftpd
Branch:         dev-rugger-nat
Changes by:     Beau Kuiper <address@hidden>    03/11/04 10:41:22
Reply-to: address@hidden

CVSROOT:        /cvsroot/muddleftpd
Module name:    muddleftpd
Branch:         dev-rugger-nat
Changes by:     Beau Kuiper <address@hidden>    03/11/04 10:41:22

Modified files:
        .              : ChangeLog Makefile.defs NEWS 
        src            : auth.c cfloader.c checkip.c ftp.c ftpd.h 
                         proxy.c 

Log message:
        Added NAT and passive port range support, so passive connections work 
behind firewalls and NAT.
        
        Fixed a couple of bugs along the way

CVSWeb URLs:
http://savannah.gnu.org/cgi-bin/viewcvs/muddleftpd/muddleftpd/ChangeLog.diff?only_with_tag=dev-rugger-nat&tr1=1.9&tr2=1.9.2.1&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/muddleftpd/muddleftpd/Makefile.defs.diff?only_with_tag=dev-rugger-nat&tr1=1.1&tr2=1.1.12.1&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/muddleftpd/muddleftpd/NEWS.diff?only_with_tag=dev-rugger-nat&tr1=1.4&tr2=1.4.2.1&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/muddleftpd/muddleftpd/src/auth.c.diff?only_with_tag=dev-rugger-nat&tr1=1.2&tr2=1.2.4.1&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/muddleftpd/muddleftpd/src/cfloader.c.diff?only_with_tag=dev-rugger-nat&tr1=1.1&tr2=1.1.12.1&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/muddleftpd/muddleftpd/src/checkip.c.diff?only_with_tag=dev-rugger-nat&tr1=1.1&tr2=1.1.8.1&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/muddleftpd/muddleftpd/src/ftp.c.diff?only_with_tag=dev-rugger-nat&tr1=1.1&tr2=1.1.8.1&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/muddleftpd/muddleftpd/src/ftpd.h.diff?only_with_tag=dev-rugger-nat&tr1=1.2&tr2=1.2.4.1&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/muddleftpd/muddleftpd/src/proxy.c.diff?only_with_tag=dev-rugger-nat&tr1=1.1&tr2=1.1.8.1&r1=text&r2=text

Patches:
Index: muddleftpd/ChangeLog
diff -c /dev/null muddleftpd/ChangeLog:1.9.2.1
*** /dev/null   Tue Nov  4 10:41:23 2003
--- muddleftpd/ChangeLog        Tue Nov  4 10:41:21 2003
***************
*** 0 ****
--- 1,294 ----
+ 2003-11-04  Beau Kuiper <address@hidden>
+ 
+       * checkip.c: made the IPACLLIST structure/code more general, so it
+       can be used to store/decode information used to determine for NAT
+       support whether a host is on the local network or on the internet.
+ 
+       * auth.c: updated to use new ipacllist interface.
+ 
+       * cfloader.c: added code to load and check configuration for 
+       passive port ranges and NAT external IP settings. Fixed 2 minor
+       error path bugs in existing code. Updated to use the new ipacllist
+       interface.
+ 
+       * ftp.c: created the new function ftp_passive_do() that encapsulates
+       the functions of limiting port ranges and doing NAT IP translation for
+       passive FTP connections. Modified the passive code to use it.
+  
+       * ftpd.h: updated the IPACLLIST structure and ipacllist prototypes
+       for the more general code. Added pasvport_start, pasvport_last,
+       ipnat, ip_localips for NAT and passive port range support.
+ 
+       * proxy.c: updated to use new ipacllist interface. Also added code
+       to initalise the pseudo-random number generator after fork() has
+       occured, so each thread has its own properly initalised PRNG.
+ 
+ 2003-11-03  Joerg Jaspert  <address@hidden>
+ 
+       * modules/auth/authlibsmb/smbval/Makefile.in: Added CFLAGS and
+       -fPIC so this thing compiles on 64bit architectures too.
+ 
+       * modules/auth: authlibexample/Makefile.in,
+       authlibmud/Makefile.in, authlibsmb/Makefile.in,
+       authlibmysql/Makefile.in, authlibmsql/Makefile.in: Added -fPIC so
+       it actually compiles on different platforms.
+ 
+ 2003-11-02  Joerg Jaspert  <address@hidden>
+ 
+       * configure.ac, modules/auth/authlibmysql/configure.ac: Changed
+       way we look for mysql. Thats easier for users now.
+ 
+       * modules/auth/: authlibmsql/auth.h, authlibmysql/auth.h,
+       authlibsmb/auth.h, authlibmud/auth.h, authlibexample/auth.h:
+       Changed include from varargs.h to stdarg.h as gcc no longer
+       includes varargs.h
+ 
+       * modules/auth/: authlibmsql/README, authlibmysql/README,
+       authlibsmb/README, authlibmud/README, authlibexample/README:
+       Changed documentation to be in sync with new configure/makefile
+       things.
+ 
+       * modules/auth: authlibmsql/Makefile.in,
+       authlibmysql/Makefile.in, authlibexample/Makefile.in,
+       authlibsmb/Makefile.in, authlibmud/Makefile.in: Change Install
+       Target from TODO to something useful. Install the autlibfile in
+       libdir/muddleftpd set by configure run.
+       Also install the READMEs as README.auth$MODULE in the
+       documentation dir (see below).
+ 
+       * doc/Makefile.in: Added things to install txt documentation in
+       prefix/share/doc/muddleftpd/*
+ 
+ 2003-11-02  Beau Kuiper <address@hidden>
+ 
+       * src/ftpd.h: removed headers for string.h since they are in
+       string.h. Updated prototype for safe_vsnprintf().
+ 
+       * src/string.c: fixed double use of varargs in string_catvprintf().
+       This fixed muddleftpd problems on PPC and possibly other archs.
+ 
+       * src/string.h: updated prototype for string_catvprintf()
+ 
+       * src/ftpout.c: modified ftp_write to use fixed string_catvprintf()
+ 
+       * src/logger.c: modified debuglog() to use fixed safe_vsnprint()
+ 
+       * src/utils.c: fixed possible double use of varargs in safe_vsnprintf()
+       Fixed safe_snprintf() so it does not use safe_vsnprintf() to prevent
+       performance degredation due to new design.
+ 
+       * Configure.ac: Added a check for stdarg.h, since the replacement
+       snprintf code needs it.
+ 
+ 2003-04-15  Beau Kuiper <address@hidden>
+ 
+       * src/main.c: mainprog(): added code to close all file descriptors.
+       After the server is started, it will also close the terminal file
+       desciptors, so ssh sessions are not stalled at logout.
+ 
+       * src/utils.c: fd_closeall_nonterminal(): determines the max file
+       descriptors number and attempts to close all open fds. The terminal
+       file descriptors are left untouched.
+ 
+ 2002-11-20  Beau Kuiper <address@hidden>
+ 
+       * src/acl.c: transfer_config(): Added code that allows the server
+       to retrieve the gid a user will operate as from the password file.
+       Just put "!" in front of the gid parameter. (eg gid !anuser) will
+       give the user the gid of the user anuser.
+ 
+ 2002-10-29  Beau Kuiper <address@hidden>
+ 
+       * src/string.c: string_filterbadchars(): Added code to only allow
+       /r and /n characters if and only if they are together as a TELNET
+       newline (/r/n). This fixes the bug of muddleftpd accepting input
+       with those characters in it and then mucking the log files up with
+       it.
+ 
+       * src/logger.c: Removed code that replaces /r and /n with spaces.
+       It doesn't need it anymore, since I have cured the original bug.
+ 
+ 2002-10-21  Joerg Jaspert  <address@hidden>
+ 
+       * modules/auth/authlibexample/COPYING: Added back. This license
+       is different (a public domain one) and not GPL as I blindly
+       assumed from the name. Sorry Beau.
+ 
+ 2002-10-20  Joerg Jaspert  <address@hidden>
+ 
+       * NEWS: Added. Contains summaries of changes between releases.
+ 
+       * CHANGES: Removed, that is now in NEWS.
+ 
+       * modules/auth/: authlibmsql/README, authlibmysql/README,
+       authlibsmb/README: No need to mention that it depends
+       on muddleftpd. Of course it does, it is distributed with it. :)
+ 
+       * tools/muddlestats.0.3.0/: AUTHORS, COPYING: Removed.
+       AUTHORS integrated into the main AUTHORS, license is the same as
+       Muddleftpd, GPL.
+ 
+       * AUTHORS: Rewrote so it reflects current state of the Art.
+ 
+       * modules/auth/: authlibmysql/AUTHORS, authlibmysql/COPYING,
+       authlibsmb/AUTHORS, authlibsmb/COPYING,
+       authlibexample/AUTHORS, authlibexample/COPYING,
+       authlibmsql/AUTHORS, authlibmsql/COPYING,
+       authlibmud/AUTHORS, authlibmud/COPYING: Removed.
+       Authors is now integrated into the main AUTHORS file and license is
+       same as Muddleftpd.
+ 
+ 2002-10-20  Joerg Jaspert  <address@hidden>
+ 
+       * Developer.txt: Added Section about Changelogs/Commit
+       Messages.  Changed (C) for new Files to be for the Muddleftpd team.
+ 
+ 2002-10-20  Beau Kuiper  <address@hidden>
+ 
+       * modules/auth/: authlibexample/AUTHORS,
+       authlibexample/CHANGES, authlibexample/COPYING,
+       authlibexample/Makefile.in, authlibexample/README,
+       authlibexample/auth.h, authlibexample/authunix.c,
+       authlibexample/configure.ac, authlibmsql/AUTHORS,
+       authlibmsql/CHANGES, authlibmsql/COPYING,
+       authlibmsql/Makefile.in, authlibmsql/README,
+       authlibmsql/auth.h, authlibmsql/authmsql.c,
+       authlibmsql/configure.ac, authlibmud/AUTHORS,
+       authlibmud/CHANGES, authlibmud/COPYING,
+       authlibmud/Makefile.in, authlibmud/README,
+       authlibmud/auth.h, authlibmud/authmud.c,
+       authlibmud/configure.ac, authlibmysql/AUTHORS,
+       authlibmysql/CHANGES, authlibmysql/COPYING,
+       authlibmysql/Makefile.in, authlibmysql/README,
+       authlibmysql/auth.h, authlibmysql/authmysql.c,
+       authlibmysql/configure.ac, authlibsmb/AUTHORS,
+       authlibsmb/CHANGES, authlibsmb/COPYING,
+       authlibsmb/Makefile.in, authlibsmb/README,
+       authlibsmb/auth.h, authlibsmb/authsmb.c,
+       authlibsmb/configure.ac, authlibsmb/valid.h,
+       authlibsmb/smbval/Makefile.in, authlibsmb/smbval/byteorder.h,
+       authlibsmb/smbval/md4.c,
+       authlibsmb/smbval/rfcnb-common.h,
+       authlibsmb/smbval/rfcnb-error.h, authlibsmb/smbval/rfcnb-io.c,
+       authlibsmb/smbval/rfcnb-io.h,
+       authlibsmb/smbval/rfcnb-priv.h,
+       authlibsmb/smbval/rfcnb-util.c,
+       authlibsmb/smbval/rfcnb-util.h, authlibsmb/smbval/rfcnb.h,
+       authlibsmb/smbval/session.c,
+       authlibsmb/smbval/smbdes.c, authlibsmb/smbval/smbencrypt.c,
+       authlibsmb/smbval/smblib-common.h,
+       authlibsmb/smbval/smblib-priv.h,
+       authlibsmb/smbval/smblib-util.c, authlibsmb/smbval/smblib.c,
+       authlibsmb/smbval/smblib.h,
+       authlibsmb/smbval/std-defines.h,
+       authlibsmb/smbval/std-includes.h, authlibsmb/smbval/valid.c,
+       authlibsmb/smbval/valid.h: Finally added module code
+       into CVS
+ 
+ 2002-10-20  Beau Kuiper  <address@hidden>
+ 
+       * Makefile.in, autogen.sh, configure.ac,
+       defaults.h, defaults.h.in, install-sh: Imported
+       authentication modules into build system.
+       
+       Install does not work properly for modules, and there are still
+       some more tweaks to do
+ 
+ 2002-10-20  Joerg Jaspert  <address@hidden>
+ 
+       * Developer.txt: Added suggestions from Beau
+ 
+ 2002-10-19  Joerg Jaspert  <address@hidden>
+ 
+       * Developer.txt: First version of Guidelines for People
+       working on Muddleftpd
+ 
+ 2002-10-17  Beau Kuiper  <address@hidden>
+ 
+       * Makefile.in, config.h.in, src/version.c:
+       updated to autoconf 2.54
+ 
+ 2002-10-17  Beau Kuiper  <address@hidden>
+ 
+       * Makefile.in, configure, configure.ac,
+       configure.in: updated to autoconf 2.54
+ 
+ 2002-10-16  Beau Kuiper  <address@hidden>
+ 
+       * src/logger.c: Logger patch was broken
+       
+       Character constants are in ', not in " And character 20 is not a
+       space, while 0x20 is
+ 
+ 2002-09-28  Joerg Jaspert  <address@hidden>
+ 
+       * src/util/seteguid.c~: Well, it exists in the original
+       tarball, but its a backup file -> delete it :)
+ 
+ 2002-09-28  Joerg Jaspert  <address@hidden>
+ 
+       * src/util/: pwgrent.c, seteguid.c, seteguid.c~,
+       snprintf.c, strerror.c, strerror.h: Uhh, looks
+       like i missed to add them into CVS
+ 
+ 2002-09-26  Joerg Jaspert  <address@hidden>
+ 
+       * defaults.h: Ok, devel version is odd, stable is even.
+ 
+ 2002-09-26  Joerg Jaspert  <address@hidden>
+ 
+       * defaults.h: This is now 1.3.12 and no longer 1.3.11-devel
+ 
+ 2002-09-26  Joerg Jaspert  <address@hidden>
+ 
+       * src/logger.c: Remove \r and \n before output to logfile
+ 
+ 2002-09-26  Joerg Jaspert  <address@hidden>
+ 
+       * AUTHORS: Changed debian maintainer
+ 
+ 2002-09-26  Joerg Jaspert  <address@hidden>
+ 
+       * AUTHORS, CHANGES, COPYING, INSTALL,
+       Makefile.defs, Makefile.in, README, TESTING,
+       TODO, config.h.in, configure, configure.in,
+       defaults.h, doc/Makefile.in, doc/cookie.txt,
+       doc/ftpcmds.txt, doc/muddleftpd.txt,
+       doc/mudlogd.txt, doc/mudpasswd.txt, doc/reference.txt,
+       doc/info/muddleftpd.info, doc/info/muddleftpd.texi,
+       doc/man/ftpwho.1, doc/man/muddleftpd.1,
+       doc/man/mudpasswd.1, examples/README,
+       examples/anon-only.conf, examples/anon-reigon.conf,
+       examples/mudlogd.conf, examples/ratios.conf,
+       examples/standard.conf, examples/vserver-host.conf,
+       examples/vserver.conf, examples/vserverbig.conf,
+       src/Makefile.in, src/acl.c, src/acl.h, src/auth.c,
+       src/auth.h, src/authanon.c, src/authdisable.c,
+       src/authint.c, src/authpam.c, src/authunix.c,
+       src/bufread.c, src/cfloader.c, src/checkip.c,
+       src/cmd.c, src/config.c, src/configfile.h,
+       src/datasock.c, src/dir.c, src/file.c, src/ftp.c,
+       src/ftpcmd.h, src/ftpd.h, src/ftplist.c,
+       src/ftpout.c, src/ftpsite.c, src/ftpstat.c,
+       src/ftpstate.c, src/ftptrans.c, src/ftpwho.c,
+       src/init.c, src/logger.c, src/main.c,
+       src/malloc.c, src/mudlogd.c, src/mudpasswd.c,
+       src/myglob.c, src/myglob.h, src/newfile.c,
+       src/pnums.h, src/procnum.c, src/proxy.c,
+       src/ratio.c, src/ratio.h, src/ratiotool.c,
+       src/reply.h, src/select.c, src/shmem.c,
+       src/socket.c, src/string.c, src/string.h,
+       src/tokset.h, src/utils.c, src/version.c,
+       tools/muddlestats.0.3.0/AUTHORS,
+       tools/muddlestats.0.3.0/COPYING,
+       tools/muddlestats.0.3.0/INSTALL,
+       tools/muddlestats.0.3.0/README, tools/muddlestats.0.3.0/TODO,
+       tools/muddlestats.0.3.0/muddleftpd.logrotate,
+       tools/muddlestats.0.3.0/muddlestats-gather.py,
+       tools/muddlestats.0.3.0/muddlestats.lsm,
+       tools/muddlestats.0.3.0/muddlestats.py: Imported muddleftpd
+ 
+ 
+ Copyright (C) 2002 Muddleftpd Team, http://www.nongnu.org/muddleftpd/
+ Copying and distribution of this file, with or without modification, are
+ permitted provided the copyright notice and this notice are preserved.
Index: muddleftpd/Makefile.defs
diff -c /dev/null muddleftpd/Makefile.defs:1.1.12.1
*** /dev/null   Tue Nov  4 10:41:23 2003
--- muddleftpd/Makefile.defs    Tue Nov  4 10:41:21 2003
***************
*** 0 ****
--- 1,31 ----
+ # Copyright (C) 1999 Beau Kuiper
+ 
+ # This program is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+ # the Free Software Foundation; either version 2, or (at your option)
+ # any later version.
+ 
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ 
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+ 
+ # **** THINGS YOU CAN CHANGE!
+ 
+ # Use the new malloc implementation. Disable to use the system malloc
+ NEWMALLOC = 1
+ 
+ # Build a debug version. Don't use for production
+ DEBUG = 1
+ 
+ # Destination filenames
+ MUDOUTPROG = muddleftpd
+ WHOPROG = ftpwho
+ MUDPASSWDPROG = mudpasswd
+ RATIOTOOLPROG = ratiotool
+ MUDLOGD = mudlogd
+ 
Index: muddleftpd/NEWS
diff -c /dev/null muddleftpd/NEWS:1.4.2.1
*** /dev/null   Tue Nov  4 10:41:23 2003
--- muddleftpd/NEWS     Tue Nov  4 10:41:21 2003
***************
*** 0 ****
--- 1,978 ----
+ 
+ Version 1.3.13-dev-nat:
+ 
+       1) Bug Fix: Fixed checking of ftp ports to allow ports > 32768 to be
+                   used as the control port.
+       2) Bug Fix: Fixed vserverdefault undefined error message.
+       3) Change: Added port range configuration for Passive connections.
+                  This allows passive connections to work in firewalled or
+                  NAT environments.
+       4) Change: Added support for NAT. Added option ipnat for the user to
+                  specify their real external IP address, and the option
+                  nat_netclass to allow users to specify what IP's will see
+                  the servers internal addresses and what IP's will see
+                  the servers external address specified by ipnat option.
+ 
+ Version 1.3.13:
+ 
+       1) Change: Revamped build system to use new build tools.
+       2) Change: Included authentication modules into build system.
+                  They are installed (if selected at configure time) into
+                          the libdir from configure. Normally prefix/lib
+                (/usr/local/lib if not specified).
+       3) Bug Fix: Properly fixed bug in 1.3.12.1, so muddleftpd does not
+                   accept /r or /n from user input unless it is the form of
+                   a TELNET newline.
+       4) Bug Fix: Scrambled output on PPC systems fixed. (possibly other
+                   systems too)
+     5) Change: Install .txt documentation in prefix/share/doc/muddleftpd/*.
+ 
+ Version 1.3.12.1:
+ 
+       1) Bug Fix: Fixed logging of /r and /n so they are converted to
+                   spaces.
+ 
+ Version 1.3.12:
+ 
+       1) Bug Fix: Linux does not like custom PASV replies with ip-masq. 
+                   Using reply recommended (but not mandatory) in RFC959.
+                   (Beau Kuiper)
+       2) Change: Included support for large files (> 2 gig) on 32bit
+                  systems.
+       3) Change: Removed QUIET compile option, replaced by verbose
+                  parameter.
+       4) Change: Added support for HP-UX 11.00
+       5) Bug Fix: fixed warnings with GCC 3.X compiler
+ 
+ Version 1.3.11:
+ 
+       1) Change: Updated mudlogd. now sighup will reload config file.
+                  (Beau Kuiper)
+       2) Bug Fix: Overflowing port value allowed sending data to ports
+                   below 1024 dispite restrictions. (Beau Kuiper)
+       3) Bug Fix: Fixed storing files so they are truncated to the
+                   start position, like other ftpds. (Beau Kuiper)
+       4) Bug Fix: Removed races in the STOR and STOU commands. 
+                   (Beau Kuiper)
+       5) Bug Fix: Removed race in STOU unique filename generator.
+                   (Beau Kuiper)
+       6) Change: Minor optimizations, less malloc frobbing in some common
+                  code paths.
+ 
+ Version 1.3.10:
+ 
+       1) Bug Fix: Fixed warnings from gcc. (Beau Kuiper)
+       2) Change: Added %(x,*)d format for cookies. (Beau Kuiper)
+       3) Bug Fix: Fixed gid/uid handling, corrected casting,
+                   mainly useful for older machines with 16bit uid and gid
+                   (Beau Kuiper)
+       4) Bug Fix: Fixed manual page installation to put .1 at the end.
+                   (Decklen Foster)
+       5) Bug Fix: Fixed makefile problems. (Beau Kuiper)
+       6) Bug Fix: Fixed freeing of null pointers when specified configuration 
+                   file is a directory. (Beau Kuiper)
+       7) Bug Fix: Fixed gid/uid handling so NIS works. (Beau Kuiper)
+       8) Change: Now using new malloc library. It is much better than
+                  most other mallocs, including GNU's (Beau Kuiper)
+       9) Change: Added the package Muddlestats. It seems very good and
+                  has a promising future. (McRee)
+       10) Bug Fix: Fixed uploads on IBM AIX systems. This bug has existed
+                    a long time. (Beau Kuiper, helped by Sergey Novoselov)
+       11) Bug Fix: Fixed recursive directory listings again (Beau Kuiper,
+                    helped by Nik Soggia)
+       12) Documentation: Minor changes to example configs. (Beau Kuiper)
+       13) Documentation: Added mudlogd documentation. (Beau Kuiper)
+       14) Change: Added the mudlogd program, which works separatly from
+                   muddleftpd to allow simple log rotation. (Beau Kuiper)
+ 
+ Version 1.3.9:
+ 
+       1) Bug Fix: Connection rejections if DNS broken fixed. (Beau Kuiper)
+       2) Documentation: added mudpasswd manual page. (Decklen Foster)
+       3) Bug Fix: Better error reporting in ftpwho (Decklen Foster)
+       4) Documentation: Minor change to info page. (Decklen Foster)
+       5) Bug Fix: made forking into background a command line
+                   parameter instead of a #define (Beau Kuiper)
+       6) Change: added separate upload/download speed limiters for people
+                  with differing upload and download speeds, eg cable modem
+                  and ASDL. (Beau Kuiper)
+       7) Change: changed source tree directory structure, separating source 
+                  files into thier own directory. (Beau Kuiper)
+       8) Bug Fix: Fixed directory listing again, so even simpler and
+                   better. (Beau Kuiper)
+       9) Bug Fix: Small memory leak in listing code fixed. (Beau Kuiper)
+       10) Bug Fix: Finally fixed date parsing in list code so year is
+                    displayed if last modified was a different year.
+                    (Beau Kuiper)
+       11) Bug Fix: String length checking fixes for config files.
+                    (Tomas Ogren)
+       12) Bug Fix: Memory alignment issues fixed in configuration 
+                    include sections (Tomas Ogren)
+       13) Bug Fix: Some libc's don't like being told to vsnprintf
+                    without a buffer. Fixed autoconf script to
+                    check for this and use replacement if true.
+                    (reported by Tomas Ogren, fixed by Beau Kuiper)
+       14) Bug Fix: Moved HOST reply strings to reply.h (Beau Kuiper)
+       15) Change: added EPSV and EPRT commands, to support IPV6 at
+                   a later date. (Beau Kuiper)
+       16) Change: added busydumpdata directive option for main and 
+                   virtual server sections. Now the server won't blindly
+                   disconnect if too many users connect at the same
+                   time. (Idea by Tomas Ogren, done by Beau Kuiper)
+       17) Bug Fix: Fixed makefiles to use configure specified manpage
+                    and info directories. (Decklin Foster)
+       18) Bug Fix: Fixed ABOR to forget any data port information.
+                    (Beau Kuoper)
+       19) Bug Fix: Fixed default data port handling, with server using
+                    the wrong IP address. (Beau Kuiper)
+       20) Bug Fix: Fixed SITE error message with no parameters.
+                    (Beau Kuiper)
+ 
+ Version 1.3.8:
+ 
+       1) Bug Fix: Fixed bug in ratio record creation
+       2) Change: Added HOST support as outlined in the draft I have
+                  written.
+       3) Bug Fix: Minor fix for errorous exit. Now exits with 1 if an
+                   error occurs. Suggested by Decklin Foster
+       4) Bug Fix: More descriptive error messages for when the server
+                   has too many users.
+       5) Bug Fix: Recursive listing crashed muddleftpd if the last dir
+                   scanned but the recurse algorithm is empty.
+       6) Bug Fix: substantial fix to be more compatible with /bin/ls for
+                   recursive lists. Should fix mirror.pl
+       7) Bug Fix: upload resume using STOR command fixed. A very simple
+                   mistake that existed in the whole (1.3.X) series.
+       8) Bug Fix: Fixed free NULL pointer error when no ipacls specified.
+ 
+ Version 1.3.7:
+ 
+       1) Bug Fix: Listing ACL's did not work correctly.
+       2) Bug Fix: Major bug in freeing children caused major problems
+                   after server maxusers had logged in and out.
+       3) Bug Fix: Bind errors were being displayed when using runasuser
+                   directive. They are now ignored (they are harmless)
+       4) Bug Fix: SIGHUP is now not acted upon if runasuser is active.
+ 
+ Version 1.3.6:
+ 
+       1) Bug Fix: Fixed serious DOS bug when the new maxipconnect
+                   directive is used.
+ 
+ Version 1.3.5:
+ 
+       1) Change: Pam module now accepts option pam_user, to force
+                  the username it will read from the password file and
+                  return the credentials for.
+       2) Bug Fix: better logging of some failed calls.
+       3) Change: Now quitdump, cddump and welcome do not need to be
+                  specified specially for chroot mode.
+       4) Bug Fix: string.c changed for easier porting. Was GCC dependant.
+       5) Bug Fix: memcpy bug while resolving hostnames.
+       6) Bug Fix: Improper handling of uid, gid in authentication.
+       7) Bug Fix: Warnings repaired, prototypes rebuilt using scribe.
+       8) Change: Ftp list code now supports recursive listing. The listing
+                  code is very fast/memory efficient.
+       9) Bug Fix: Fixed memory leak while authenticating user.
+       10) Change: Added funtions to retrieve hostname/ip from
+                   authentication modules.
+       11) Change: I am using my own glob routines now, sorts correctly for
+                   machines without glob. 
+       12) Change: The fnmatch code I wrote is now being used regardless of
+                   whether the library has its own one.
+       13) Bug Fix: fnaccess and pfnaccess have been repaired, and can be
+                    used.
+       14) Change: Listing now shows total directory sizes like the real
+                   ls.
+       15) Bug Fix: Dumping relative files when changing directories caused
+                    the file for the last directory to be dumped
+       16) Bug Fix: ratiotool failed to set correct file permissions on
+                    a newly created ratio file.
+       17) Bug Fix: compile fixes for IRIX.
+       18) Bug Fix: Macros broken in ftpd.h
+       19) Bug Fix: Ips were shown backwards in ftpwho if reverse hostname
+                    was not found.
+       20) Change: Added option maxconnectperip. This limits the number of
+                   connections from a single IP address.
+       21) Bug Fix: Fix when user operation is more than 120 characters.
+                    This lead to ftpwho printing the hostname after the
+                    operation.
+       22) Bug Fix: cookies in access directives were not being applied 
+                    correctly.
+       23) Bug Fix: Signals must be blocked while reloading config, or
+                    deleting users, or bad things happen
+ 
+ Version 1.3.4:
+ 
+       1) Bug Fix: The internal password module was feeding the config
+                   error function the wrong variable.
+       2) Bug Fix: Fixed a bug in include handling that would prevent
+                   use in 64bit environments.
+       3) Bug Fix: Include looping is now checked before config file is
+                   loaded.
+       4) Change: Removed authparams option. Auth modules get to fend for
+                  themselves (this provides more functionality, and better
+                  directive names)
+       5) Bug Fix: Help was not being shown correctly.
+       6) Documentation: Updated some documentation.
+       7) Bug Fix: Possible to free NULL pointers in config.c
+       8) Change: Changed a few error messages to be more understandable.
+       9) Change: A temporary change that will lock up muddleftpd if it
+                  tries to free a NULL pointer. It locks muddleftpd up
+                  so a debugger can get a clean backtrace of the program.
+       10) Bug Fix: Fix for attempting to rotate logs while in droproot
+                    mode.
+       11) Bug Fix: Usernames are more rigourously checked because
+                    regular expression characters in accepted usernames
+                    may provide more access than intended.
+       12) Change: The anonymous authentication module now returns the uid
+                   and gid of nobody instead of the ftp user. This is
+                   more consistent with other ftp servers.
+       13) Change: Listing of devices is more accurate, showing the
+                   major and minor device numbers like real ls.
+       14) Change: Muddleftpd now uses the real hostname as the default
+                   hostname of the host and virtual servers.
+       15) Change: Ipacl for hostnames must be in brackets. Without
+                   brackets, it is always checked against the IP
+       16) Bug Fix: got rid of any use of inet_ntoa, since I wasn't
+                    using it right.    
+       17) Change: Set default logstrength to log everything.
+       18) Documentation: Documented all server directives.
+ 
+ Version 1.3.3:
+ 
+       1) Change: Can now use %v in group config to get current virtual
+                  server section name and %V to get current virtual server
+                  host name. Ratio %v and %V are now %a and %A
+       2) Change: Can now use tokens in greeting line.
+       3) Bug Fix: Fix for bug where directories with % in them did weird
+                   things (see version 1.2.10)
+       4) Change: New config option droproot. This option will drop all
+                  root prividges when a user logs. It also will use 
+                  capibilities so it can still use low numbered ports on
+                  linux 2.2 or better if libcap is installed.
+       5) Change: reply.h now contains macros to the replys the server
+                  generates, It allows replies for foriegn languages or
+                  other ftp servers to be used, basicly changing the
+                  persona of the ftp server.
+       6) Bug Fix: muddleftpd was not correctly enforcing the one command
+                   at a time requirement of FTP.
+       7) Bug Fix: muddleftpd was not updateing upload/download byte
+                   counter.
+ 
+ Version 1.3.2:
+ 
+       1) Bug Fix: Muddleftpd is much more careful now about the
+                   configuration file it listens to. The config file now
+                   must be owned by the user/group who runs it, and must
+                   not be world or group writable. This fixes a security
+                   vulnerability in ftpwho if it is setuid. Same for
+                   internal password files.
+       2) Change: Added the -n option to display IP's instead of hostnames
+                  in ftpwho.
+       3) Bug Fix: Fix for truncated replys where length of reply was more
+                   than 1024 characters.
+       4) Change: Implemented new directive include, which allows you to
+                  include another section in the current section. Good for
+                  creating templates.
+       5) Change: Changed how config errors were handled, improving error
+                  reporting and detection
+       6) Change: By default, muddleftpd is built to be quiet on startup.
+       7) Change: ratios now get data from groups section rather than a
+                  separate section. Use include to have ratio directives in 
+                  other sections.
+       8) Change: Sending SIGUSR1 to the server process will make it reopen
+                  the log file and instruct the children servers to do the
+                  same. Note that chrooted server children will not be able
+                  to reopen the log file.
+       9) Change: added fnmatch pattern based access controls, including
+                  controls to explicitly match slashes.
+       10) Bug Fix: Maximum process limit setting was broken, fixed.
+       11) Bug Fix: Symbolic links were always assumed readable in the
+                    listing code. Does not work because the /proc
+                    filesystem actually has non-world readable symbolic
+                    links.
+ 
+ Version 1.3.1:
+ 
+       1) Change: Minor changes to how the scratchfile is used.
+       2) Change: Muddleftpd now uses proper unix style arguments. It
+                  has a usage screen and a version option.
+       3) Change: PORT command more verbose, repling where it will connect
+                  to.
+       4) Change: root privliges are given for connecting to low number
+                  ports. This is for RFC951 complience.
+       5) Bug Fix: Fixed code so muddleftpd runs of FreeBSD. Borrowed :-)
+                   some code from proftpd (for functions fgetpwent and
+                   fgetgrent)
+       6) Bug Fix: snprintf replacement code has a bug causing very bad
+                   problems.
+       7) Bug Fix: removed TCP_CORK code since it only slows data transfers
+                   down.
+       8) Bug Fix: Fixed a few compile bugs.
+       9) Change: Changed log format and how log was written
+       10) Change: Added support of dlopen modules for authentication
+                   modules
+       11) Change: Minor updates to authentication module interfaces.
+       12) Change: Passwords used by anonymous users are now logged.
+       13) Bug Fix: Fixed %N cookie for user count.
+       14) Change: Got rid of pidfile support, now ftpwho -p will return
+                   the pid of the running standalone muddleftpd.
+       15) Change: Now scratchfile is used for locking in both inetd and
+                   standalone modes. This means the server doesn't need to
+                   lock against the config file and it could be changed
+                   without problems. 
+       16) Change: ftpwho program rewritten and much more useful now :-)
+       17) Bug Fix: A couple of missing returns fixed. Should have caused
+                    problems already :-)
+       18) Bug Fix: A bug in acl.c fixed where segfault would occur if no
+                    ACLs were specified for a user  
+       19) Change: Authentication modules can now cancel authentication of
+                   a user if a serious error occurs.
+       20) Change: Authentication is now cancelled if an authentication
+                   module cannot be found.
+       21) Change: If muddleftpd cannot bind to a port when it starts up,
+                   then it will exit. If it cannot bind to a port when
+                   you issue a SIGHUP, then it will log it and continue.
+       22) Change: Commands can now be disabled per group within the config
+                   file. 
+       23) Bug Fix: Data corruption bug after SIGHUP removes a virtual
+                    server fixed. 
+       24) Change: When a user connects, muddleftpd will fail to recognize
+                   all but a few commands needed to login.
+       25) Change: ftpwho reports vservers using their section names.
+       26) Change: Now download speed limits can be specified per group
+                   These are highly accurate and reasonably smooth.
+       27) Bug Fix: Porting fixes for weirder unixes including simple-glob
+                    fixes.
+       28) Bug Fix: Small bug parsing gid lists caused crashes with long
+                    suplemental gid lists.
+       29) Change: Speed limits now also apply to file lists and uploads.
+       30) Bug Fix: New listing code would possibly break when a partial write
+                    occured.
+       31) Change: Finished logindumpdata support.
+ 
+ Version 1.3.0:
+ 
+       1) Bug Fix: for large maxusers, the default settings would cause
+                   muddleftpd to croak. Fixed.
+       2) Bug Fix: for long usernames and non-default constant values, a
+                   possible problem with setting username has been fixed
+       3) Change: PAM module now does more logging to aid debugging.
+       4) Change: shared memory is more compact now, only storing thread
+                  users.
+       5) Change: EXPERIMENTAL INETD SUPPORT!!!!
+       6) Bug Fix: on systems that need -ldl for pam support, it would 
+                   cause pam support not to compile. Fixed.
+       7) Bug Fix: if muddleftpd is running inetd mode, now errormessages
+                   go to the system log.
+       8) Change: Log message now emmitted if non-existant auth method
+                  used.
+       9) Change: Better debug logging system now in place. Can work
+                  even with inetd
+       10) Bug Fix: Fix to MDTM. This broke netscape quite convincingly
+       11) Bug Fix: Fix for netscape race and maybe other clients.
+       12) Change: All configuration is in single file now! This improves
+                   performace muchly esp under load.
+       13) Documentation: Examples updated for new code!
+       14) Bug Fix: Fix to logging where SITE commands were logged twice.
+       15) Bug Fix: Listing bug fixed where files were being used as
+                    regular expressions.
+       16) Chnage: Replaced ACL code. New code also recognises mkdir and
+                   rmdir, chmod and chdir. Add only applies to files now.
+       17) Change: Now ip's can be denied as well as allowed with the new
+                   ipacl directive. Note that the order ipacl's are
+                   specified in is very important.
+       18) Change: Now log messages are generated when a user logs out
+                   of the server.
+       19) Bug Fix: User cannot specify PORT command with data port < 1024
+       20) Bug Fix: Total file size is now given when doing binary
+                    downloads, regardless of starting position. Other  
+                    ftp servers act this way, and some client expect it.
+       21) Bug Fix: Maximum timeout was not correctly set.
+       22) Change: Writing to control port now has timeout, so it cannot
+                   lock up.
+       23) Change: Continuation messages have new format that look nicer.
+       24) Change: New group system is working, It now traverses a list
+                   of groups in the user file to determine what group a
+                   user is. It needs more work ATM.
+       25) Change: added IP and name access lists to make group system
+                   very, very, very configurable.
+       26) Bug Fix: For systems which uses non-DES encryption, muddleftpd
+                    used crypt correctly.
+       27) Change: added hostname based matching and hostnames in logs. 
+       28) Change: reverse dns timout setting added
+       29) Change: Alternate long reply format option added, allows for bad
+                   proxys and stuff.
+       30) Change: VIRTUAL SERVER SUPPORT ADDED. Not yet completed or
+                   optimised for file descriptor usage.
+       31) Bug Fix: Fixed several small bugs found in 1.2.2.
+       32) Bug Fix: A minor bug working with parameters fixed.
+       33) Bug Fix: A bug when parsing config files caused tabs to be
+                    misinterpreted.
+       34) Change: Smart binding. This allow muddleftpd to minimize the
+                   number of file descriptors used when binding files.
+       35) Change: Zero binding. When used with smart binding above, allows
+                   virtual servers to be used on all servers. This basicly
+                   makes sure all binds are performed on address 0.0.0.0
+       36) Change: Using rlimits instead of manually checking memory size
+                   for determining overuse of memory.
+       37) Change: Made single configuration file loading to be less memory
+                   intensive.
+       38) Change: Server will now reload config file with a HUP signal.
+       39) Change: Server now lets you specify if you want to real chroot 
+                   to the rootdir instead of doing a pretend chroot. It is
+                   ignored if root access isn't avaliable. This provides
+                   extreme security for paranoid system administrators
+                   (like me!)
+       40) Change: When a user logs in, the uid/gid is switched to that
+                   user instead of being left as root. This reduces code
+                   size and improves security. I also seem to learn things
+                   the hard way (I should have had this ages ago)
+       41) Change: Ratio support. An external binary file is used for this,
+                   and users are added to it as they log in. Ratio's are
+                   implimented so multiple sessions are no problem. Ratios
+                   apply to both files and bytes, and can be persistant or
+                   non-persistant.
+       42) Change: Config changes. Now parameters are easier to specify,
+                   and including spaces is easy.
+       43) Change: By default, muddleftpd now doesn't allow access to non
+                   regular files (eg device files). To re-enable use
+                   devaccess in group sections.
+       44) Change: Full group support, include supplementry group lists.
+       45) Change: Added a ratio changing program, ratiotool. It can add,
+                   edit and psudo-delete ratio file entries. It is safe to
+                   use while the server is running.
+       46) Bug fix: Fixed a small bug that always put an extra / at the
+                    beginning of a filename in the log if rootdir is /
+       47) Change: If rootdir contains symlinks, they are now dereferenced
+                   as the user logs in.
+       48) Change: Added a realdir option. This will automaticly report the
+                   real directory name, with all symlinks dereferenced,
+                   instead of the internal construction.
+       49) Change: Added code to detect broken snprintf, and to work out
+                   which syntax it uses and use that syntax.
+       50) Change: Detects if double ints work correctly and disables
+                   ratios if they don't
+       51) Change: Added fxpallow option, so server-server transfers may
+                   work on a per-group basis.
+       52) Change: Implemented output buffer to make sure long replies are
+                   sent effeciently.
+       53) Change: Input from dumped files and user is now put through a
+                   filter that removes non-printable characters.
+       54) Change: Implemented options to specify long replies directly in
+                   the config file
+       55) Change: Data port handling rewritten, should be a little faster
+                   now.
+       56) Change: Sendfile() support implemented. This should improve 
+                   performace slightly, but linux needs a better sendfile :(
+       57) Change: Groups and vserver limits are handled in a much better
+                   way now. Faster for standalone operation, correct for
+                   inetd operation.
+       58) Change: Log now contains thread id so it is easier to trace what
+                   a specific user is doing
+ 
+ Version 1.2.10:
+ 
+       1) Bug Fix: Bug where % in pathname would cause weird errors when
+                   current directory was displayed. It was caused by using
+                   the output of one printf (with user supplied data)
+                   as the format string in the next. Fixed by removing all
+                   (except where it is obviously safe) non-constant format
+                   strings in the source. Entire source was checked for the
+                   problem.
+ 
+ Version 1.2.9:
+ 
+       1) Bug Fix: Mudpasswd backported from 1.3.X. Fixes problems with
+                   mudpasswd segfaulting.
+       2) Change: Included an init file for system V init scripts
+       3) Chnage: Added a spec file for rpm generation. Havn't tested it
+                  yet.
+ 
+ Version 1.2.8:
+       
+       1) Bug Fix: Another config file reading bug fixed. If you used tabs
+                   at the end of a value, they would not be discarded by
+                   the ftp server.
+       2) Bug Fix: Repaired the README file.
+ 
+ Version 1.2.7:
+ 
+       1) Bug Fix: Rename bug fixed. Ops :-) Occured because errno wasn't
+                   reset correctly.
+ 
+ Version 1.2.6:
+ 
+       1) Bug Fix: Fixed bug where garbage was printed if null password
+                   was supplied.
+ 
+ Version 1.2.5:
+ 
+       1) Bug Fix: snprintf madness fixed. Server now works for all known
+                   snprintf types.
+       2) Bug Fix: fixed the provided snprintf so it reports -1 if result
+                   is too long.
+       3) Bug Fix: ftpwho would segfault in certain denied access cases.
+ 
+ Version 1.2.4:
+ 
+       1) Bug Fix: Parsing problems with tab separating name and value
+                   pairs fixed. 
+       2) Change: Ftpwho program revamped. It can now do reverse IP lookups
+                  for hostnames and supports regular options. Run ftpwho -h
+                  to see options.
+       3) Bug Fix: Problems with zero length strings on ftp commands fixed.
+                   No real problems, just zero length parameters need to
+                   be detected and reported as missing parameters.
+       2) Bug Fix: A secuity problem with cddump relative files has been
+                   fixed. If the user could create a symlink as the name
+                   of a relative cddump file, the user could view any file
+                   on the system over the control connection.
+ 
+ Version 1.2.3 (was 1.1.1c)
+ 
+       1) Bug Fix: Active data connections now bind to specific interface.
+                   Old method seemed to fail when used with strict ip
+                   filewalls.
+       2) Bug Fix: If an error occured accepting a control connection, the
+                   server would bad.
+       3) Bug Fix: If the PASV command couldn't bind to a port, it wouldn't
+                   cleanly report the error.
+       4) Bug Fix: An extra close was hiding the real cause of data port
+                   connection failure.
+       5) Bug Fix: Some calls to create sockets were not checked correctly.
+       6) Bug Fix: No error was displayed if passive port connection 
+                   accept fails
+       7) Bug Fix: Slight change in Makefile so that CFLAGS is recognised.
+ 
+ Version 1.1.1b (now 1.2.2):
+ 
+       1) Bug Fix: if you didn't set authmethod in the config file, the
+                   server process would crash.
+       2) Bug Fix: if you use a non-DES crypt function, muddleftpd would
+                   not authenticate properly. This bugfix fixes for a least
+                   slackware 7.0.
+       3) Change: New versioning system. Now I copy the linux versioning
+                  system since I cannot get it right myself.
+       4) Bug Fix: if a list failed due that was not the fault of glob,
+                   then server would crash tring to free a non-existant
+                   variable:
+       5) Change: You can use group/usernames for uid/gid specification now
+                  in user config files and authentication modules.
+ 
+ Version 1.1.1a (now 1.2.1):
+ 
+       1) Bug Fix: Long usernames would cause the server to crash. Not an
+                   exploitable bug, but not very nice looking.
+       2) Bug Fix: files dumped over control connection were not logged
+                   correctly.
+ 
+ Version 1.1.1 (now 1.2.0):
+ 
+       1) Bug Fix: A minor memory leak was sealed.
+       2) Bug Fix: Changed ACL error code to EACCES instead of
+                   EPERM.
+       3) Documentation: Added the info file back.
+       4) Change: log entry generated when a user logs out of the ftp
+                  server
+       5) Bug Fix: Server will not allow client to specify a data port
+                   less than 1024.
+       6) Bug Fix: Changed byte count when starting download to total size
+                   of file regardless of restart position. Other ftp
+                   servers act this way, and some clients use this info.
+       7) Change: When authenticating users, the server will change to
+                  the configuration directory. This will improve
+                  robustness of marginally defined configurations.
+       8) Bug Fix: Maximum timeout was not set correctly when logging user
+                   in.
+       9) Bug Fix: Minor fixes for platform independance in mudpasswd.
+ 
+ Version 1.1.1beta3:
+ 
+       1) Bug Fix: invalid rootdir message didn't show root dir.
+       2) Bug Fix: fix for site commands being logged twice.
+       3) Documentation: info files removed for now since maintainer
+                         hasn't reponded to email.
+       4) Bug Fix: Directory listing problems solved.
+ 
+ Version 1.1.1beta2:
+ 
+       1) Bug Fix: Pam support didn't compile on system with dynamic linking
+                   in a library
+       2) Bug Fix: Small change to code to prevent problems if username
+                   length constants get changed.
+       3) Bug Fix: Fix to MDTM command, broke netscape access quite well :)
+       4) Bug Fix: Fix for netscape race and maybe other clients.
+ 
+ Version 1.1.1beta1:
+ 
+       1) Change: Changed the error message management system. Now much
+                  better.
+       2) Change: Help now contains descriptions for each command.
+       3) Change: Full shadow password support with account expiry.
+       4) Change: User now gets message as to why login fails.  
+       5) Bug Fix: strerror was compiled even if it already existed!
+       6) Change: PAM support has been added!
+       7) Bug Fix: now all root logins get demoted to user nobody like they
+                   should
+       8) Bug Fix: now user nobody from the password file is used to
+                   determine what user nobody is. Any system without a user
+                   nobody is in SERIOUS TROUBLE!
+       9) Change: Added the disabled user authentication which will disable
+                  a user.
+       10) Bug Fix: Added a copy of the usleep function for older computers.
+       11) Documentation: Fixed documentation for new features.
+       12) Bug Fix: Put limits on maximum generated string lengths to
+                    prevent users from filling virtual memory.
+       13) Change: Added SITE IDLE command.
+ 
+ Version 1.1.1beta0:
+ 
+       1) Change: Log message emitted when user cannot login due to bad
+                  root dir
+       2) Change: Error messages for socket connection are better handled.
+       3) Change: added -d option to list a directory instead of its
+                  contents.
+       4) Change: Major refit of auth code. Now all auth code is modularied
+                  into the different sources. Makes adding new auth schemes
+                  easily.
+       5) Change: new unix auth module can autodetect shadow passwords!
+       6) Change: config files can handle windows style files.
+       7) Change: added an internal password file support module.
+       8) Change: added runasuser option in main config file.
+       9) Change: removed runasuid and runasgid from main config file.
+       10) Change: created a simple mudpasswd program to modify internal
+                   password files.
+       11) Change: Minor server reply tweeks.
+       12) Documentation: Fixed examples for new code.
+       13) Change: Added the badauthwait option to the main config file to 
+                   cause the server to wait a specifed number of milliseconds 
+                   if the user doesn't provide the correct password.
+       14) Change: Added the userjail option to user files so when the user
+                   logs in, he/she will not be able to log into a new user.
+                   Good for anonymous accounts.
+       15) Change: Added the logintrys option to the main config file. This
+                   tells the server how many attempts a connection has to
+                   login before the server refuses to log the user in.
+ 
+ Version 1.1:
+ 
+       1) Documentation: Added man page and info pages. Thanks to 
+                         Jonas Oberg <address@hidden> for this.
+       2) Bug Fix: Spelling mistake in init.c fixed. Thanks to
+                   Jonas Oberg <address@hidden> for noticing my mistake.
+       3) Change: Server will now detect if it needs root access to bind to a
+                  port and gives a message to the user if it is so.
+                   
+ Version 1.1.beta3: Final beta if no bugs are found!
+ 
+       1) Bug Fix: Fixed Makefile so QUIET=1 option is recognised.
+       2) Bug Fix: Fixed spelling mistake in init.c (already instead of 
+                   aleady)
+       3) Documentation: Added some documentation on how to build and install
+                         muddleftpd (muddleftpd.txt)
+       4) Documentation: Updated README and INSTALL.
+       5) Bug Fix: If unixuser is specified and that specified user doesn't
+                   exist, login will fail automaticly.
+       6) Bug Fix: If it can't load a user's config file, it will now
+                   output a log message.
+       7) Bug Fix: Checks on files and directories now done in user auth
+                   files.
+       8) Change: Minor optimisations to improve processing of file
+                  dumping, streamlines code.
+       9) Bug Fix: busydump was crashing, due to incorrect working
+                   directory value, fixed!
+       10) Bug Fix: the username in the shared memory structure is properly
+                    updated at the correct time. Allows a user with
+                    maxconnect 1, to relogin.
+       11) Documentation: Added documentation for user configuration files.
+       12) Bug Fix: now mud authenication stuff is not compiled by default.
+       13) Documentation: Added documentation for cookies in user config
+                          files.
+       14) Documentation: Added documentation for accepted ftp commands.
+       15) Documentation: Gave up on HTML documentation!
+ 
+ Version 1.1.beta2:
+ 
+       1) Change: Failed magic cookie tranformations are logged so that the
+                  admin can find if cookies fail. (I decided that this
+                  change is minor enough and important enough to be added 
+                  before version 1.1)
+       2) Bug Fix: Download statistics were not correctly tallyed.
+       3) Documentation: Added documentation for dumped file magic cookies
+                         (cookies.txt)
+       4) Documentation: Added documentation for ftpwho. (ftpwho.txt)
+       5) Documentation: changed user.other so that if the example config
+                         is directly used, it won't open gaping holes in the
+                         system.
+       6) Bug Fix: Server dies improperly on startup if shared memory isn't 
+                   initalized
+       7) Bug Fix: Commented out error message about shadow passwords not
+                   working as root. Change 1 should produce a log entry if
+                   the user tries to use shadow passwords without root
+                   access.
+       8) Documentation: Created a new set of examples, that are more secure,
+                         and better for creating a full configuration.
+       9) Bug Fix: Updated socket.c, now if the server fails binding data
+                   connections for any reason, it lets the computer choose
+                   the port.
+ 
+ Version 1.1.beta1: Thanks to Mike Javorski <address@hidden> for the
+                  first 3 fixes.
+ 
+       1) Bug Fix: Removed SO_SNDLOWAT and SO_RCVLOWAT log messages because
+                   they are unimportant and seem to be annoying people :)
+                   The feature is still there, it will just fail quietly on
+                   systems that have problems with it.
+       2) Bug Fix: Manual page installation fixed (but commented out becuase
+                   the manual page is yet to be written.
+       3) Bug Fix: Fixed ftpwho.c for ANSI C complience. Now main returns 
+                   an int. 
+       4) Bug Fix: Defined prototypes for listing functions and found a bug
+                   where they wern't supplied correct parameters.
+       5) Bug Fix: Server now checks if you are already logged in before 
+                   accepting a password command.
+       6) Bug Fix: Ops, I must have been on drugs to not have noticed this
+                   bug before now (and write the broken code). SIZE in ascii 
+                   mode was badly broken.
+       7) Bug Fix: SITE CHMOD was broken and logged out the client
+       8) Bug Fix: HELP missed the first command when printing out a
+                   command list.
+       9) Bug Fix: When using runasuid/runasgid, the parent terminal holder
+                   would not get killed due to the process not having 
+                   permission after changing uid.
+       10) Bug Fix: When using runasuid/runasgid, and listening on ports
+                    less than 1024, muddleftpd would fail to create data
+                    ports because it couldn't bind to the port.
+       11) Bug Fix: prototypes in ftpd.h for procnum.c fixed.
+       12) Bug Fix: ftpwho.c now includes <unistd.h> as required.
+       13) Bug Fix: fixed compiler warning compiling sglob.c on systems
+                    without glob.
+       14) Bug Fix: Prototype for strerror if the system hasn't got an
+                    strerror
+       15) Bug Fix: Fixed problems about double defining crypt
+       16) Bug Fix: -lsocket and -lnsl should work for machines that need
+                    them now.
+ 
+ Version 1.1.beta0:
+ 
+       1) Bug Fix: forkwrapper is declared int instead of pid_t in ftpd.h
+                   fixed.
+       2) Bug Fix: On systems that don't support GLOB_PERIOD, compile will
+                   fail, fixed.
+       3) Documentation: Added some documentation for the main config file, 
+                         in doc/config.txt
+       4) Bug Fix: Compile warning fixed for ftp.c
+       5) Bug Fix: Fixed race condition in starting up program where tty
+                   could hang if the parent recieved the signal to
+                   give it back too soon.
+       6) Documentation: Added some HTML documentation (just a quick 
+                         conversion of doc/config.txt)
+       
+ Version 1.1.alpha5: Imposing a feature freeze for Version 1.1. Only bugfixes
+                   and documentation from here.
+ 
+       1) Change: Magic cookies when authing clients is now handled more
+                  gracefully. No user side changes. This allows Change 6.
+       2) Bug Fix: Empty listing caused memory leak. Fixed.
+       3) Change: Server now holds the terminal until after the startup
+                  messages so the prompt occurs after it.
+       4) Change: Startup message fixed for correctness.
+       5) Bug Fix: pids are now type pid_t, gids are now type gid_t, and
+                   uids are now type uid_t in my program. They were ints
+                   before!.
+       6) Change: Now magic cookies work in dumped files (eg welcome file)
+                  so you can give info out. The cookies are similar to the
+                  real wu.ftpd cookies.
+       7) Change: Added email option in config file for magic cookie %E.
+       8) Change: Now you can bind to a port to a specific interface by
+                  using port/ip as a parameter to ftpport. eg to bind 
+                  port 21 on the loopback device use "ftpport 21/127.0.0.1"
+                  in the config file.
+ 
+ Version 1.1.alpha4:
+ 
+       1) Change: SITE command now operates similarly to normal command. No
+                  user visible changes.
+       2) Change: Messages have changed so that the code is more general.
+       3) Change: STAT ACCESS has been moved over the SITE ACCESS.
+       4) Change: help routine is more generalized, giving better results,
+                  less code and more maintainablity.
+       5) Change: Now I use config.h instead of lots of compile options.
+                  Thanks to Decklin Foster <address@hidden> for the
+                  patch.
+       6) Change: The pidfile is used to check if muddleftpd is still
+                  running. If it is, muddleftpd refuses to start. Note, you
+                  can still have multiple instances of muddleftpd if they
+                  run with different config files.
+       7) Bug Fix: config file name was not initalized properly.
+       8) Change: Added a new user config option (quitdump) that prints a
+                  file when the user quits.
+ 
+ Version 1.1.alpha3:
+ 
+       1) Bug Fix: New parameter bug where process will segfault if it
+                   cannot correctly substitue a %(a,b)h parameter.
+       2) Change: Ftp listing is better now, it recognises the -a option
+                  and displays all files. This improves interpolability
+                  with misbehaving clients.
+       3) Change: I removed the status reporting option and the code that
+                  does it.
+       4) Change: Shared memory used by muddleftpd can now be externally
+                  accessed. (for things like ftpwho)
+       5) Change: Added a simple ftpwho program that actually works
+                  without races by accessing the shared memory.
+       6) Change: The default config file is now specified in config.h
+ 
+ Version 1.1.alpha2:
+ 
+       1) Change: Changed the status reporting so it now dumps info into a
+                  file that can be read by other programs.
+       3) Change: The admin now has the option to create a pidfile as
+                  muddleftpd starts.
+       2) Change: Changed the way parameters are processed. Now you can
+                  select a range of characters to include. eg %(10,12)h 
+                  means to substitute characters 10 through 12 into the
+                  string. The old %h still works.
+ 
+ Version 1.1.alpha1:
+ 
+       1) Change: Added simple shadow password support
+       2) Change: Changed the way listing works. Simpler, easier
+       3) Change: now STAT <filename> works.
+       4) Change: A list cannot be longer than MAXLISTSIZE
+       5) Bug Fix: A long running LIST -n ./ bug has been fixed. It caused
+                   an embarrasing infinite loop. Been in there for ages.
+                   A big thanks to Richard(address@hidden) for the 
+                   tipoff.
+       6) Bug Fix: Resume position should not be allowed to be negative.
+ 
+ Version 1.1.alpha0:   * means ported back to 1.0
+       
+       1)*Bug Fix: Rename fixed again to check if RNFR was run before RNTO
+       2) Change: The main routine now uses the select.c services.
+       3) Change: The server can bind to many ports, and serve ftp over
+                  them
+       4)*Bug Fix: Memory leak in displaying symbolic links in listing.
+       5) Change: Internal list function now displays setuid/setgid and
+                  sticky bit. Rewrote to be much smaller.
+       6)*Bug Fix: Fixed ENOTDIR error to becone MYERR_NODIR instead of
+                   MYERR_NOFILE in file.c
+       7)*Bug Fix: Minor changes to error descriptions in ftp.c
+       8) Change: ~ works for going to your home directory.
+       9) Change: Download/Upload now don't memmove buffer.
+       10) Change: make a substitute glob using old BSD re_comp calls
+                   for very old systems.
+       11)*Bug Fix: Uid/Gid cache full causes problems. 
+       12) Change: for passive downloads, makes sure the host that connects
+                   to the dataport is also the host that the control port
+                   is connected to
+       13)*Bug Fix: Obscure bug in ftplist.c fixed!
+       14) Change: The server sets the high and low water marks for the
+                   sockets on opening a data connection to improve
+                   performace.
+       15) Change: Added the STOU command!
+       16) Change: Added the ability to type a file before the user logs in
+         17) Change: Added the ability to type a file when the user enters a
+                   directory.
+       18) Change: The welcome file is now an absolute filename instead of
+                   relative to rootdir
+       19)*Bug Fix: Timeout didn't get updated properly when user logged on.
+       20) Change: Added timeout to STAT command.
+       21) Change: Added the APPE command!
+       22) Change: Added the ALLO command. Does nothing.
+       23) Change: Added the ACCT command. Does nothing.
+       24)*Bug Fix: The server didn't kill children ftpd processes. Now it
+                    does.
+       25)*Bug Fix: If you didn't specify a groupname in a user file, the
+                    server would crash.
+       26) Change: Changed maxthreads directive to maxusers.
+       27) Change: Cleaned up reading of commands.
+       28) Change: Started to use assertions to debug code.
+       29) Bug Fix: Server full log message not logged correctly.
+       30) Change: select.c adjusted to work better.
+       31) Bug Fix: server made 2 replys on logout.
+       32) Bug FIx: server can handle lots of dataconnections now.
+       33) Change: now too many login message is configurable per user
+                   using the busydump directive.
+       34) Change: ASCII upload resume disabled. It is not actually
+                   possible to this safely. Use the APPE command instead.
+       35) Change: Changed data collection to use shared memory rather than
+                   pipes. Faster, easier to access and allows future
+                   changes.
+       36) Change: Error messages now output the filename and line the
+                   error occured on.
+       37) Change: I am starting to use assersions to test features, find
+                   bugs.
+       38) Bug Fix: Server exits if it can't write to the log file, i.e
+                    disk full or other fatal error.
+       39) Change: Passive is handled better througth the main select
+                   handler.
+       40) Change: A simple strerror replacement has been thrown in for
+                   unlucky systems that don't have it.
+       41) Bug Fix: High and low water marks are only set if the constants
+                    for them exist. See 14.
+       42) Bug Fix: --prefix parameter in configure works as expected.
+ 
+ Version 1.0: These fixed are ported back from version 1.1.alpha0.
+ 
+       1) Bug Fix: very obscure and rare bug fixed in ftplist.c 
+       2) Bug Fix: Memory leak fixed in listing symbolic links.
+       3) Bug Fix: Rename fixed to check if the command RNFR was
+                   run first.
+       4) Bug Fix: Fixed ENOTDIR to become MYERR_NODIR instead of
+                   MYERR_NOFILE in file.c 
+       5) Bug Fix: Modified error descriptions in error.c 
+       6) Bug Fix: Fixed problems with full UID/GID caches.
+       7) Bug FIx: Timeout wasn't updated when user logged in. 
+       8) Bug Fix: children processes are now automaticly killed when 
+                   you kill the parent. 
+       9) Change: Changed maxthreads to maxusers in the config file
+       10) Bug Fix: If you don't specify a groupname in the user config 
+                    file, then the server will crash. 
+ 
+ Version 1.0.beta3:
+ 
+       1) Bug Fix: Buffers in acl.c fixed. Checked the rest of the code for
+                   possible buffer problems.
+       2) Bug Fix: stats.c buffers fixed. (not really a problem)
+       3) Bug Fix: pnums.c buffers fixed. (not really a problem)
+       4) Bug Fix: ftplist.c buffers fixed. (not really a problem)
+       5) Bug Fix: Rename properly fixed. For some reason it worked with
+                   the bug, and that worries me a little!
+       6) Change: reentrant.c (old name for original threaded version)
+                  cleaned up a little.
+ 
+ Version 1.0.beta2:
+ 
+       1) Bug Fix: QUIT stopped working on 1.0.beta1, now fixed. 
+       2) Bug Fix: Buffers in auth.c fixed completely.
+       3) Bug Fix: newfile.c has a realloc bug in it. ie it used a
+                   pointer to the buffer after remalloc was run, and since
+                   realloc may move the data to a new location, it was
+                   unstable but difficult to debug!
+       4) Bug Fix: a couple of memory over-freeing bugs fixed.
+       5) Change: Symbolic links listed properly.
+       
+ Version 1.0.beta1:
+ 
+       1) Bug Fix: unimplemented type would print response code twice.
+       2) Bug Fix: %U wasn't recognized unless unixuser was specified.
+       3) Bug Fix: Permanent group changes specified in config file would
+                   not work.
+ 
+       4) Change: FTP server now runs data transfers (almost) completely
+                  non-blocking. Timeout and ABOR work properly. ASCII
+                  resume works now too.  
+       5) Change: Most of the code has been rewritten to use dynamic
+                  buffers, removing most problems with fixed buffer code.
+       6) Change: cdup, pwd, and cwd all use the same code now, thus all
+                  return the current directory
+       7) Change: mkdir now uses the 257 return code.
+       8) Change: all commands now give better error messages based on
+                  the real error, not the most likely error.
+ 
+ Version 1.0.beta0:
+ 
+       1) First Version with CHANGES file.
Index: muddleftpd/src/auth.c
diff -c /dev/null muddleftpd/src/auth.c:1.2.4.1
*** /dev/null   Tue Nov  4 10:41:23 2003
--- muddleftpd/src/auth.c       Tue Nov  4 10:41:21 2003
***************
*** 0 ****
--- 1,642 ----
+ /* auth.c Handle user authentication and setup of user options
+ 
+    Copyright (C) 1999 Beau Kuiper
+ 
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+ 
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+ 
+ #include "ftpd.h"
+ #include "auth.h"
+ 
+ extern FTPCMD mainftpcmd[];
+ extern FTPCMD siteftpcmd[];
+ 
+ PERMINFO authtypes[] = {
+       { "unix", &unixauth_commands },
+       { "anonymous", &anonauth_commands },
+       { "internal", &internalauth_commands },
+       { "disabled", &disableauth_commands },
+ #ifdef HAVE_PAM_START
+       { "pam", &pamauth_commands },
+ #endif
+       { NULL, NULL },
+ };
+ 
+ /* this converts a config file string to discribe a multiline response to a
+    real string. This does not need a new string to copy it to */
+ 
+ void converttorealstr(char *strbuf)
+ {
+       char *pos1, *pos2;
+       
+       pos1 = pos2 = strbuf;
+       
+       while(*pos1 != 0)
+       {
+               if (*pos1 == '/')
+               {
+                       pos1++;
+                       switch(*pos1)
+                       {
+                       case 'n':
+                               *pos2 = '\n';
+                               pos2++;
+                               break;
+                       case 's':
+                               *pos2 = ' ';
+                               pos2++;
+                               break;
+                       case 't':
+                               *pos2 = '\t';
+                               pos2++;
+                               break;
+                       case '/':
+                               *pos2 = '/';
+                               pos2++;
+                               break;
+                       default:
+                               *pos2 = '/';
+                               pos2++;
+                               pos1--;
+                               break;
+                       }       
+               }
+               else
+               {
+                       *pos2 = *pos1;
+                       pos2++;
+               }
+               pos1++;
+       }               
+       *pos2 = 0;
+ }
+ 
+ /* check an encrypted password against a plain text password */
+ 
+ int chkpassword(char *encrypass, char *password)
+ {
+       char *pass2;
+       int result = FALSE;
+       
+       pass2 = crypt(password, encrypass);
+       if (strcmp(pass2, encrypass) == 0)
+               result = TRUE;
+ 
+       /* destroy the passwords so it cannot be seen if muddleftpd crashes! */
+       memset(password, 0, strlen(password));
+       memset(pass2, 0, strlen(pass2));
+       
+       return(result);
+ }
+ 
+ /* Make sure the user isn't trying to exploit the nature of the ftp server */
+ 
+ int checkexploits(FTPSTATE *peer)
+ {
+       int count;
+       int namelen = strlen(peer->username);
+       
+       if (strlen(peer->username) > MAXNAMELEN)
+               return(TRUE);
+       for(count = 0; count < namelen; count++)
+       {
+               switch ((peer->username)[count])
+               {
+                       case '?':
+                       case '*':
+                       case '/':
+                       case '[':
+                       case ']':
+                       case '\\':
+                       case '!':
+                       case ':':
+                       case '^':
+                               return(TRUE);
+               }
+       }       
+       return(FALSE);
+ }
+ 
+ int authlogmsg(char *directive, char *groupname)
+ {
+       log_giveentry(MYLOG_INFO, NULL, safe_snprintf("directive %s is not 
correct in group %s", directive, groupname));
+       return(FALSE);
+ }     
+ 
+ int checkabsdir(char *dir)
+ {
+       if (dir == NULL)
+               return(TRUE);
+       return(dir[0] == '/');
+ }     
+               
+ void clearauth(FTPSTATE *peer)
+ {
+       freeifnotnull(peer->pwd);
+       peer->pwd = NULL;
+       freeifnotnull(peer->homedir);
+       freeifnotnull(peer->basedir);
+       if (peer->passiveport) select_delfd(peer->sel, peer->passiveport);
+       peer->passiveport = 0;  
+       freeifnotnull(peer->renameoldname);
+       peer->renameoldname = NULL;
+       peer->restartpos = 0;
+       peer->binary = TRUE;
+       peer->remoteport = peer->connport - 1;
+       freeifnotnull(peer->logindump);
+       freeifnotnull(peer->cwddump);
+       freeifnotnull(peer->busydump);
+       freeifnotnull(peer->quitdump);
+       freeifnotnull(peer->logindumpdata);
+       freeifnotnull(peer->cwddumpdata);
+       freeifnotnull(peer->busydumpdata);
+       freeifnotnull(peer->quitdumpdata);
+       freeifnotnull(peer->cmddisableset);
+       freeifnotnull(peer->sitedisableset);
+       peer->sitedisableset = disableset_create();
+       peer->cmddisableset = disableset_create();
+       peer->umask = peer->vserver->umask;
+       if (peer->acldata) acllist_dest(peer->acldata);
+       peer->gidt_asgid = config->gidt_nobodygid;
+       peer->uidt_asuid = config->uidt_nobodyuid;      
+       peer->maxusers = peer->vserver->maxusers;
+       peer->maxtimeout = peer->vserver->timeout;
+       peer->timeout = peer->vserver->timeout;
+       peer->chmodable = FALSE;
+       peer->jailenabled = FALSE;
+       peer->droproot = FALSE;
+       peer->fakemode = -1;
+       peer->accessdevices = FALSE;
+       peer->fxpallow = FALSE;
+       peer->maxtranspd = 0;
+       freeifnotnull(peer->fakename);
+       freeifnotnull(peer->fakegroup);
+       if (peer->ratioinfo) ratio_finish(peer->ratioinfo);
+       freeifnotnull(peer->supgids);
+ }
+ 
+ char *mkconfrealstr(int sectionid, char *item, char *defaul)
+ {
+       char *setting;
+       
+       loadstrfromconfig(config->configfile, sectionid, item, &(setting), 
defaul);
+       if (setting == NULL)
+               return(NULL);
+       
+       setting = strdupwrapper(setting);
+       
+       converttorealstr(setting);
+       return(setting);
+               
+       return(setting);
+ }
+ 
+ char *mktokconfstr(TOKENSET *tset, int sectionid, char *item, char *defaul)
+ {
+       char *setting;
+       
+       loadstrfromconfig(config->configfile, sectionid, item, &(setting), 
defaul);
+       if (setting == NULL)
+               return(NULL);
+       
+       setting = strdupwrapper(setting);               
+       setting = tokenset_apply(tset, setting, FALSE);
+       return(setting);
+ }
+ 
+ int mktokconfint(TOKENSET *tset, int sectionid, char *item, char *format,
+                char *defaultstr, int defaultint)
+ {
+       char *setting;
+       int ret;
+       
+       loadstrfromconfig(config->configfile, sectionid, item, &(setting), 
defaultstr);
+       if (setting == NULL)
+               return(defaultint);
+       setting = strdupwrapper(setting);
+       setting = tokenset_apply(tset, setting, FALSE);
+       if (sscanf(setting, format, &ret) != 1)
+               ret = defaultint;
+       freewrapper(setting);
+       return(ret);
+ }
+ 
+ void buildtokset(TOKENSET *tset, PERMSTRUCT *s, void *a)
+ {
+       gid_t *list;
+       uid_t inuid;
+       gid_t ingid;
+       int result;
+       char *str;
+       
+       if (s->getuseruid)
+       {
+               inuid = s->getuseruid(a);
+               result = (int)inuid;
+               str = safe_snprintf("%d", result);
+               tokenset_settoken(tset, 'u', str);
+       }
+       
+       if (s->getusergid)
+       {
+               ingid = s->getusergid(a);
+               result = (int)ingid;
+               str = safe_snprintf("%d", result);
+               tokenset_settoken(tset, 'g', str);
+       }
+       
+       if (s->gethomedir)
+               tokenset_settoken(tset, 'h', strdupwrapper(s->gethomedir(a)));
+       if (s->getrootdir)
+               tokenset_settoken(tset, 'r', strdupwrapper(s->getrootdir(a)));
+       if (s->getusersupgid)
+       {
+               list = s->getusersupgid(a);
+               tokenset_settoken(tset, 'G', makegidliststr(list));
+               freewrapper(list);
+       }
+ }     
+ 
+ void setupacls(FTPSTATE *peer, TOKENSET *tset, int section, char *funcname, 
int aclfunc)
+ {
+       char *data;
+       int occur = 1;
+       int escapetoks = (aclfunc != 0);
+       
+       while((data = getconfigdata(config->configfile, section, funcname, 
occur)))
+       {
+               char *pos;
+ 
+               data = tokenset_apply(tset, strdupwrapper(data), escapetoks);
+               if ((pos = strrchr(data, ':')) != NULL)
+               {
+                       /* this will temporaryly damage config data. But
+                          I fix it afterwards :) */
+                       *pos = 0;
+                       acllist_add(peer->acldata, data, pos + 1, aclfunc);
+               }
+               
+               freewrapper(data);
+               
+               occur++;
+       }
+ }
+ 
+ int transferconfig(FTPSTATE *peer, TOKENSET *tset, int section)
+ {
+       int occur, result = TRUE;
+       char *data;
+       int intdata;
+       
+       peer->timeout = mktokconfint(tset, section, "timeout", "%d", NULL, 
peer->vserver->timeout);
+       peer->maxtimeout = peer->timeout;
+ 
+       /* get uid setting */
+       loadstrfromconfig(config->configfile, section, "uid", &(data), "%u");
+       data = strdupwrapper(data);
+       data = tokenset_apply(tset, data, FALSE);
+       if (sscanf(data, "%d", &intdata) != 1)
+       {
+               /* try for username then */
+               struct passwd *pwdent;
+               pwdent = getpwnam(data);
+               if (pwdent)
+                       peer->uidt_asuid = pwdent->pw_uid;
+               else
+                       peer->uidt_asuid = config->uidt_nobodyuid;
+       }
+       else
+       {
+               peer->uidt_asuid = (uid_t)intdata;
+       }
+       freewrapper(data);
+ 
+       /* get gid setting */
+       loadstrfromconfig(config->configfile, section, "gid", &(data), "%g");
+       data = strdupwrapper(data);
+       data = tokenset_apply(tset, data, FALSE);
+       
+       if (sscanf(data, "%d", &intdata) != 1)
+       {
+               /* if it starts with a "!" mark, get gid of username */
+               if (data[0] == '!')
+               {
+                       struct passwd *pwdent;
+                       pwdent = getpwnam(data+1);
+                       if (pwdent)
+                               peer->gidt_asgid = pwdent->pw_gid;
+                       else
+                               peer->gidt_asgid = config->gidt_nobodygid;
+               }
+               else    
+               {
+                       /* try for groupname then */
+                       struct group *grent;
+                       grent = getgrnam(data);
+                       if (grent)
+                               peer->gidt_asgid = grent->gr_gid;
+                       else
+                               peer->gidt_asgid = config->gidt_nobodygid;
+               }
+       }
+       else
+       {
+               peer->gidt_asgid = (gid_t)intdata;
+       }
+       
+       freewrapper(data);
+ 
+       if ((int)peer->uidt_asuid == 0)
+               peer->uidt_asuid = config->uidt_nobodyuid;
+       if ((int)peer->gidt_asgid == 0)
+               peer->gidt_asgid = config->gidt_nobodygid;
+ 
+       peer->homedir = mktokconfstr(tset, section, "homedir", "%h");
+       if (!checkabsdir(peer->homedir))
+               result = authlogmsg("homedir", peer->groupname);
+ 
+       peer->basedir = mktokconfstr(tset, section, "rootdir", "%r");
+       if (!checkabsdir(peer->basedir))
+               result = authlogmsg("rootdir", peer->groupname);
+ 
+ 
+       peer->umask = mktokconfint(tset, section, "umask", "%o", NULL, 
peer->vserver->umask) & 0777;
+       peer->chmodable = mktokconfint(tset, section, "chmoding", "%d", NULL, 
0);
+       peer->jailenabled = mktokconfint(tset, section, "userjail", "%d", NULL, 
0);
+       peer->maxusers = mktokconfint(tset, section, "maxusers", "%d", NULL, 
config->defaults->maxusers);
+       peer->maxtranspd = mktokconfint(tset, section, "maxspeed", "%d", NULL, 
0);
+       peer->maxtranspd_down = mktokconfint(tset, section, "maxspeeddown", 
"%d", NULL, 0);
+       peer->maxtranspd_up = mktokconfint(tset, section, "maxspeedup", "%d", 
NULL, 0);
+       peer->chroot = mktokconfint(tset, section, "chroot", "%d", NULL, FALSE);
+       peer->droproot = mktokconfint(tset, section, "droproot", "%d", NULL, 
FALSE);
+ 
+       peer->nicevalue = mktokconfint(tset, section, "nice", "%d", NULL, 0);
+       peer->accessdevices = mktokconfint(tset, section, "devaccess", "%d", 
NULL, FALSE);
+       peer->realdir = mktokconfint(tset, section, "realdir", "%d", NULL, 
FALSE);
+       peer->fxpallow = mktokconfint(tset, section, "fxpallow", "%d", NULL, 
FALSE);
+       
+       peer->fakemode = mktokconfint(tset, section, "fakemode", "%o", NULL, 
-1);
+       peer->fakename = mktokconfstr(tset, section, "fakename", NULL);
+       peer->fakegroup = mktokconfstr(tset, section, "fakegroup", NULL);
+ 
+       peer->cwddumpdata = mkconfrealstr(section, "cddumpdata", NULL);
+       peer->logindumpdata = mkconfrealstr(section, "welcomedumpdata", NULL);
+       peer->quitdumpdata = mkconfrealstr(section, "quitdumpdata", NULL);
+       peer->busydumpdata = mkconfrealstr(section, "busydumpdata", NULL);
+ 
+       if (!peer->cwddumpdata)
+               peer->cwddump = mktokconfstr(tset, section, "cddump", NULL);
+ 
+       if (!peer->logindumpdata)
+       {
+               peer->logindump = mktokconfstr(tset, section, "welcome", NULL);
+               if (!checkabsdir(peer->logindump))
+                       result = authlogmsg("welcome", peer->groupname);
+       }
+       
+       if (!peer->quitdumpdata)
+       {
+               peer->quitdump = mktokconfstr(tset, section, "quitdump", NULL);
+               if (!checkabsdir(peer->quitdump))
+                       result = authlogmsg("quitdump", peer->groupname);
+       }
+       
+       if (!peer->busydumpdata)
+       {
+               peer->busydump = mktokconfstr(tset, section, "busydump", NULL);
+               if (!checkabsdir(peer->busydump))
+                       result = authlogmsg("busydump", peer->groupname);
+       }
+ 
+       peer->acldata = acllist_create();
+ 
+       setupacls(peer, tset, section, "fnaccess", 1);
+       setupacls(peer, tset, section, "pfnaccess", 2);
+       setupacls(peer, tset, section, "access", 0);
+       
+       occur = 1;
+       while((data = getconfigdata(config->configfile, section, "cmdoff", 
occur++)))
+               disableset_disablecmd(peer->cmddisableset, mainftpcmd, data);
+       occur = 1;
+       while((data = getconfigdata(config->configfile, section, "sitecmdoff", 
occur++)))
+               disableset_disablecmd(peer->sitedisableset, siteftpcmd, data);
+ 
+       if (mktokconfint(tset, section, "ratios", "%d", NULL, FALSE))
+               peer->ratioinfo = ratio_loaduser(peer->username, section);
+       else
+               peer->ratioinfo = NULL;
+ 
+       data = mktokconfstr(tset, section, "supgid", "%G");
+       peer->supgids = parsegidlist(data);
+       freewrapper(data);
+       
+       return(result);
+ }
+ 
+ int auth_getcursectionid(FTPSTATE *peer)
+ {
+       return(getsectionid(config->configfile, peer->groupname));
+ }
+ 
+ void *auth_getconfigcache(void)
+ {
+       return(config->configfile);
+ }
+ 
+ char *auth_getremotename(FTPSTATE *peer)
+ {
+       return(peer->hostname);
+ }
+ 
+ unsigned int *auth_getremoteip(FTPSTATE *peer)
+ {
+       return(&(peer->remoteip));
+ }
+ 
+ 
+ PERMSTRUCT *configauthmethod(char *group, char *authmethod)
+ {
+       int count = 0;
+       static PERMSTRUCT dp;
+ 
+ #ifdef HAVE_DLOPEN
+       if (authmethod[0] == '/')
+       {
+               /* assume it is a libary module */
+ #ifndef RTLD_NOW
+               dp.handle = dlopen(authmethod, 1);
+ #else
+               dp.handle = dlopen(authmethod, RTLD_NOW);
+ #endif
+               if (dp.handle == NULL)
+               {
+                       log_giveentry(MYLOG_INFO, NULL, safe_snprintf("could 
not open or link '%s', reason: %s", authmethod, dlerror()));
+                       return(NULL);
+               }
+               dp.gethandle = dlsym(dp.handle, "gethandle");
+               dp.freehandle = dlsym(dp.handle, "freehandle");
+               dp.chkpasswd = dlsym(dp.handle, "chkpasswd");
+               dp.gethomedir = dlsym(dp.handle, "gethomedir");
+               dp.getrootdir = dlsym(dp.handle, "getrootdir");
+               dp.getuseruid = dlsym(dp.handle, "getuseruid");
+               dp.getusergid = dlsym(dp.handle, "getusergid");
+               dp.getusersupgid = dlsym(dp.handle, "getusersupgid");
+ 
+               /* make sure the required functions exist and are defined. */
+               if (!dp.gethandle || !dp.freehandle || !dp.chkpasswd)
+               {
+                       log_giveentry(MYLOG_INFO, NULL, safe_snprintf("linked 
module '%s' incomplete", authmethod));
+                       dlclose(dp.handle);
+                       return(NULL);
+               }
+               return(&dp);
+       }
+ #endif
+       while(authtypes[count].authname != NULL)
+       {
+               if (strcasecmp(authmethod, authtypes[count].authname) == 0)
+                       return(authtypes[count].authstruct);
+               count++;
+       }
+       
+       log_giveentry(MYLOG_INFO, NULL, safe_snprintf("authmethod '%s' does not 
exist, used by group %s", authmethod, group));
+       return(NULL);
+ }
+ 
+ /* this finds the section the user belongs to */
+ 
+ char *auth_getgroup(FTPSTATE *peer, TOKENSET *tset, int *section, PERMSTRUCT 
**rprog, void **rinf)
+ {
+       int count = 0;
+       int sectionid, result;
+       char *mode, *authuser;
+       char **grouplist = peer->vserver->grouplist;
+       PERMSTRUCT *am = NULL;
+       void *handle = NULL;
+       
+       while(grouplist[count] != NULL)
+       {
+               sectionid = getsectionid(config->configfile, grouplist[count]);
+               if (sectionid == -1)
+               {
+                       /* stop authenticating if error occurs */
+                       log_giveentry(MYLOG_INFO, NULL, safe_snprintf("group %s 
does not exist", grouplist[count]));
+                       return(NULL);
+               }
+               else
+               {
+                       authuser = mktokconfstr(tset, sectionid, "authuser", 
peer->username);
+                       mode = getconfigdata(config->configfile, sectionid, 
"authmethod", 1);
+                       if (mode)
+                               am = configauthmethod(grouplist[count], mode);
+                       if (am)
+                       {
+                               /* Check ip */
+                               IPACLLIST *clist;
+                               
+                               clist = ipacllist_new(config->configfile, 
sectionid, "ipacl", 'A', FALSE);
+                               if (!ipacllist_query(clist, peer->remoteip, 
peer->hostname))
+                                       am = NULL;
+                               ipacllist_destroy(clist);
+                       }
+                       else
+                               return(NULL);
+                       if (am)
+                               /* Check username */
+                               if (!checknamelist(config->configfile, 
sectionid, peer->username))
+                                       am = NULL;
+                       if (am)
+                       {
+                               /* now see if valid user */
+                               peer->groupname = grouplist[count];
+                               handle = am->gethandle(peer, tset, authuser, 
&result);
+                               /* if the authentication module returns ERROR, 
return now! */
+                               if (result == AUTH_ERROR)
+                               {
+                                       freewrapper(authuser);
+                                       return(NULL);   
+                               }
+                       }
+                       
+                       if (handle)
+                       {
+                               *section = sectionid;
+                               *rprog = (void *)am;
+                               *rinf = handle;
+                               freewrapper(authuser);
+                               return(grouplist[count]);
+                       }
+                       freewrapper(authuser);
+               }
+               count++;
+       }
+       return(NULL);
+ }
+ 
+ char *setuseropts(FTPSTATE *peer, char *password)
+ {
+       int section;
+       void *authhandle = NULL;
+       char *errstr = NULL;
+       PERMSTRUCT *authcmd;
+       TOKENSET *tset = tokenset_new();
+       
+       /* Check possible exploits here! */ 
+       if (checkexploits(peer))
+               goto authend;           
+ 
+       peer->loggedin = FALSE;
+       tokenset_settoken(tset, 'U', strdupwrapper(peer->username));
+       tokenset_settoken(tset, 'v', strdupwrapper(peer->vserver->sectionname));
+       tokenset_settoken(tset, 'V', strdupwrapper(peer->vserver->vhostname));
+       
+       peer->groupname = auth_getgroup(peer, tset, &section, &authcmd, 
&authhandle);
+ 
+       if (peer->groupname == NULL)
+               goto authend;
+       
+       if (!authcmd->chkpasswd(authhandle, password, &errstr))
+               goto authend;
+       
+       clearauth(peer);
+       buildtokset(tset, authcmd, authhandle);
+        
+       if (!transferconfig(peer, tset, section))
+               goto authend;
+       
+       peer->loggedin = TRUE;
+ 
+ authend:
+       tokenset_finish(tset);
+       if (authhandle)
+       {
+               authcmd->freehandle(authhandle);
+ #ifdef HAVE_DLOPEN
+               if (authcmd->handle)
+                       dlclose(authcmd->handle);
+ #endif
+       }
+       
+       if (peer->loggedin)
+       {
+               freeifnotnull(errstr);
+               return(NULL);
+       }
+       else
+       {
+               if (!errstr)
+                       errstr = strdupwrapper("Bad password");
+               return(errstr);
+       }
+ }
Index: muddleftpd/src/cfloader.c
diff -c /dev/null muddleftpd/src/cfloader.c:1.1.12.1
*** /dev/null   Tue Nov  4 10:41:23 2003
--- muddleftpd/src/cfloader.c   Tue Nov  4 10:41:22 2003
***************
*** 0 ****
--- 1,629 ----
+ /* cfloader.c Configuration loader
+ 
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+ 
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+ 
+ #include "ftpd.h"
+ #include "reply.h"
+ 
+ VSERVER serverdefaults = 
+ {
+       NULL,
+       MAXUSERS,
+       LOGSTRENGTH,
+       TIMEOUT,
+       LOGFILE,
+       EMAIL,
+       LOGINTRIES,
+       BADAUTHWAIT,
+       0,
+       0,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL
+ };    
+ 
+ IPACL nat_locallist[] =
+ {
+       /* 127.0.0.0 Local interface */
+       { 0x7F000000, 0xFF000000, NULL, 2, TRUE },
+       /* 192.168.0.0/16 Private address space */
+       { 0xC0A80000, 0xFFFF0000, NULL, 2, TRUE },
+       /* 10.0.0.0/8 Private address space */
+       { 0x0A000000, 0xFF000000, NULL, 2, TRUE },
+       /* 172.16.0.0/12 Private address space */
+       { 0xAC100000, 0xFFF00000, NULL, 2, TRUE },
+ };
+ 
+ IPACLLIST nat_localdefault =
+ {
+       4,              /* count */
+       0,              /* defualt is notlocal */
+       nat_locallist
+ };
+ 
+ char *gethostname2(void)
+ {
+       char *hostname;
+       int size = 32;
+       struct hostent *h;
+       
+       hostname = mallocwrapper(size);
+       while(gethostname(hostname, size-1) == -1)
+       {
+               size *= 2;
+               reallocwrapper(size, (void **)&hostname);
+       }
+       
+       h = gethostbyname(hostname);
+       freewrapper(hostname);
+       if (!h)
+               return(NULL);
+       hostname = strdupwrapper(h->h_name);
+ 
+       return(hostname);
+ }
+ 
+ /* generates a busy string from config file data */
+ 
+ char *makebusystring(char *input, int alt)
+ {
+       char *r, *n;
+       
+       r = mallocwrapper(strlen(input) + ((strchrcount(input, '/') + 1) * 6) 
+                         + strlen(REPLY_SERVERBUSY) + 2);
+       
+       strcpy(r, input);
+       
+       /* do / translation */
+       converttorealstr(r);
+       
+       /* insert inital 421 */
+       memmove(r + 4, r, strlen(r) + 1);
+       memcpy(r, "421-", 4);
+ 
+       /* insert further 421 for each line */
+       n = r;
+       while((n = strchr(n, '\n')) != NULL)
+       {
+               int len = strlen(n);
+               memmove(n + 5, n, len + 1);
+               if(alt)
+                       memcpy(n, "\r\n421-", 6);
+               else
+                       memcpy(n, "\r\n    ", 6);
+               n = n + 2;
+       }
+       
+       /* insert server message */
+       strcpy(r + strlen(r), "\r\n");
+       strcpy(r + strlen(r), REPLY_SERVERBUSY);
+ 
+       return(r);
+ }
+ 
+ void configerror(char *str)
+ {
+       switch(logerrors)
+       {
+               case SYSLOG:
+                       syslog(LOG_ERR, PROGNAME":%s", str);
+                       break;
+               case TERMINAL:
+                       fprintf(stderr, "CONFIG: %s\n", str);
+                       break;
+               case MUDLOG:
+                       log_addentry(MYLOG_INFO, NULL, str);
+       }
+       freewrapper(str);
+ }
+ 
+ void vserver_kill(VSERVER *c)
+ {
+       if (c->ipaccess)
+               ipacllist_destroy(c->ipaccess);
+       freeifnotnull(c->prelogindumpdata);
+       freeifnotnull(c->grouplist);
+       freeifnotnull(c->toobusy);
+       freewrapper(c);
+ }
+ 
+ VSERVER *vserver_load(CONFIGDATA *cf, char *sectionname, VSERVER *def, 
VSERVERCONN ***vp)
+ {
+       int section, occur;
+       char *setting;
+       VSERVER *newvs = mallocwrapper(sizeof(VSERVER));
+       VSERVER *dvs = def;
+       
+       if (!dvs)
+               dvs = &serverdefaults;
+       newvs->grouplist = NULL;
+       newvs->ipaccess = NULL;
+       newvs->sectionname = sectionname;
+ 
+       section = getsectionid(cf->configfile, sectionname);
+       if (section == -1)
+       {
+               configerror(safe_snprintf("Cannot get %s section in config 
file!", sectionname));
+               vserver_kill(newvs);
+               return(NULL);
+       }
+       
+       loadintfromconfig(cf->configfile, section, "maxusers", 
+                         &(newvs->maxusers), dvs->maxusers);
+       loadintfromconfig(cf->configfile, section, "logstrength",
+                         &(newvs->loglevel), dvs->loglevel);
+       loadintfromconfig(cf->configfile, section, "logintries",
+                         &(newvs->logincount), dvs->logincount);
+       loadintfromconfig(cf->configfile, section, "timeout",
+                         &(newvs->timeout), dvs->timeout);
+       loadintfromconfig(cf->configfile, section, "umask",
+                         &(newvs->umask), dvs->umask);
+       loadintfromconfig(cf->configfile, section, "maxconnectperip",
+                         &(newvs->maxperip), dvs->maxperip);
+ 
+       if (newvs->logincount == 0)
+               newvs->logincount = -1;
+       loadintfromconfig(cf->configfile, section, "badauthwait",
+                         &(newvs->authwait), dvs->authwait / 1000);
+       newvs->authwait *= 1000;
+       
+       loadstrfromconfig(cf->configfile, section, "logfile",
+                         &(newvs->logfile), dvs->logfile);
+       loadstrfromconfig(cf->configfile, section, "busydumpdata",
+                         &setting, NULL);
+       if (setting)
+               newvs->toobusy = makebusystring(setting, cf->altlongreplies);
+       else if (dvs->toobusy)
+               newvs->toobusy = strdupwrapper(dvs->toobusy);
+       else
+               newvs->toobusy = NULL;
+       
+       loadstrfromconfig(cf->configfile, section, "email",
+                         &(newvs->email), dvs->email);
+       loadstrfromconfig(cf->configfile, section, "greeting",
+                         &(newvs->greetline), dvs->greetline);
+       loadstrfromconfig(cf->configfile, section, "hostname",
+                         &(newvs->vhostname), dvs->vhostname);
+       if (newvs->vhostname == NULL)
+               newvs->vhostname = cf->hostname;
+       loadstrfromconfig(cf->configfile, section, "logindump",
+                         &(newvs->prelogindump), dvs->prelogindump);
+       
+       loadstrfromconfig(cf->configfile, section, "logindumpdata",
+                         &(newvs->prelogindumpdata), dvs->prelogindumpdata);
+       
+       /* if prelogindumpdata is there, allocate it by itself and then
+          convert it to a displayable string */
+       if (newvs->prelogindumpdata)
+       {
+               newvs->prelogindumpdata = 
strdupwrapper(newvs->prelogindumpdata);
+               converttorealstr(newvs->prelogindumpdata);
+       }       
+       newvs->grouplist = makeconfiglist(cf->configfile, sectionname, "group");
+       newvs->ipaccess = ipacllist_new(cf->configfile, section, "ipacl", 'A', 
FALSE);
+ 
+       if (vp == NULL)
+               return(newvs);
+               
+       occur = 1;
+       while((setting = getconfigdata(cf->configfile, section, "ftpport", 
occur)) != NULL)
+       {
+               char *bindip = strchr(setting, '/');
+               **vp = mallocwrapper(sizeof(VSERVERCONN));
+ 
+               if (bindip == NULL)
+                       getnetworkint("0.0.0.0", &((**vp)->ip));
+               else
+               {
+                       *bindip = 0;
+                       getnetworkint(++bindip, &((**vp)->ip));
+               }
+ 
+               sscanf(setting, "%d", &((**vp)->port));
+               (**vp)->fd = 0;
+               (**vp)->vptr = newvs;
+               *vp = &((**vp)->next);
+               occur++;
+       }
+ 
+       return(newvs);
+ }
+ 
+ void ftpd_killconfig(CONFIGDATA *dc)
+ {
+       if (dc->configfile)
+               freeconfigcache(dc->configfile);
+       freeifnotnull(dc->vserverlist);
+       freeifnotnull(dc->hostname);
+       if (dc->vservers)
+       {
+               VSERVER *c = dc->vservers;
+               while(c != NULL)
+               {
+                       VSERVER *d = c->next;
+                       vserver_kill(c);
+                       
+                       c = d;
+               }
+       }
+       if (dc->inports)
+       {
+               VSERVERCONN *c = dc->inports;
+               while (c != NULL)
+               {
+                       VSERVERCONN *d = c->next;
+                       freewrapper(c);
+                       c = d;
+               }
+       }
+       if (dc->defaults)
+               vserver_kill(dc->defaults);
+       if (dc->nat_localips)
+               if (dc->nat_localips != &nat_localdefault)
+                       ipacllist_destroy(dc->nat_localips);
+       freewrapper(dc);
+ }
+ 
+ /* this loads the configuration into a new configdata structure. Checking does
+    not need to be done at this point. Do not mess with the current config 
+    because this can be called due to a signal while the FTP server is live
+ */
+ 
+ CONFIGDATA *ftpd_loadconfig(char *inidata, int as_inetd, int use_umask)
+ {
+       CONFIGDATA *newconfig = mallocwrapper(sizeof(CONFIGDATA));
+       int section, occur, line, error;
+       struct passwd *userinfo;
+       char *setting;
+       VSERVERCONN **vscpos;
+       VSERVER **vspos, *vpos;
+       
+       newconfig->configfile = NULL;
+       newconfig->vserverlist = NULL;
+       newconfig->vservers = NULL;
+       newconfig->inports = NULL;
+       
+       newconfig->inetd = as_inetd;
+       newconfig->parentpid = getpid(); 
+       newconfig->defaults = NULL;
+       newconfig->defaulthost = 0;
+       newconfig->nat_localips = NULL;
+ 
+       userinfo = getpwnam("nobody");
+       if (userinfo == NULL)
+       {
+               ftpd_killconfig(newconfig);
+               configerror(strdupwrapper("Cannot find uid/gid for user 
nobody!"));
+               return(NULL);
+       }
+ 
+       newconfig->hostname = gethostname2();
+       if (!(newconfig->hostname))
+       {
+               ftpd_killconfig(newconfig);
+               configerror(strdupwrapper("Could not resolve hostname for local 
machine."));
+               return(NULL);
+       }
+ 
+       newconfig->gidt_nobodygid = userinfo->pw_gid;
+       newconfig->uidt_nobodyuid = userinfo->pw_uid;
+       
+       newconfig->configfile = loadconfigcache(inidata, &line, &error);
+       
+       if (!(newconfig->configfile))
+       {
+               configerror(safe_snprintf("Error on line %d, %s", line, 
config_errorstr(error)));
+               configerror(safe_snprintf("Cannot open config file %s", 
inidata));
+               ftpd_killconfig(newconfig);
+               return(NULL);
+       }
+ 
+       section = getsectionid(newconfig->configfile, "main");
+       if (section == -1)
+       {
+               ftpd_killconfig(newconfig);
+               configerror(strdupwrapper("Cannot get main section in config 
file!"));
+               return(NULL);
+       }
+               
+       loadintfromconfig(newconfig->configfile, section, "altlongreplies", 
+                         &(newconfig->altlongreplies), ALTLONGREPLIES);
+       loadintfromconfig(newconfig->configfile, section, "smartbind",
+                         &(newconfig->smartbind), SMARTBIND);
+       loadintfromconfig(newconfig->configfile, section, "zerobind",
+                         &(newconfig->zerobind), ZEROBIND);
+       loadintfromconfig(newconfig->configfile, section, "vserverhost",
+                         &(newconfig->hostvservers), 0);
+       loadintfromconfig(newconfig->configfile, section, "rdnstimeout", 
+                         &(newconfig->dnstimeout), RDNSTIMEOUT);
+       if (newconfig->dnstimeout == 0)
+               newconfig->dnstimeout = -1;
+ 
+       if (!(setting = getconfigdata(newconfig->configfile, section, 
"runasuser", 1)))                 
+       {
+               newconfig->gidt_asgid = getgid();
+               newconfig->uidt_asuid = getuid();
+               newconfig->username = NULL;
+       }
+       else
+       {
+               userinfo = getpwnam(setting);
+               if (userinfo == NULL)
+               {
+                       configerror(safe_snprintf("runasuser: username '%s' 
doesn't exist!", setting));
+                       ftpd_killconfig(newconfig);
+                       return(NULL);
+               }
+               newconfig->gidt_asgid = userinfo->pw_gid;
+               newconfig->uidt_asuid = userinfo->pw_uid;
+               newconfig->username = setting;
+       }
+ 
+       vscpos = &(newconfig->inports);
+       newconfig->vserverlist = makeconfiglist(newconfig->configfile, "main", 
"vserver");
+       if (newconfig->vserverlist[0] == NULL)
+       {
+               /* we don't have vservers, so we create one vserver
+                  using the main section. This has the major advantage
+                  of not needing lots of code for non-vserver setups */
+               newconfig->defaults = vserver_load(newconfig, "main", NULL, 
&(vscpos));
+               newconfig->defaults->umask = use_umask;
+               newconfig->vservers = NULL;
+       }
+       else
+       {
+               if (newconfig->hostvservers)
+                       newconfig->defaults = vserver_load(newconfig, "main", 
NULL, &(vscpos));
+               else
+                       newconfig->defaults = vserver_load(newconfig, "main", 
NULL, NULL);
+               newconfig->defaults->umask = use_umask;
+               occur = 0;
+               vspos = &(newconfig->vservers);
+               while(newconfig->vserverlist[occur] != NULL)
+               {
+                       if (strlen(newconfig->vserverlist[occur]) >= 
MAXSECTIONLEN)
+                       {
+                               configerror(safe_snprintf("vserver '%s', name 
too long. Must be less than %d characters long.", 
newconfig->vserverlist[occur], MAXSECTIONLEN));
+                               ftpd_killconfig(newconfig);
+                               return(NULL);
+                       }
+                       *vspos = vserver_load(newconfig, 
newconfig->vserverlist[occur], newconfig->defaults, &(vscpos));
+                       if (*vspos == NULL)
+                       {
+                               ftpd_killconfig(newconfig);
+                               return(NULL);
+                       }
+                       vspos = &((*vspos)->next);
+                       occur++;
+               }
+               *vspos = NULL;
+       }
+       
+       *vscpos = NULL;
+       newconfig->rootmode = ((int)newconfig->uidt_asuid == 0);
+       
+       loadstrfromconfig(newconfig->configfile, section, "vserverdefault",
+                         &setting, NULL);
+       if ((setting) && (newconfig->vservers) && (newconfig->hostvservers))
+       {       
+               vpos = newconfig->vservers;
+               while((vpos != NULL) && (strcmp(vpos->sectionname, setting) != 
0))
+                       vpos = vpos->next;
+               
+               if (vpos == NULL)
+               {
+                       configerror(safe_snprintf("vserverdefault section '%s' 
is not defined.", setting));
+                       ftpd_killconfig(newconfig);
+                       return(NULL);
+               }
+               newconfig->defaulthost = vpos;  
+       }
+       
+       /* process pasvrange */
+       
+       if (!(setting = getconfigdata(newconfig->configfile, section, 
"pasvrange", 1)))
+       {
+               newconfig->pasvport_first = 0;
+               newconfig->pasvport_last = 0;
+       }
+       else
+       {
+               char *rsetting = strdupwrapper(setting);
+               char a;
+               
+               strtrimspace(rsetting);
+               if (sscanf(rsetting, "%u-%u%c", &(newconfig->pasvport_first), 
&(newconfig->pasvport_last), &a) != 2)
+               {
+                       freewrapper(rsetting);
+                       configerror(strdupwrapper("pasvrange requires parameter 
in the form of x-y (eg 1500-2000)"));
+                       ftpd_killconfig(newconfig);
+                       return(NULL);
+               }
+       }
+ 
+       /* process natip */
+       
+       if (!(setting = getconfigdata(newconfig->configfile, section, "natip", 
1)))
+       {
+               newconfig->natip = 0;
+       }
+       else
+       {
+               if (getnetworkint(setting, &(newconfig->natip)) == -1)
+               {
+                       configerror(safe_snprintf("natip '%s' does not resolve 
to an IP address", setting));
+                       ftpd_killconfig(newconfig);
+                       return(NULL);
+               }       
+       }
+ 
+       /* process nat_netclass */
+ 
+       if (newconfig->natip != 0)
+       {
+               newconfig->nat_localips = ipacllist_new(newconfig->configfile, 
section, "nat_netclass", 'L', FALSE);
+               if (newconfig->nat_localips->count == 0)
+               {
+                       ipacllist_destroy(newconfig->nat_localips);
+                       newconfig->nat_localips = &nat_localdefault;
+               }
+       }
+       
+       return(newconfig);
+ }
+ 
+ int ftpd_checkvserver(CONFIGDATA *cdat, VSERVER *vs)
+ {
+       int result = TRUE;
+       int count = 0;
+       
+       if (vs->maxusers <= 0)
+       {
+               configerror(safe_snprintf("section '%s': must has maxusers more 
than zero.", vs->sectionname));
+               result = FALSE;
+       }
+       if ((vs->loglevel > 127) || (vs->loglevel < 0))
+       {
+               configerror(safe_snprintf("section '%s': Logstrength must be 
between 0 and 127", vs->sectionname));
+               result = FALSE;
+       }
+       if (vs->logfile)
+               if (vs->logfile[0] != '/')
+               {
+                       configerror(safe_snprintf("section '%s': logfile is not 
an absolute filename", vs->sectionname));
+                       result = FALSE;
+               }
+               
+       if (vs->prelogindump)
+               if (vs->prelogindump[0] != '/')
+               {
+                       configerror(safe_snprintf("section '%s': logindump is 
not an absolute filename.", vs->sectionname));
+                       result = FALSE;
+               }
+ 
+       if ((vs->logincount == 0) || (vs->logincount < -1))
+       {
+               configerror(safe_snprintf("section '%s': logintries must be 
more than 0 or set to -1.", vs->sectionname));
+               result = FALSE;
+       }
+ 
+       if (vs->authwait < 0)
+       {
+               configerror(safe_snprintf("section '%s': badauthwait must be 0 
or more milliseconds.", vs->sectionname));
+               result = FALSE;
+       }
+ 
+       while(vs->grouplist[count] != NULL)
+       {
+               if (strlen(vs->grouplist[count]) >= MAXSECTIONLEN)
+               {
+                       configerror(safe_snprintf("section '%s': group '%s', 
name too long. Must be less than %d characters long", vs->sectionname, 
vs->grouplist[count], MAXSECTIONLEN));
+                       result = FALSE;
+               }
+               else if (getsectionid(cdat->configfile, vs->grouplist[count]) 
== -1)
+               {
+                       configerror(safe_snprintf("section '%s': group '%s' 
does not have a section in the config file.", vs->sectionname, 
vs->grouplist[count]));
+                       result = FALSE;
+               }
+               count++;
+       }
+ 
+       return(result);
+ }
+ 
+ int ftpd_checkconfig(CONFIGDATA *cdat)
+ {
+       int result = TRUE;
+       VSERVERCONN *vsc, *vsc2;
+       VSERVER *vs;
+       
+       if (cdat->dnstimeout < -1)
+       {
+               result = FALSE;
+               configerror(strdupwrapper("dnstimeout must be zero or more 
seconds"));
+       }
+       /* search for duplicate binds! */
+ 
+       vsc = cdat->inports;
+       if (vsc == NULL)
+       {
+               result = FALSE;
+               configerror(strdupwrapper("there are no ports to bind to."));
+       }
+       
+       while ((vsc != NULL) && result)
+       {
+               vsc2 = vsc->next;
+               if ((vsc->port <= 0) || (vsc->port > 65535))
+               {
+                       result = FALSE;
+                       configerror(strdupwrapper("port to bind is not valid"));
+               }
+ 
+               while ((vsc2 != NULL) && result)
+               {
+                       if ((vsc2->port) == (vsc->port))
+                               if ((vsc2->ip == 0) ||
+                                  (vsc2->ip == vsc->ip))
+                               {
+                                       result = FALSE;
+                                       configerror(strdupwrapper("Overlapping 
port binds found!"));
+                               }
+                       vsc2 = vsc2->next;
+               }
+               vsc = vsc->next;
+       }
+ 
+       /* now check pasvrange */
+       
+       if (cdat->pasvport_first || cdat->pasvport_last)
+       {
+               if ((cdat->pasvport_first < 1024) || (cdat->pasvport_last > 
65535) 
+                       || (cdat->pasvport_first > cdat->pasvport_last))
+               {
+                       result = FALSE;
+                       configerror(strdupwrapper("pasvrange is not valid!"));
+               } 
+       }
+ 
+       /* now check all the vservers */
+ 
+       vs = cdat->vservers;
+       while ((vs != NULL) && result)
+       {
+               result = ftpd_checkvserver(cdat, vs);
+               vs = vs->next;
+       }
+ 
+       if (result)
+               result = ftpd_checkvserver(cdat, cdat->defaults);
+       
+       /* now check the config file, and open attempt to open it */
+       
+       if (result)     /* if everything is ok still */         
+       {
+               cdat->logout = log_initcontext(cdat->defaults->logfile);
+               if (cdat->logout == -1)
+               {
+                       result = FALSE;
+                       configerror(safe_snprintf("Couldn't open logfile '%s'", 
cdat->defaults->logfile));
+               }
+       }
+ 
+       return(result);
+ }
Index: muddleftpd/src/checkip.c
diff -c /dev/null muddleftpd/src/checkip.c:1.1.8.1
*** /dev/null   Tue Nov  4 10:41:23 2003
--- muddleftpd/src/checkip.c    Tue Nov  4 10:41:22 2003
***************
*** 0 ****
--- 1,151 ----
+ /* Copyright (C) 1999 Beau Kuiper
+ 
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+ 
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+ 
+ #include "ftpd.h"
+ 
+ int checknamelist(CONFIGFILECACHE *cf, int section, char *username)
+ {
+       int occur;
+       char *setting;
+       
+       occur = 1;
+       while ((setting = getconfigdata(cf, section,
+                                       "nameacl", occur)))
+       {
+               char *name = strchr(setting, ':');
+               
+               if (name)
+               {
+                       name++;
+                       if (my_fnmatch(name, username, 0) == 0)
+                               return((*setting == 'A') || (*setting == 'a'));
+               }
+               occur++;
+       }
+       return(FALSE);
+ }
+ 
+ /* TODO: ... don't just press on if syntax is not good ... */
+ 
+ IPACLLIST *ipacllist_new(CONFIGFILECACHE *cf, int section, char *name, char 
acceptchar, int defaultret)
+ {
+       int occur;
+       char *setting;
+       IPACLLIST *new = mallocwrapper(sizeof(IPACLLIST));
+       
+       occur = 1;
+       new->count = 0;
+       new->defaultret = defaultret;
+       new->list = NULL;
+       
+       while ((setting = getconfigdata(cf, section, name,
+                         occur)))
+       {
+               char *settingt = strdupwrapper(setting);
+               char *netmask, *ipaddr;
+               
+               strtrimspace(settingt);
+               ipaddr = strchr(settingt, ':');
+       
+               if (ipaddr)
+               {
+                       ipaddr++;
+                       netmask = strchr(ipaddr, '/');
+                       reallocwrapper(sizeof(IPACL) * (new->count + 1),
+                                      (void *)&(new->list));
+                       new->list[new->count].accept = (*settingt == 
acceptchar) ||
+                                                      (*settingt == 
(char)tolower((int)acceptchar));
+ 
+                       if ((*ipaddr == '(') && (ipaddr[strlen(ipaddr)-1] == 
')'))
+                       {
+                               /* hostname-pattern type IPACL */
+                               memmove(settingt, ipaddr + 1, strlen(ipaddr));
+                               settingt[strlen(settingt)-1] = 0;
+                               new->list[new->count].fnstr = settingt;
+                               new->list[new->count].type = 0;
+                       }
+                       else if (!netmask)
+                       {
+                               /* IP-pattern type IPACL */
+                               memmove(settingt, ipaddr, strlen(ipaddr)+1);
+                               new->list[new->count].fnstr = settingt; 
+                               new->list[new->count].type = 1;
+                       }
+                       else
+                       {
+                               *netmask = 0;
+                               netmask++;
+                               getnetworkint(ipaddr, 
&(new->list[new->count].ipaddr));
+                               getnetworkint(netmask, 
&(new->list[new->count].netmask));
+                               new->list[new->count].fnstr = NULL;
+                               freewrapper(settingt);
+                               new->list[new->count].type = 2;
+                       }
+                       new->count++;
+               }
+               else
+               {
+                       log_addentry(MYLOG_INFO, NULL, "Error decoding ipacl 
directive, data missing ':'. Skipping entry.");
+                       freewrapper(settingt);
+               }
+ 
+               occur++;
+       }
+       return(new);
+ }
+ 
+ void ipacllist_destroy(IPACLLIST *list)
+ {
+       int count;
+       
+       if (list->count > 0)
+       {
+               for (count = 0; count < list->count; count++)
+                       freeifnotnull(list->list[count].fnstr);
+               freewrapper(list->list);
+       }
+ 
+       freewrapper(list);
+ }
+ 
+ int ipacllist_query(IPACLLIST *list, int ip, char *hostname)
+ {
+       int pos = 0;
+       int mip, mip2;
+       char *ipstr = (char *)getipstr(ip);
+       
+       for (pos = 0; pos < list->count; pos++)
+       {
+               switch(list->list[pos].type)
+               {
+                       case 0:
+                               if (my_fnmatch(list->list[pos].fnstr, hostname, 
0) == 0)
+                                       return(list->list[pos].accept);
+                               break;
+                       case 1:
+                               if (my_fnmatch(list->list[pos].fnstr, ipstr, 0) 
== 0)
+                                       return(list->list[pos].accept);
+                               break;
+                       case 2:
+                               mip = ip & list->list[pos].netmask;
+                               mip2 = list->list[pos].ipaddr & 
list->list[pos].netmask;
+                               if (mip == mip2)
+                                       return(list->list[pos].accept);
+                               break;
+               }
+       }
+       return(list->defaultret);
+ }
Index: muddleftpd/src/ftp.c
diff -c /dev/null muddleftpd/src/ftp.c:1.1.8.1
*** /dev/null   Tue Nov  4 10:41:23 2003
--- muddleftpd/src/ftp.c        Tue Nov  4 10:41:22 2003
***************
*** 0 ****
--- 1,1070 ----
+ /* Copyright (C) 1999 Beau Kuiper
+ 
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+ 
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+ 
+ #include "ftpd.h"
+ #include "ftpcmd.h"
+ #include "reply.h"
+ 
+ extern FTPCMD mainftpcmd[];
+ 
+ /* This will print a NICE! error to the user */
+ 
+ void reporterror(FTPSTATE *peer, char *filename, int errorno)
+ {
+       ftp_write(peer, FALSE, 550, "'%s': %s", filename, strerror(errorno));
+ }
+ 
+ int readipstr(char *data, int *port, unsigned int *ip)
+ {
+       int a1,a2,a3,a4,a5,a6, result;
+       
+       result = sscanf(data, "%d,%d,%d,%d,%d,%d",&a1, &a2, &a3, &a4, &a5, &a6);
+       if (result != 6)
+               return(FALSE);
+       
+       *ip = (a1 << 24) | (a2 << 16) | (a3 << 8) | (a4);
+       *port = (a5 * 256) + a6;
+       return(TRUE);
+ }
+ 
+ int readeipstr(char *ip, unsigned int *outip)
+ {
+       int a1, a2, a3, a4, result;
+ 
+       result = sscanf(ip, "%d.%d.%d.%d", &a1, &a2, &a3, &a4);
+       if (result != 4)
+               return(FALSE);
+       
+       *outip = (a1 << 24) | (a2 << 16) | (a3 << 8) | (a4);
+       return(TRUE);
+ }
+ 
+ int ftp_dele(FTPSTATE *peer, char *filename)
+ {
+       if (file_unlink(peer, filename) == 0)
+               ftp_write(peer, FALSE, 250, REPLY_DELETE(filename));
+       else
+               reporterror(peer, filename, errno); 
+ 
+       return(FALSE);
+ }
+ 
+ int ftp_chmod(FTPSTATE *peer, char *filename, int mode)
+ {
+       if (file_chmod(peer, filename, mode) == 0)
+               ftp_write(peer, FALSE, 250, REPLY_CHMOD(filename, mode)); 
+       else
+               reporterror(peer, filename, errno); 
+ 
+       return(FALSE);
+ }
+ 
+ int ftp_size(FTPSTATE *peer, char *filename)
+ {
+       int filefd;
+       off_t size = 0;
+       int asize;
+       struct stat statdata;
+       
+       if (peer->binary)
+       {
+               filefd = file_stat(peer, filename, &statdata);
+               if (filefd == 0)
+                       size = statdata.st_size;
+       }
+       else
+       {
+               char *rname;
+               size = 0;
+               filefd = file_readopen(peer, filename, &rname);
+               freewrapper(rname);
+               
+               if (filefd > 0)
+               {
+                       char *chin, buffer[BUFFERSIZE];
+                               
+                       asize = read(filefd, buffer, BUFFERSIZE-1);
+                       while (asize > 0)
+                       {
+                               buffer[asize] = 0;
+                               size += asize;
+                               chin = buffer;
+                               while ((chin = strchr(chin, 10)) != NULL)
+                               {
+                                       chin++;
+                                       size++;
+                               }
+                               asize = read(filefd, buffer, BUFFERSIZE-1);
+                       }
+                       close(filefd);
+               }
+       }
+ 
+       if (filefd >= 0)
+               ftp_write(peer, FALSE, 213, "%s", offt_tostr(size)); 
+       else
+               reporterror(peer, filename, errno);
+ 
+       return(FALSE);
+ }
+ 
+ int ftp_mdtm(FTPSTATE *peer, char *filename)
+ {
+       struct stat statdata;
+       
+       if (file_stat(peer, filename, &statdata) == 0)
+       {
+               char timestr[20];
+               struct tm *resulttm = gmtime(&statdata.st_mtime);
+               
+               strftime(timestr, 20, "%Y%m%d%H%M%S", resulttm);
+ 
+               ftp_write(peer, FALSE, 213, timestr); 
+       }
+       else
+               reporterror(peer, filename, errno); 
+ 
+       return(FALSE);
+ }
+ 
+ void setdumptokens(FTPSTATE *peer, TOKENSET *ts)
+ {
+       char *outstr;
+       time_t mytime = time(NULL);
+               
+       outstr = ctime(&mytime);
+       outstr[strlen(outstr) - 1] = 0;
+       tokenset_settoken(ts, 'T', strdupwrapper(outstr));
+       tokenset_settoken(ts, 'U', strdupwrapper(peer->username));
+       if (peer->pwd)
+               tokenset_settoken(ts, 'C', strdupwrapper(dir_getvirtual(peer, 
peer->pwd)));
+       tokenset_settoken(ts, 'E', strdupwrapper(peer->vserver->email));
+       tokenset_settoken(ts, 'M', safe_snprintf("%d", peer->maxusers));
+       if (peer->loggedin)
+               tokenset_settoken(ts, 'N', safe_snprintf("%d", 
peer->usercount));
+       tokenset_settoken(ts, 'R', strdupwrapper(peer->hostname));
+       tokenset_settoken(ts, 'L', strdupwrapper(peer->vserver->vhostname));
+       tokenset_settoken(ts, 'f', safe_snprintf("%d", peer->downloadedfiles));
+       tokenset_settoken(ts, 'F', safe_snprintf("%d", peer->uploadedfiles));
+       tokenset_settoken(ts, 'b', safe_snprintf("%s", 
offt_tostr(peer->downloadedfilebytes)));
+       tokenset_settoken(ts, 'B', safe_snprintf("%s", 
offt_tostr(peer->uploadedfilebytes)));
+       tokenset_settoken(ts, 'I', safe_snprintf("%s", 
offt_tostr(peer->listdownloadedbytes)));
+       tokenset_settoken(ts, 'i', safe_snprintf("%d", peer->listconns));
+       tokenset_settoken(ts, 'D', safe_snprintf("%s", 
offt_tostr(peer->listdownloadedbytes + peer->downloadedfilebytes)));
+       tokenset_settoken(ts, 't', safe_snprintf("%s", 
offt_tostr(peer->listdownloadedbytes + peer->downloadedfilebytes + 
peer->uploadedfilebytes)));
+       tokenset_settoken(ts, 'c', safe_snprintf("%d", peer->listconns + 
peer->downloadedfiles + peer->uploadedfiles));
+       tokenset_settoken(ts, 'd', safe_snprintf("%d", peer->listconns + 
peer->downloadedfiles));
+       if (peer->ratioinfo)
+               ratio_settokens(peer->ratioinfo, ts);
+       tokenset_settoken(ts, 'v', strdupwrapper(peer->vserver->sectionname));
+ }
+ 
+ int ftp_dumper(FTPSTATE *peer, NEWFILE *infile, int number, char *fmessage, 
int dotokens, int endtokens)
+ {
+       TOKENSET *ts = NULL;
+       char *inp, *tfmessage;
+       int first;
+ 
+       if (dotokens)
+               if ((infile != NULL) || (endtokens))
+               {
+                       ts = tokenset_new();
+                       setdumptokens(peer, ts);
+               }
+               
+       if (infile != NULL)
+       {
+               first = TRUE;           
+               while ((inp = nfgetcs(infile, '\n')) != NULL)
+               {
+                       if (inp[strlen(inp)-1] == '\n')
+                               inp[strlen(inp)-1] = 0;
+                       
+                       if (dotokens)
+                               inp = tokenset_apply(ts, inp, FALSE);
+                               
+                       if (first || config->altlongreplies)
+                       {
+                               ftp_write(peer, TRUE, 0, "%d-%s", number, inp);
+                               first = FALSE;
+                       }
+                       else
+                               ftp_write(peer, TRUE, 0, "    %s", inp);
+ 
+                       freewrapper(inp);
+               }
+  
+               nfclose(infile);
+       }
+       
+       if (endtokens)
+       {
+               tfmessage = tokenset_apply(ts, strdupwrapper(fmessage), FALSE);
+               ftp_write(peer, FALSE, number, "%s", tfmessage);
+               freewrapper(tfmessage);
+       }
+       else
+               ftp_write(peer, FALSE, number, "%s", fmessage);
+       
+       if (ts)
+               tokenset_finish(ts);
+ 
+       return(FALSE);
+ }
+ 
+ int ftp_dumpstr(FTPSTATE *peer, char *dumpstr, int number, char *fmessage, 
int endtokens)
+ {
+       TOKENSET *ts = NULL;
+       char *oldinp = dumpstr;
+       char *inp = dumpstr;
+       char *datline, *tfmessage;
+       int first;
+ 
+       ts = tokenset_new();
+       setdumptokens(peer, ts);
+       
+       first = TRUE;
+       while (oldinp != NULL)
+       {
+               inp = strchr(inp, '\n');
+               if (inp != NULL)
+                       *inp = 0;
+               
+               datline = strdupwrapper(oldinp);
+               if (inp != NULL)
+               {
+                       *inp = '\n';
+                       inp++;
+               }
+               oldinp = inp;
+               datline = tokenset_apply(ts, datline, FALSE);
+                               
+               if (first || config->altlongreplies)
+               {
+                       ftp_write(peer, TRUE, 0, "%d-%s", number, datline);
+                       first = FALSE;
+               }
+               else
+                       ftp_write(peer, TRUE, 0, "    %s", datline);
+               freewrapper(datline);
+ 
+       }
+  
+       if (endtokens)
+       {
+               tfmessage = tokenset_apply(ts, strdupwrapper(fmessage), FALSE);
+               ftp_write(peer, FALSE, number, "%s", tfmessage);
+               freewrapper(tfmessage);
+       }
+       else
+               ftp_write(peer, FALSE, number, "%s", fmessage);
+ 
+       tokenset_finish(ts);
+       return(FALSE);
+ }
+ 
+ VSERVER *find_vserver_byname(char *name)
+ {
+       VSERVER *v = config->vservers;
+       
+       while((v != NULL) && (strcasecmp(v->vhostname, name) != 0))
+               v = v->next;
+       
+       return(v);
+ }
+ 
+ int host_isip(char *name)
+ {
+       while(((*name >= '0') && (*name <= '9')) || (*name == '.'))
+               name++;
+       return(*name == 0);
+ }
+ 
+ int ftp_host(FTPSTATE *peer, char *params)
+ {
+       VSERVER *v;
+       /* if there are no vservers, or hostname vservers are not defined,
+          return with not implemented */
+       if ((!config->vservers) || (!config->hostvservers))
+               return(ftp_write(peer, FALSE, 502, REPLY_NOHOSTS));
+ 
+       /* if a vserver has already been selected, return 530 */
+       if (peer->vserver != config->defaults)
+               return(ftp_write(peer, FALSE, 530, REPLY_HOSTSELECTED));
+       
+       if (config->defaulthost && (host_isip(params)))
+               v = config->defaulthost;
+       else
+               v = find_vserver_byname(params);
+       
+       if (!v)
+               return(ftp_write(peer, FALSE, 533, 
REPLY_HOSTNOTFOUND(params))); 
+ 
+       switch(vserver_select(peer, v))
+       {
+               case 1:
+                       return(ftp_write(peer, FALSE, 533, 
REPLY_HOSTMISCONF(params)));
+               case 2:
+                       return(ftp_write(peer, FALSE, 533, 
REPLY_HOSTNOTFOUND(params)));
+               case 3:
+                       ftp_write(peer, FALSE, 421, REPLY_HOSTTOOBUSY(params));
+                       return(TRUE);
+       }
+       
+       ftp_write(peer, FALSE, 202, REPLY_HOSTSEL(v->vhostname));
+ 
+       return(3);
+ }
+ 
+ int ftp_rnto(FTPSTATE *peer, char *filename)
+ { 
+       char *badfile;
+       char *oldname;
+       
+       if (!peer->renameoldname)
+               return(ftp_write(peer, FALSE, 503, REPLY_RENAMENOSOURCE)); 
+       
+       oldname = dir_getvirtual(peer, peer->renameoldname);
+ 
+       if ((badfile = file_rename(peer, oldname, filename)) == NULL)
+               ftp_write(peer, FALSE, 250, REPLY_RENAME(oldname,  filename));
+       else
+               reporterror(peer, badfile, errno);
+ 
+       freewrapper(peer->renameoldname);
+       peer->renameoldname = NULL;
+ 
+       return(FALSE);
+ }
+ 
+ int ftp_mkd(FTPSTATE *peer, char *filename)
+ {
+       if (file_mkdir(peer, filename) == 0)
+       {
+               char *newfile = file_expand(peer, filename);
+               ftp_write(peer, FALSE, 257, REPLY_MKDIR(dir_getvirtual(peer, 
newfile)));
+               freewrapper(newfile);
+       }
+       else
+               reporterror(peer, filename, errno); 
+ 
+       return(FALSE);
+ }
+ 
+ int ftp_rmd(FTPSTATE *peer, char *filename)
+ {
+       if (file_rmdir(peer, filename) == 0)
+               ftp_write(peer, FALSE, 250, REPLY_RMDIR(filename));
+       else
+               reporterror(peer, filename, errno); 
+ 
+       return(FALSE);
+ }
+ 
+ int ftp_cwddo(FTPSTATE *peer, char *newdir, int dump)
+ {
+       char *tmp = strdupwrapper(peer->pwd);
+       
+       dir_combine(peer, &tmp, newdir);
+       
+       if (!checkchdir(peer, tmp))
+       {
+               freewrapper(tmp);
+               reporterror(peer, newdir, errno);
+               return(FALSE);
+       }
+       chdir(tmp);
+       
+       if (peer->realdir)
+       {
+               char *nt;
+               /* this relies on the side effect of checkchdir where
+                  the server changes dir to check */
+               nt = (char *)dir_getreal(peer);
+               if (nt != NULL)
+               {
+                       freewrapper(tmp);
+                       tmp = nt;
+               }
+       }
+               
+       
+       {
+               NEWFILE *nfile = NULL;
+               char *outstr;
+               int ret = 257;
+               /* If we need to print a file, then open it */ 
+               /* Oh bugger, crap, and other things, I left a big
+                  security hole here! :-(, now fixed */
+               if (dump)
+                       ret = 250;
+               freewrapper(peer->pwd);
+               peer->pwd = tmp;
+               if ((dump) && (peer->cwddump) && (!peer->cwddumpdata))
+               {
+                       if (peer->cwddump[0] == '/')
+                               /* if it is an absolute file, don't worry
+                                  about permissions accessing file */
+                               nfile = nfopen(peer->cwddump);
+                       else
+                               /* worry like hell and pass request via
+                                  both ACL's and file permissions! */
+                               nfile = file_nfopen(peer, peer->cwddump);
+               }
+               outstr = safe_snprintf(REPLY_PWD(dir_getvirtual(peer, 
peer->pwd)));
+               
+               if ((dump) && (peer->cwddumpdata))
+                       ftp_dumpstr(peer, peer->cwddumpdata, ret, outstr, 
FALSE);
+               else
+                       ftp_dumper(peer, nfile, ret, outstr, TRUE, FALSE);
+               freewrapper(outstr);
+       }
+       
+       return(FALSE);
+ }
+ 
+ int ftp_cwd(FTPSTATE *peer, char *newdir)
+ {
+       return(ftp_cwddo(peer, newdir, TRUE));
+ }
+ 
+ int ftp_run(FTPSTATE *peer, INPUTLINE *cmd, char *token)
+ {
+       if ((cmd->command->ftpfunc) == NULL)
+               return(ftp_write(peer, FALSE, 500, REPLY_CMDNOTKNOWN(token)));
+       
+       if (cmd->command->paramnum) 
+       {
+               if (cmd->parameters == NULL)
+                       return(ftp_write(peer, FALSE, 500, 
REPLY_NOPARAM(token)));
+               else if (cmd->parameters[0] == 0)
+                       return(ftp_write(peer, FALSE, 500, 
REPLY_NOPARAM(token)));
+       }
+       
+       if ((cmd->command->needslogin) && (peer->loggedin != TRUE))
+               return(ftp_write(peer, FALSE, 530, REPLY_NOLOGIN));
+       
+       if ((!cmd->command->dataportok) && (peer->dport))
+               return(ftp_write(peer, FALSE, 520, 
REPLY_DATACONNINVALID(token)));
+       else
+               return(cmd->command->ftpfunc(peer, cmd->parameters));
+ }
+ 
+ int ftp_quit(FTPSTATE *peer, char *param)
+ {
+       if (peer->quitdump)
+       {
+               NEWFILE *nfile = nfopen(peer->quitdump);
+               ftp_dumper(peer, nfile, 221, REPLY_QUIT, TRUE, FALSE);
+       }
+       else if (peer->quitdumpdata)
+               ftp_dumpstr(peer, peer->quitdumpdata, 221, REPLY_QUIT, FALSE);
+       else
+               ftp_write(peer, FALSE, 221, REPLY_QUIT);
+       return(TRUE);
+ }
+ 
+ int ftp_user(FTPSTATE *peer, char *param)
+ {
+       if (peer->jailenabled)
+               return(ftp_write(peer, FALSE, 530, REPLY_JAILUSER));
+       if (peer->loginsleft == 0)
+               return(ftp_write(peer, FALSE, 530, REPLY_NOCREDITS));
+       if (peer->loggedin)
+               shinfo_delusergroup(peer->groupname);
+ 
+       shinfo_changeuser(param);
+       peer->loggedin = 0;
+       
+       freewrapper(peer->username);
+       peer->username = strdupwrapper(param);
+       
+       return(ftp_write(peer, FALSE, 331, REPLY_USER(peer->username)));
+ }
+ 
+ int ftp_host_compat(FTPSTATE *peer)
+ {
+       VSERVER *v;
+       char *name;
+       /* if there are no vservers, or hostname vservers are not defined,
+          return with not implemented */
+       if ((!config->vservers) || (!config->hostvservers))
+               return(1);
+ 
+       /* if a vserver has already been selected, return 530 */
+       if (peer->vserver != config->defaults)
+               return(1);
+ 
+       name = strchr(peer->username, '%');
+       
+       if (!name)
+               v = config->defaulthost;
+       else
+       {
+               *name = 0;
+               name++;
+               
+               if (config->defaulthost && (host_isip(name)))
+                       v = config->defaulthost;
+               else
+                       v = find_vserver_byname(name);
+       }
+       
+       if (!v)
+               return(0);
+ 
+       switch(vserver_select(peer, v))
+       {
+               case 1:
+               case 2:
+                       return(0);
+               case 3:
+                       return(2);
+       }
+       
+       return(1);
+ }
+ 
+ int ftp_pass(FTPSTATE *peer, char *param)
+ {
+       int result = FALSE;
+       int loginok, oldnice = peer->nicevalue;
+       int toomany = FALSE;
+       char *errmsg = NULL;
+       
+       switch(ftp_host_compat(peer))
+       {
+               case 0:
+                       return(ftp_write(peer, FALSE, 530, 
REPLY_LOGINFAIL(peer->username, "Bad password")));
+               case 2:
+                       ftp_write(peer, FALSE, 421, REPLY_TOOMANYUSERS);
+                       return(TRUE);
+       }
+ 
+       if (peer->loggedin)
+               return(ftp_write(peer, FALSE, 530, REPLY_ALREADYLOGGEDIN));
+ 
+       if (strcmp(peer->username, "<unknown>") == 0)
+               return(ftp_write(peer, FALSE, 503, REPLY_USEUSERFIRST));
+       
+       loginok = (param != NULL);
+       
+       if (loginok)
+       {
+               file_becomeroot(peer);
+               setgroups(0, NULL);
+               errmsg = setuseropts(peer, param);
+               loginok = (errmsg == NULL);
+       }
+ 
+       /* check some stuff, set basedir to real pathname */
+       if (loginok)
+       {
+               file_becomeuser(peer);
+               if (chdir(peer->basedir) == -1)
+               {
+                       loginok = FALSE;
+                       log_giveentry(MYLOG_INFO, NULL, 
safe_snprintf("rootdir(%s) for user '%s' is not accessable. Check rootdir and 
rootdir permissions", peer->basedir, peer->username));
+               }
+               else
+               {
+                       char *curdir = getcwd2();
+                       if (strcmp(curdir, peer->basedir) != 0)
+                       {
+                               log_giveentry(MYLOG_INFO, NULL, 
safe_snprintf("rootdir changed from '%s' to '%s', symbolic links resolved for 
user '%s'", peer->basedir, curdir, peer->username));
+                               freewrapper(peer->basedir);
+                               peer->basedir = curdir;
+                       }
+                       else
+                               freewrapper(curdir);
+               }
+               file_becomeroot(peer);
+       }
+ 
+       if (loginok)
+       {
+               peer->usercount = toomany = 
shinfo_addusergroup(peer->groupname, peer->maxusers);
+               toomany = (toomany == -1);
+               if (toomany)
+                       loginok = FALSE;
+       }
+ 
+       if (loginok)
+       {
+               char *outstr;
+               NEWFILE *nfile;
+               
+               log_addentry(MYLOG_LOGIN, peer, "Login successful."); 
+               outstr = safe_snprintf(REPLY_PASSOK(peer->username));
+               
+               /* perform chroot if needed */
+               if (peer->chroot)
+                       dochroot(peer);
+ 
+               if (nice(peer->nicevalue + (-oldnice)) == -1)
+               {
+                       log_giveentry(MYLOG_INFO, NULL, safe_snprintf("nice(%d) 
gave error %s", peer->nicevalue + (-oldnice), strerror(errno)));
+                       peer->nicevalue = oldnice;
+               }
+               
+               if (config->rootmode)
+                       setgroups(peer->supgids[0], peer->supgids + 1);
+               
+               /* see if rootdir == "/", change to "" */
+               if (peer->basedir[1] == 0)
+                       peer->basedir[0] = 0;
+               
+               if ((peer->droproot) && (config->rootmode))
+               {
+                       if (giveuproot(peer->uidt_asuid, peer->gidt_asgid))
+                               log_addentry(MYLOG_INFO, peer, "Failed to set 
capabilities!");
+                       peer->jailenabled = TRUE;
+                       /* we are not root anymore and can never be root
+                          again! */
+                       config->rootmode = FALSE;
+               }
+                                                       
+               file_becomeuser(peer);
+               
+               nfile = nfopen(peer->logindump);
+               peer->pwd = strdupwrapper(peer->basedir);
+               dir_combine(peer, &(peer->pwd), peer->homedir);
+               
+               if (peer->logindumpdata)
+                       result = ftp_dumpstr(peer, peer->logindumpdata, 230, 
outstr, FALSE);
+               else
+                       result = ftp_dumper(peer, nfile, 230, outstr, TRUE, 
FALSE);
+               freewrapper(outstr);
+       }
+       else
+       {
+               peer->loggedin = FALSE;
+               log_addentry(MYLOG_LOGIN, peer, "Login failed");
+               peer->timeout = peer->vserver->timeout;
+               if (toomany)
+               {
+                       NEWFILE *nfile = nfopen(peer->busydump);
+                       logfullmessage(GROUPFULL, peer->remoteip);
+                       if (peer->busydumpdata)
+                               ftp_dumpstr(peer, peer->busydumpdata, 421, 
REPLY_TOOMANYUSERS, FALSE);
+                       else
+                               ftp_dumper(peer, nfile, 421, 
REPLY_TOOMANYUSERS, TRUE, FALSE);
+               }
+               else
+               {
+                       usleep(peer->vserver->authwait);
+                       if (errmsg == NULL)
+                               errmsg = strdupwrapper("Bad password");
+                       result = ftp_write(peer, FALSE, 530, 
REPLY_LOGINFAIL(peer->username, errmsg));
+                       if (peer->loginsleft > 0)
+                               peer->loginsleft--;
+                       freewrapper(errmsg);
+               }
+               freewrapper(peer->username);
+               peer->username = strdupwrapper("<unknown>");
+       }
+       if (toomany)
+               return(TRUE);   /* quit */
+       return(3);              /* make sure timeout gets updated */
+ }
+ 
+ int ftp_rest(FTPSTATE *peer, char *param)
+ {
+       off_t newpos;
+       int res;
+ 
+       res = strto_offt(param, &newpos);
+       
+       if ((res != 0) || (newpos < 0))
+               return(ftp_write(peer, FALSE, 501, REPLY_INVALIDREST));
+       else
+       {
+               peer->restartpos = newpos;
+               return(ftp_write(peer, FALSE, 350, 
REPLY_RESTOK(peer->restartpos)));
+       }
+ }
+ 
+ int ftp_syst(FTPSTATE *peer, char *param)
+ {
+       return(ftp_write(peer, FALSE, 215, "UNIX Type: L8"));
+ }
+       
+ int ftp_port(FTPSTATE *peer, char *param)
+ {
+       if (peer->epsv_forced)
+               return(ftp_write(peer, FALSE, 500, REPLY_EPSVSET));
+ 
+       /* reset remoteport */
+       peer->remoteport = 0;
+       if (peer->passiveport)
+       {
+               select_delfd(peer->sel, peer->passiveport);
+               peer->passiveport = 0;
+       }
+ 
+       if (readipstr(param, &(peer->remoteport), &(peer->dataip)))
+       {
+               if ((peer->remoteport <= 1024) || (peer->remoteport >= 65536))
+               {
+                       peer->remoteport = 0;
+                       return(ftp_write(peer, FALSE, 500, REPLY_PORTBADPORT));
+               }
+               if ((peer->dataip != peer->remoteip) && (!peer->fxpallow))
+               {
+                       peer->remoteport = 0;
+                       peer->dataip = peer->remoteip;
+                       return(ftp_write(peer, FALSE, 500, REPLY_PORTBADFXP));
+               }
+               return(ftp_write(peer, FALSE, 200, REPLY_PORTOK(
+                                peer->dataip >> 24, peer->dataip >> 16 & 255, 
peer->dataip >> 8 & 255,
+                                peer->dataip & 255, peer->remoteport)));
+       }
+       else
+               return(ftp_write(peer, FALSE, 501, REPLY_PORTBADPARM));
+ }
+ 
+ int ftp_eprt(FTPSTATE *peer, char *param)
+ {
+       char *ipstr;
+       int proto, tcpport;
+       unsigned int remote_ip;
+       
+       if (peer->epsv_forced)
+               return(ftp_write(peer, FALSE, 500, REPLY_EPSVSET));
+ 
+       ipstr = mallocwrapper(strlen(param) + 1);
+ 
+       /* clear off old ports */
+       peer->remoteport = 0;
+       if (peer->passiveport)
+       {
+               select_delfd(peer->sel, peer->passiveport);
+               peer->passiveport = 0;
+       }
+ 
+       if (sscanf(param, "|%d|%[0-9.]|%d|", &proto, ipstr, &tcpport) != 3)
+               ftp_write(peer, FALSE, 500, REPLY_EPRTBADPARM);
+       else if (proto != FTP_IPV4)
+               ftp_write(peer, FALSE, 522, REPLY_BADPROTO);
+       else if (!readeipstr(ipstr, &(remote_ip)))
+               ftp_write(peer, FALSE, 500, REPLY_EPRTBADPARM);
+       else if (tcpport <= 1024)
+               ftp_write(peer, FALSE, 500, REPLY_EPRTBADPORT);
+       else if ((remote_ip != peer->remoteip) && (!peer->fxpallow))
+               ftp_write(peer, FALSE, 500, REPLY_EPRTBADFXP);
+       else
+       {
+               /* everything seems to be ok */
+               peer->remoteport = tcpport;
+               peer->dataip = remote_ip;
+               ftp_write(peer, FALSE, 200, REPLY_EPRTOK(
+                                peer->dataip >> 24, peer->dataip >> 16 & 255, 
peer->dataip >> 8 & 255,
+                                peer->dataip & 255, peer->remoteport));
+       }
+       freewrapper(ipstr);
+       return(FALSE);
+ }
+ 
+ int ftp_pwd(FTPSTATE *peer, char *param)
+ {
+       return(ftp_cwddo(peer, ".", FALSE));
+ }
+ 
+ int ftp_type(FTPSTATE *peer, char *param)
+ {
+       if ((param[0] & (255-32)) == 'A')
+       {
+               peer->binary = FALSE;
+               return(ftp_write(peer, FALSE, 200, REPLY_TYPEASCII));
+       }
+       else if ((param[0] & (255-32)) == 'I')
+       {
+               peer->binary = TRUE;
+               return(ftp_write(peer, FALSE, 200, REPLY_TYPEBINARY));
+       }
+       else
+               return(ftp_write(peer, FALSE, 504, REPLY_TYPEUNIMP));
+ }
+ 
+ int ftp_abor(FTPSTATE *peer, char *param)
+ {
+       /* abort any active or pending data port */
+       abortdatasocket(peer);
+       return(ftp_write(peer, FALSE, 226, REPLY_ABORT));
+ }
+ 
+ int ftp_passive_do(FTPSTATE *peer, int *rip)
+ {
+       int pcount, count;
+       
+       if (peer->passiveport != 0)
+               select_delfd(peer->sel, peer->passiveport);
+ 
+       /* if a port range is specified, choose a port within it */
+       if (config->pasvport_first)
+       {
+               pcount = config->pasvport_last - config->pasvport_first + 1;
+               peer->remoteport = config->pasvport_first + (((random() & 
0x00FFFF00) >> 8) % pcount);
+               
+               peer->passiveport = listenparrelelport(peer->remotefd, 
&(peer->remoteport), rip, 5);
+       
+               count = 1;      
+               // while I can't get a port, increment and try again.
+               while ((peer->passiveport == -1) && (count < pcount))
+               {
+                       peer->remoteport++;
+                       if (peer->remoteport > config->pasvport_last)
+                               peer->remoteport = config->pasvport_first;
+                       count++;
+                       peer->passiveport = listenparrelelport(peer->remotefd, 
&(peer->remoteport), rip, 5);
+               }
+       }
+       else
+       {
+               peer->remoteport = 0;
+               peer->passiveport = listenparrelelport(peer->remotefd, 
&(peer->remoteport), rip, 5);
+       }
+ 
+       if ((peer->passiveport != -1) && (config->natip))
+       {
+               /* determine if we should use the IP the stack tells us or if
+                  the stack is lying to us */
+               
+               if (!ipacllist_query(config->nat_localips, peer->remoteip, 
peer->hostname))
+               {
+                       // yep, its a remote user, rewrite ip
+                       *rip = config->natip;
+               }
+       }       
+ 
+       return (peer->passiveport != -1);
+ }
+ 
+ int ftp_pasv(FTPSTATE *peer, char *param)
+ {
+       unsigned int a1, a2, a3, a4, a5, a6;
+       
+       if (peer->epsv_forced)
+               return(ftp_write(peer, FALSE, 500, REPLY_EPSVSET));
+       
+       if (!ftp_passive_do(peer, &a1))
+       {
+               peer->passiveport = 0;
+               reporterror(peer, "passive port", errno);
+               return(FALSE);
+       }
+       select_addfd(peer->sel, peer->passiveport);
+       a2 = ((a1 >> 8) & 255);
+       a3 = ((a1 >> 16) & 255);
+       a4 = ((a1 >> 24) & 255);
+       a1 = a1 & 255;
+       a5 = (peer->remoteport >> 8);
+       a6 = (peer->remoteport & 255);
+       return(ftp_write(peer, FALSE, 227, REPLY_PASV(a4, a3, a2, a1, a5, a6)));
+ }
+ 
+ int ftp_epsv(FTPSTATE *peer, char *param)
+ {
+       int proto, a1;
+ 
+       /* the all parameter means that only epsv can be used from now on */
+ 
+       if (peer->passiveport != 0)
+               select_delfd(peer->sel, peer->passiveport);
+ 
+       peer->remoteport = 0;
+       peer->passiveport = 0;
+       
+       if (param)
+       {
+               if (strcasecmp(param, "ALL") == 0)
+               {
+                       peer->epsv_forced = 1;
+                       return(ftp_write(peer, FALSE, 200, REPLY_EPSVON));
+               } 
+               else if (sscanf(param, "%d", &proto) == 1)
+               {
+                       if (proto != FTP_IPV4)
+                               return(ftp_write(peer, FALSE, 522, 
REPLY_BADPROTO));
+               }
+               else
+                       return(ftp_write(peer, FALSE, 500, REPLY_EPSVERR));
+       }
+               
+       if (!ftp_passive_do(peer, &a1))
+       {
+               peer->passiveport = 0;
+               reporterror(peer, "passive port", errno);
+               return(FALSE);
+       }
+       select_addfd(peer->sel, peer->passiveport);
+ 
+       return(ftp_write(peer, FALSE, 229, REPLY_EPSV(peer->remoteport)));
+ }
+ 
+ int ftp_list(FTPSTATE *peer, char *param)
+ {
+       char *p = param;
+       int done = FALSE;
+       int parm = 0;
+       while (!done)
+       {
+               if (p == NULL)
+                       done = TRUE;
+               else if ((p[0] != '-') && (p[0] != ' ') && (p[0] != 0))
+                       done = TRUE;
+               else
+               {
+                       parm = parm | ftplist_parseflags(p + 1);
+                       p = strchr(p, ' ');
+                       if (p != NULL)
+                               p++;
+               }
+       }
+ 
+       return(ftp_lister(peer, p, FALSE, parm));
+ }
+ 
+ int ftp_nlst(FTPSTATE *peer, char *param)
+ {
+       char *p = param;
+       int done = FALSE;
+       int parm = 0;
+       while (!done)
+       {
+               if (p == NULL)
+                       done = TRUE;
+               else if ((p[0] != '-') && (p[0] != ' ') && (p[0] != 0))
+                       done = TRUE;
+               else
+               {
+                       parm = parm | ftplist_parseflags(p + 1);
+                       p = strchr(p, ' ');
+                       if (p != NULL)
+                               p++;
+               }
+       }
+ 
+       return(ftp_lister(peer, p, TRUE, parm));
+ }
+ 
+ int ftp_cdup(FTPSTATE *peer, char *param)
+ {
+       return(ftp_cwd(peer, ".."));
+ }
+ 
+ int ftp_rnfr(FTPSTATE *peer, char *param)
+ {
+       freeifnotnull(peer->renameoldname);
+       peer->renameoldname = file_expand(peer, param);
+       return(ftp_write(peer, FALSE, 350, REPLY_RENAMESOURCE));
+ }
+ 
+ int ftp_noop(FTPSTATE *peer, char *param)
+ {
+       return(ftp_write(peer, FALSE, 200, REPLY_NOOP));
+ }
+ 
+ int ftp_rein(FTPSTATE *peer, char *param)
+ {
+       if (peer->jailenabled)
+               return(ftp_write(peer, FALSE, 530, REPLY_JAILUSER));
+       if (peer->loggedin)
+               shinfo_delusergroup(peer->groupname);
+ 
+       peer->loggedin = FALSE;
+       abortdatasocket(peer);
+       return(ftp_write(peer, FALSE, 220, "Reinitialize successful, enter 
username."));
+ } 
+ 
+ int ftp_none(FTPSTATE *peer, char *param)
+ {
+       return(FALSE);
+ }
+ 
+ int ftp_help(FTPSTATE *peer, char *param)
+ {
+       return(ftp_dohelp(peer, mainftpcmd, param, peer->cmddisableset));
+ }
+ 
+ int ftp_dohelp(FTPSTATE *peer, FTPCMD *ftpcmds, char *command, int 
*disableset)
+ {
+       char outstr[128];
+       char cmd[10];
+       int result = FALSE;
+       int count2,count = 0;
+       
+       if (command != NULL)
+       {
+               while(((strcasecmp(ftpcmds[count].command, command) != 0) &&
+                    (ftpcmds[count].ftpfunc != NULL)) ||
+                    (peer->loggedin ? disableset[count] : 
ftpcmds[count].needslogin))
+                       count++;
+               
+               if (ftpcmds[count].ftpfunc == NULL)
+                       ftp_write(peer, FALSE, 502, REPLY_HELPUNKNOWN(command));
+               else
+                       ftp_write(peer, FALSE, 214, "%-8s : %s", 
ftpcmds[count].command, ftpcmds[count].helpdesc);
+               return(FALSE);
+       }
+       
+       ftp_write(peer, TRUE, 0, REPLY_HELPSTART);
+       while (ftpcmds[count].ftpfunc != NULL)
+       {
+               count2 = 0;
+               if (config->altlongreplies)
+                       strcpy(outstr, "214-");
+               else
+                       strcpy(outstr, "    ");
+               /* now increment past disabled commands */
+               while((peer->loggedin ? disableset[count] : 
ftpcmds[count].needslogin))
+                       count++;
+ 
+               while ((count2 < 7) && (ftpcmds[count].ftpfunc != NULL))
+               {
+                       snprintf(cmd, 10, "%-9s", ftpcmds[count].command);
+                       strcat(outstr, cmd);
+                       count2++;
+                       count++;
+                       /* now increment past disabled commands */
+                       while((peer->loggedin ? disableset[count] : 
ftpcmds[count].needslogin))
+                               count++;
+               }
+               ftp_write(peer, TRUE, 0, outstr);
+       }
+       
+       result = ftp_write(peer, FALSE, 214, 
REPLY_HELPEND(peer->vserver->email)); 
+ 
+       return(result);
+ }
+ 
+ int ftp_allo(FTPSTATE *peer, char *param)
+ {
+       return(ftp_write(peer, FALSE, 202, REPLY_ALLO));
+ }
+ 
+ int ftp_acct(FTPSTATE *peer, char *param)
+ {
+       return(ftp_write(peer, FALSE, 200, REPLY_ACCT));
+ }
+ 
+ int ftp_stru(FTPSTATE *peer, char *param)
+ {
+       if ((param[0] & (255-32)) == 'F')
+               return(ftp_write(peer, FALSE, 200, REPLY_STRUFILE));
+       else
+               return(ftp_write(peer, FALSE, 500, REPLY_STRUUNKNOWN));
+ }
Index: muddleftpd/src/ftpd.h
diff -c /dev/null muddleftpd/src/ftpd.h:1.2.4.1
*** /dev/null   Tue Nov  4 10:41:23 2003
--- muddleftpd/src/ftpd.h       Tue Nov  4 10:41:22 2003
***************
*** 0 ****
--- 1,812 ----
+ /* Copyright (C) 1999 Beau Kuiper
+ 
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+ 
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+ 
+ /* Includes */
+ 
+ #ifndef DEBUG         /* If we are not debuging, disable assertions! */
+ #define NDEBUG
+ #endif
+ 
+ #include "../config.h"        /* Insert the config header */
+ #include "../defaults.h"   /* insert default info */
+ 
+ #include <assert.h>   /* enable assertions */
+ 
+ #include "myglob.h"           /* use our simple glob */
+ #include "util/strerror.h"
+ #include <sys/types.h>
+ #ifdef HAVE_SYS_CAPABILITY_H
+ #include <sys/capability.h>
+ #undef WNOHANG
+ #undef WUNTRACED
+ #endif
+ #include <sys/ipc.h>
+ #include <sys/shm.h>
+ #include <pwd.h>
+ #include <grp.h>
+ #include <stdarg.h>
+ #include <sys/wait.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <syslog.h>
+ #include <string.h>
+ #include <netinet/in.h>
+ #include <netinet/tcp.h>
+ #include <netdb.h>
+ #include <sys/socket.h>
+ #include <sys/time.h>
+ #include <sys/stat.h>
+ #include <sys/resource.h>
+ #include <fcntl.h>
+ #include <errno.h>
+ #include <unistd.h>
+ #include <time.h>
+ #include <signal.h>
+ #include <ctype.h>
+ #include <dirent.h>
+ #include <malloc.h>
+ #include <syslog.h>
+ #include <setjmp.h>
+ 
+ #ifdef HAVE_DLFCN_H
+ #include <dlfcn.h>
+ #endif
+ 
+ #ifdef HAVE_CRYPT_H
+ #include <crypt.h>
+ #endif
+ 
+ #ifdef HAVE_GETSPNAM
+ #include <shadow.h>
+ #endif
+  
+ #ifndef INT_MAX
+ #define INT_MAX 0x7FFFFFFF
+ #endif
+ 
+ #include "pnums.h"
+ #include "configfile.h"
+ #include "acl.h"
+ #include "tokset.h"
+ #include "ratio.h"
+ #include "string.h"
+ 
+ /* Inportant Constants */
+ 
+ #ifndef FALSE
+ #define               FALSE           0
+ #endif
+ #ifndef TRUE
+ #define               TRUE            !FALSE
+ #endif
+ #define       SYSLOG          1
+ #define       TERMINAL        2
+ #define       MUDLOG          3
+ 
+ #define               MYLOG_DACCESS   1
+ #define               MYLOG_FTRANS    2
+ #define               MYLOG_COMMAND   4
+ #define               MYLOG_RESPONSE  8
+ #define               MYLOG_INFO      16
+ #define       MYLOG_LOGIN     32
+ #define       MYLOG_DEBUG     64
+ 
+ #define               TRANS_LIST      1
+ #define       TRANS_UPLOAD    2
+ #define               TRANS_DOWNLOAD  3
+ #define               TRANS_SUPLOAD   4
+ #define               TRANS_RUNNING   5
+ 
+ /* used in authentication modules */
+ #define               AUTH_OK         0
+ #define               AUTH_USERNKNOW  1
+ #define       AUTH_ERROR      2
+ 
+ /* used for config errors */
+ 
+ #define               CONFIG_OK            0
+ #define               CONFIG_HANDLER_ERROR 1
+ #define       CONFIG_FILE_UNSAFE   2
+ #define               CONFIG_FILE_ERROR    3
+ 
+ #define               CFC_INCLUDE_ERROR    10
+ #define               CFC_SECTION_NONE     11
+ #define               CFC_SECTION_EXISTS   12
+ #define               CFC_NO_SECTION       13
+ #define               CFC_INCLUDE_LOOP     14
+ 
+ /* used to define maximum IP address size (for ipv6 support) */
+ 
+ #define               MAX_IPSIZE      16
+ 
+ #define               FTP_IPV4        1
+ #define               FTP_IPV6        2
+ 
+ /* DO NOT CHANGE THESE DEFINES. THEY ARE REQUIRED CONSTANTS */
+ 
+ #define CR                    13
+ #define LF                    10
+ 
+ /* Macros */
+ 
+ #define MAXIMUM(x, y)         ((x) > (y) ? (x) : (y))
+ #define MINIMUM(x, y)         ((x) > (y) ? (y) : (x))
+ #define ERRORMSG(x)           errormsg(x, __FILE__, __LINE__)
+ #define ERRORMSGFATAL(x)      errormsgfatal(x, __FILE__, __LINE__)
+ 
+ #ifndef FNM_PATHNAME
+ #define FNM_PATHNAME 1
+ #endif
+ 
+ /* this specifies that the operation cannot be stopped by a signal */
+ 
+ #define NOSIGNALINTR(x)               while(((x) == -1) && (errno == EINTR))
+ 
+ /* Typedefs */
+ 
+ typedef unsigned int ipaddr_mt[MAX_IPSIZE];
+ 
+ typedef struct
+ {
+       int fd;
+       STRING *buffer;
+       int eof;
+ } NEWFILE;
+ 
+ typedef struct strcache
+ {
+       int size;
+       struct {
+               int num;
+               char *str;
+              } data[STRCACHESIZE];
+ } STRCACHE;
+ 
+ struct selector;
+ 
+ struct selectorobj
+ {
+       int (* readsockopt)(struct selector *, int, void *);
+       int (* writesockopt)(struct selector *, int,void *);
+       void *readdata;
+       void *writedata;
+       int next;               /* linked list of file descriptors */
+       int last;
+ };
+ 
+ typedef struct selector
+ {
+       fd_set readset;
+       fd_set writeset;
+       struct selectorobj **fdtable;
+       int firstfd;
+       int maxfds;
+       int smax;
+ } SELECTER;
+ 
+ typedef struct limiter
+ {
+       int maxspeed;
+       struct timeval current_time;
+       int bytes_transfered;
+ } LIMITER;
+ 
+ typedef struct
+ {
+       int pos;
+       MYGLOBDATA *dirdata;
+       int nlist;
+       int recursive;
+       int all;
+       STRCACHE *uidcache;
+       STRCACHE *gidcache;
+       char *dir;
+       char *rdir;
+       int odirlen;
+       int numsubdirs;
+       char **subdirs;
+       unsigned int year;              /* this is stored as 4 chars */
+ } LISTHANDLE;
+ 
+ typedef struct dataport
+ {
+       int filefd;
+       int socketfd;
+       off_t pos;
+       off_t startpos;
+       off_t tsize;
+       STRING *buffer;
+       LISTHANDLE *lhandle;
+       int trans_type;
+       int binary;
+       int passive;
+       off_t transbytes;
+       LIMITER *download_limiter;
+       LIMITER *upload_limiter;
+ } DATAPORT;
+ 
+ typedef struct
+ {
+       unsigned int ipaddr;
+       unsigned int netmask; 
+       char *fnstr;
+       int type;
+       int accept;
+ } IPACL;
+ 
+ typedef struct
+ {
+       int count;
+       int defaultret;
+       IPACL *list;
+ } IPACLLIST;
+ 
+ typedef struct vserver
+ {
+       IPACLLIST *ipaccess;
+       int maxusers;
+       int loglevel;
+       int timeout;
+       char *logfile;
+       char *email;
+       int logincount;
+       int authwait; 
+       int umask;
+       int maxperip;
+       char *sectionname;
+       char **grouplist;
+       char *vhostname;
+       char *prelogindump;
+       char *prelogindumpdata;
+       char *greetline;
+       char *toobusy;
+       struct vserver *next;
+ } VSERVER;
+ 
+ typedef struct vserverconn
+ {
+       int port;
+       int ip;
+       int fd;
+       VSERVER *vptr;
+       struct vserverconn *next;
+ } VSERVERCONN;
+ 
+ typedef struct configdata
+ {
+       CONFIGFILECACHE *configfile;
+ 
+       pid_t parentpid;      /* the pid to signal when an fatal error occurs */
+ 
+       int rootmode;         /* are we in root mode or not */
+       int logout;           /* log context */
+ 
+       uid_t uidt_asuid;             /* change to this user after listening to 
port */
+       gid_t gidt_asgid;
+ 
+       int toobusycount;     /* number of connections being given too busy
+                                messages by main thread */
+       char *username;       /* the username muddleftpd will run as */
+       uid_t uidt_nobodyuid;      /* uid for user nobody */
+       gid_t gidt_nobodygid;      /* gid for user nobody */
+ 
+       int inetd;            /* are we running as inetd */
+       int hostvservers;     /* are we running with hostname only
+                                virtual servers */
+       int dnstimeout;       /* maximum time to wait for reversedns */
+       int altlongreplies;   /* use alternate long replies, more compatible 
+                                with crappy clients */
+       int smartbind;        /* specifies whether to search for effecient
+                                fd usage, or correct binding */
+       int zerobind;         /* bind all ports using address 0.0.0.0, use
+                                with smartbind on Freebsd to use
+                                virtual servers */
+       int pasvport_first;   /* When a passive connection is to be made */
+       int pasvport_last;    /* this can specify what ports can be used */
+                             /* useful for firewalls or NAT */
+       int natip;            /* if the ftp server is behind NAT, this stores */
+                             /* the real IP address */
+       IPACLLIST *nat_localips; /* if the ftp server is behind NAT, this 
stores */
+                             /* the access list that determines if the what IP 
*/
+                             /* to show for the PASV command */
+ 
+       char *hostname;       /* Computer host name */
+       VSERVER *defaults;    /* Default values for VSERVER */
+       VSERVER *defaulthost; /* The default host for hostvservers */
+       char **vserverlist;   /* list of virtual servers names */
+       VSERVER *vservers;    /* virtual server list */
+       VSERVERCONN *inports; /* input ports */
+       
+ } CONFIGDATA;
+       
+ typedef struct ftpstate
+ {
+       int remotefd;           /* fd of the port to the remote system */
+       unsigned int remoteip;          /* the systems IP number */
+       int connport;           /* port connected to */
+       char *username;         /* the username of the ftp user */
+       char *pwd;              /* the current dir the ftp user is in */
+       char *homedir;          /* the home directory of the ftp user */
+       char *basedir;          /* the / directory for that user */
+       int passiveport;        /* the fd of the listening passive port */
+       char *renameoldname;    /* the source file to rename */
+       int loggedin;           /* true if the user is logged in */
+       off_t restartpos;       /* the position to start a download or upload */
+       int remoteport;         /* the port number to connect to for data conns 
*/
+       int binary;             /* true if binary mode is set */
+       int threadnum;          /* the number of the thread in ftpd terms (not 
pid) */
+       int *cmddisableset;     /* a set of integers used to disable commands */
+       int *sitedisableset;    /* a set of integers used to disable site 
commands */
+       STRING *inbuffer;       /* the input buffer */
+ 
+       char *logindump;        /* file to print after login */
+       char *cwddump;          /* dump this file when you move to this dir */
+       char *busydump;         /* dump this file if too many users are logged 
in */
+       char *quitdump;         /* dump this file when the user quits */
+ 
+       char *logindumpdata;    /* this is for login dump */
+       char *cwddumpdata;      /* this is when we change dir */
+       char *busydumpdata;     /* this is when we are too busy */
+       char *quitdumpdata;     /* this is when we quit */
+ 
+       ACLLIST *acldata;       /* The access lists for this user */
+       int umask;              /* the umask of newly created files */
+       gid_t gidt_asgid;       /* all file work is done as this gid (if 
possible) */
+       uid_t uidt_asuid;       /* all file work is done as this uid (if 
possible) */
+       int maxusers;           /* maximum instances of this user allowed to be 
logged in */
+       int maxtimeout;         /* maximum time out for this user */
+       int timeout;            /* timeout for this user */
+       int chmodable;          /* can do chmod and change umask */
+       SELECTER *sel;          /* select data stored here */
+       DATAPORT *dport;        /* Data port information */
+       int loginsleft;         /* number of login attempts left */
+       int jailenabled;        /* is this user a jail */
+       int chroot;             /* does a chroot occur */
+       int droproot;           /* do we drop root when logging in */
+       int nicevalue;          /* what niceness to run as */
+       int realdir;            /* use real directory instead of built up
+                                  name */
+       int fxpallow;           /* allow fxp(server-server) transfers */
+       int usercount;          /* the number of users in the current group 
when the user logged on */
+       char *hostname;         /* the hostname of the remote host */
+       int accessdevices;      /* can the user access device files */ 
+       char *groupname;
+       int fakemode;
+       char *fakename;
+       char *fakegroup;
+       VSERVER *vserver;       /* vserver the user is connected to */
+       RATIOHANDLE *ratioinfo; /* ratio info */
+       gid_t *supgids;         /* user supplementary gids */
+       unsigned int dataip;    /* ip to connect data port to */
+       STRING *outbuffer;      /* output coalessing buffer */
+       int maxtranspd;         /* Maximum transfer speed */
+       int maxtranspd_down;    /* Maximum download speed, overrides maxtranspd 
*/
+       int maxtranspd_up;      /* Maximum upload speed, overrides maxtranspd */
+       int epsv_forced;        /* EPSV ALL was called, deny everything else */
+       
+       /* Counters for stats */
+       int downloadedfiles;
+       int uploadedfiles;
+       off_t downloadedfilebytes;
+       off_t uploadedfilebytes;
+       off_t listdownloadedbytes;
+       int listconns;
+ } FTPSTATE;
+ 
+ typedef struct ftpcmd
+ {
+       char *command;
+       int (* ftpfunc)(FTPSTATE *peer, char *params);
+       int paramnum;
+       int needslogin; 
+       int dataportok;
+       char *helpdesc;
+ } FTPCMD;
+ 
+ typedef struct inputline
+ {
+       FTPCMD *command;
+       char *parameters;
+ } INPUTLINE;
+ 
+ /* GLOBAL VARIABLES */
+ 
+ CONFIGDATA *config;     /* configuration data stored here for easy access */
+ int inetd;              /* is process running as inetd */
+ int logerrors;                  /* do we log config errors to screen, syslog, 
or
+                            screen */
+ 
+ /* these prototypes are mostly auto generated by scribe */
+ 
+ /* procnum.h */
+ 
+ void writescratch(int pos, int size, char *buff);
+ void writeshmem(int num, int pos, int len, void *buff);
+ void shinfo_addtogrouplist(VSERVER *vs);
+ void shinfo_setuparea(void);
+ void shinfo_init(char *scfilename);
+ void inetd_init(char *scfile);
+ void shinfo_reinit(void);
+ int shinfo_addstandalone(int newthid, char *vservername, unsigned int ip, int 
viplimit, int iplimit);
+ int shinfo_adduser_inetd(unsigned int ip, int slimit, int iplimit, int 
*error);
+ int shinfo_adduser(char *vserver, unsigned int ip, int vlimit, int slimit, 
int viplimit, int iplimit);
+ int shinfo_setvserver(int thid, char *vservername, unsigned int ip, int 
vlimit, int viplimit, int *error);
+ int shinfo_newuser_standalone(unsigned int ip, int iplimit, int *error);
+ void shinfo_changeop(char *operation);
+ void shinfo_changeuser(char *username);
+ void shinfo_sethost(char *hostname);
+ int shinfo_addusergroup(char *groupname, int limit);
+ void shinfo_setpid(int thrid, int pid);
+ void shinfo_delbypid(int pid);
+ void shinfo_delusergroup(char *groupname);
+ void shinfo_freebynum(int threadnum);
+ void shinfo_freethreads(int freecount, pid_t *freelist);
+ void pnums_signalchildren(int signalnum);
+ void shinfo_shutdown(void);
+ 
+ /* socket.h */
+ 
+ char *getipstr(unsigned int server);
+ void takentoolong(int sig);
+ char *getnetworkstr(unsigned int server);
+ int getnetworkint(char *server, unsigned int *out);
+ int conn_server(unsigned int ip, int port);
+ int conn_server_nonblocking(unsigned int ip, int port, int localport, int fd);
+ int listenport(int port, unsigned int ip, int maxconnect);
+ int get_conn(int socketin, unsigned int *addr);
+ int listenparrelelport(int fd, int *port, unsigned int *ip, int maxconnect);
+ void getsockinfo(int fd, unsigned int *ip, int *port);
+ unsigned int getremoteip(int fd);
+ void socket_flush_wait(int fd, int timeout);
+ 
+ /* select.h */
+ 
+ SELECTER *select_new(void);
+ void select_addfd(SELECTER *sel, int newport);
+ void select_delfd(SELECTER *sel, int deadport);
+ void select_addread(SELECTER *sel, int port, int (* proc)(SELECTER *, int, 
void *), void *dat);
+ void select_addwrite(SELECTER *sel, int port, int (* proc)(SELECTER *, int, 
void *), void *dat);
+ void select_takeread(SELECTER *sel, int port);
+ void select_takewrite(SELECTER *sel, int port);
+ int select_do(SELECTER *sel, int *signum, int timeout);
+ void select_shutdown(SELECTER *sel);
+ 
+ /* ftpsite.h */
+ 
+ int ftp_site(FTPSTATE *peer, char *cmd);
+ int ftpsite_dump(FTPSTATE *peer, char *cmd);
+ int ftpsite_umask(FTPSTATE *peer, char *cmd);
+ int ftpsite_help(FTPSTATE *peer, char *cmd);
+ int ftpsite_idle(FTPSTATE *peer, char *cmd);
+ int ftpsite_chmod(FTPSTATE *peer, char *cmd);
+ int ftpsite_access(FTPSTATE *peer, char *cmd);
+ 
+ /* proxy.h */
+ 
+ int controlportgotdata(SELECTER *sel, int fd, void *peerdata);
+ char *remove_rootcomponent(FTPSTATE *peer, char *filename, char *descript);
+ void dochroot(FTPSTATE *peer);
+ void rotatelogs(FTPSTATE *peer);
+ void ftpserverside_main(int remotefd, int remoteip, int threadnum, int 
portnum, VSERVER *vserver);
+ int inport_bind(int portid, unsigned int bindip, int dodie);
+ int inport_getconn(SELECTER *mainsel, int fd, void *vs);
+ int vserver_select(FTPSTATE *peer, VSERVER *vserver);
+ 
+ /* version.h */
+ 
+ void showversion(char *desc);
+ 
+ /* acl.h */
+ 
+ ACLLIST *acllist_create(void);
+ char *acllist_makepermstr(ACLLIST *acl, int cnt, char *ret);
+ void acllist_add(ACLLIST *acl, char *dir, char *attribs, int is_regexp);
+ void acllist_tokendo(ACLLIST *acl, TOKENSET *tset);
+ int acllist_check(ACLLIST *acl, char *dir, int attr);
+ int check_acl(FTPSTATE *peer, char *path, int perm);
+ void acllist_dest(ACLLIST *acl);
+ 
+ /* ftpout.h */
+ 
+ void control_timeout(int num);
+ int ftp_write(FTPSTATE *peer, int bare, int messnum, char *fmt, ...);
+ 
+ /* utils.h */
+ 
+ void *mallocwrapper(int size);
+ void reallocwrapper(int size, void **inarea);
+ char *strdupwrapper(char *s);
+ void freewrapper(void *tofree);
+ void freeifnotnull(void *);
+ char *offt_tostr(off_t size);
+ int strto_offt(char *str, off_t *ret);
+ 
+ #ifndef HAVE_MEMMOVE
+ void *memmove(void *, const void *, int);
+ #endif
+ 
+ void strtrimspace(char *string);
+ int strchrcount(char *string, char tok);
+ char *safe_vsnprintf(int size, char *format, va_list ap);
+ char *safe_snprintf(char *format, ...);
+ char *getcwd2(void);
+ void pathname_simplify(char *pathname);
+ void test_libc(int verbose);
+ 
+ #ifndef HAVE_USLEEP
+ int usleep(int usecs);
+ #endif
+ 
+ STRCACHE *strcache_new(void);
+ char *strcache_check(STRCACHE *cache, int num);
+ void strcache_add(STRCACHE *cache, int num, char *str);
+ void strcache_free(STRCACHE *cache);
+ TOKENSET *tokenset_new(void);
+ void tokenset_settoken(TOKENSET *tset, unsigned char tok, char *data);
+ void tokenset_deltoken(TOKENSET *tset, unsigned char tok);
+ char *tokenset_apply(TOKENSET *tok, char *inputstr, int);
+ void tokenset_finish(TOKENSET *tset);
+ 
+ void init_pwgrfiles(void);
+ char *get_passwdname(uid_t inuid, int usefile);
+ char *get_groupname(gid_t ingid, int usefile);
+ gid_t *getusergrouplist(char *username);
+ gid_t *newgidlist(void);
+ gid_t *addgidlist(gid_t *list, gid_t new);
+ void delgidlist(gid_t *list, gid_t old);
+ gid_t *parsegidlist(char *str);
+ char *makegidliststr(gid_t *list);
+ void kill_uidgidfiles(void);
+ int isfilesafe(int fd);
+ LIMITER *limiter_new(int maxspeed);
+ void limiter_add(LIMITER *l, int byte_count, int force);
+ int giveuproot(uid_t uid, gid_t gid);
+ void errormsg( char *errmessage, char *file, int line );
+ void errormsgfatal( char *errmessage, char *file, int line );
+ void blockallsignals( void );
+ void unblockallsignals( void );
+ 
+ /* init.h */
+ 
+ void ftpd_setnogroups(void);
+ void ftpd_preinit(void);
+ void ftpd_init(char *filename, int verbose);
+ 
+ /* ftp.h */
+ 
+ void reporterror(FTPSTATE *peer, char *filename, int errorno);
+ int readipstr(char *data, int *port, unsigned int *ip);
+ int ftp_dele(FTPSTATE *peer, char *filename);
+ int ftp_chmod(FTPSTATE *peer, char *filename, int mode);
+ int ftp_size(FTPSTATE *peer, char *filename);
+ int ftp_mdtm(FTPSTATE *peer, char *filename);
+ void setdumptokens(FTPSTATE *peer, TOKENSET *ts);
+ int ftp_dumper(FTPSTATE *peer, NEWFILE *infile, int number, char *fmessage, 
int dotokens, int endtokens);
+ int ftp_dumpstr(FTPSTATE *peer, char *dumpstr, int number, char *fmessage, 
int endtokens);
+ int ftp_rnto(FTPSTATE *peer, char *filename);
+ int ftp_mkd(FTPSTATE *peer, char *filename);
+ int ftp_rmd(FTPSTATE *peer, char *filename);
+ int ftp_cwddo(FTPSTATE *peer, char *newdir, int dump);
+ int ftp_cwd(FTPSTATE *peer, char *newdir);
+ int ftp_run(FTPSTATE *peer, INPUTLINE *cmd, char *token);
+ int ftp_quit(FTPSTATE *peer, char *param);
+ int ftp_user(FTPSTATE *peer, char *param);
+ int ftp_pass(FTPSTATE *peer, char *param);
+ int ftp_rest(FTPSTATE *peer, char *param);
+ int ftp_syst(FTPSTATE *peer, char *param);
+ int ftp_port(FTPSTATE *peer, char *param);
+ int ftp_pwd(FTPSTATE *peer, char *param);
+ int ftp_type(FTPSTATE *peer, char *param);
+ int ftp_abor(FTPSTATE *peer, char *param);
+ int ftp_pasv(FTPSTATE *peer, char *param);
+ int ftp_list(FTPSTATE *peer, char *param);
+ int ftp_nlst(FTPSTATE *peer, char *param);
+ int ftp_cdup(FTPSTATE *peer, char *param);
+ int ftp_rnfr(FTPSTATE *peer, char *param);
+ int ftp_noop(FTPSTATE *peer, char *param);
+ int ftp_rein(FTPSTATE *peer, char *param);
+ int ftp_none(FTPSTATE *peer, char *param);
+ int ftp_help(FTPSTATE *peer, char *param);
+ int ftp_dohelp(FTPSTATE *peer, FTPCMD *ftpcmds, char *command, int 
*disableset);
+ int ftp_allo(FTPSTATE *peer, char *param);
+ int ftp_acct(FTPSTATE *peer, char *param);
+ int ftp_stru(FTPSTATE *peer, char *param);
+ 
+ /* ftpstat.h */
+ 
+ int ftp_stat(FTPSTATE *peer, char *param);
+ 
+ /* cmd.h */
+ 
+ int *disableset_create(void);
+ void disableset_disablecmd(int *set, FTPCMD *ctable, char *cmd);
+ char *getkeyword(char **iline);
+ void cmd_split(FTPSTATE *peer, INPUTLINE *cmd, char* inpline, FTPCMD *ctable, 
int log, int *disableset);
+ 
+ /* logger.h */
+ 
+ int log_initcontext(char *logname);
+ void log_setcontext(int logfd, int logmask);
+ void log_addentry(int type, FTPSTATE *peer, char *desc);
+ void log_giveentry(int type, FTPSTATE *peer, char *desc);
+ void debuglog(char *format, ...);
+ void log_shutdown(void);
+ 
+ /* dir.h */
+ 
+ char *dir_getreal(FTPSTATE *peer);
+ char *dir_getvirtual(FTPSTATE *peer, char *);
+ void dir_combine(FTPSTATE *peer, char **pwd, char *newdir);
+ 
+ /* shmem.h */
+ 
+ void shmem_finish(int shmemnum);
+ void *shmem_get(char *ipcfile, int size, int *shmemnum, int *isnew, int 
*lockfd);
+ 
+ /* ftplist.h */
+ 
+ int ftplist_parseflags(char *params);
+ char *listmakeline(FTPSTATE *peer, char *longname, char *filename, struct 
stat *fileinfo, STRCACHE *uidcache, STRCACHE *gidcache, int year);
+ LISTHANDLE *getlisthandle(FTPSTATE *peer, char *dirpattern, int nlist, char 
*reldir, int params);
+ char *getlisthandleline(FTPSTATE *peer, LISTHANDLE *lh);
+ void freelisthandle(LISTHANDLE *lh);
+ int list_write(SELECTER *sel, int fd, void *peerv);
+ char *listmakepattern(FTPSTATE *peer, char *parm, char **rdir, int flags);
+ int ftp_lister(FTPSTATE *peer, char *parm, int nlist, int params);
+ 
+ /* checkip.h */
+ 
+ int checknamelist(CONFIGFILECACHE *cf, int section, char *username);
+ IPACLLIST *ipacllist_new(CONFIGFILECACHE *cf, int section, char *setting, 
char acceptchar, int defaultret);
+ void ipacllist_destroy(IPACLLIST *list);
+ int ipacllist_query(IPACLLIST *list, int ip, char *hostname);
+ 
+ /* ftpstate.h */
+ 
+ void ftpstate_init(FTPSTATE *ftp, int fd, int ip, int threadnum, int portnum, 
VSERVER *vs);
+ void ftpstate_dest(FTPSTATE *ftp);
+ 
+ /* bufread.h */
+ 
+ int popcmd(FTPSTATE *peer, char **outstr);
+ int readcmd(FTPSTATE *peer);
+ 
+ /* config.h */
+ 
+ int loadconfigfile(char *filename, int (* confighandler)(char *, char *, int, 
void *), 
+                  void *configdata, int bescure, int *handlererror, int 
*linenum);
+ int cachemaker(char *cmd, char *data, int linenum, void *c);
+ CONFIGFILECACHE *loadconfigcache(char *filename, int *linenum, int *error);
+ char *config_errorstr(int result);
+ int getsectionid(CONFIGFILECACHE *cache, char *section);
+ char *getconfigdata_r(CONFIGFILECACHE *cache, int section, char *cmd, int 
*occur, int depth, int *error);
+ char *getconfigdata(CONFIGFILECACHE *cache, int section, char *cmd, int 
occur);
+ char **makeconfiglist(CONFIGFILECACHE *cache, char *section, char *label);
+ void loadintfromconfig(CONFIGFILECACHE *cache, int section, char *setting, 
+                       int *to, int def);
+ void loadstrfromconfig(CONFIGFILECACHE *cache, int section, char *setting, 
+                       char **to, char *def);
+ void freeconfigcache(CONFIGFILECACHE *cache);
+ 
+ /* ftptrans.h */
+ 
+ int sigio_handler(int signum);
+ int filter_toascii(char *data, int *len);
+ int filter_fromascii(char *data, int *len);
+ int download_write(SELECTER *sel, int fd, void *peerv);
+ int upload_read(SELECTER *sel, int fd, void *peerv);
+ int ftp_retr(FTPSTATE *peer, char *filename);
+ int ftp_stor(FTPSTATE *peer, char *filename);
+ int ftp_stou(FTPSTATE *peer, char *filename);
+ int ftp_appe(FTPSTATE *peer, char *filename);
+ 
+ /* newfile.h */
+ 
+ int readbuffer(NEWFILE *file);
+ NEWFILE *nfopen(char *filename);
+ NEWFILE *nfdopen(int fd);
+ char *nfgetcs(NEWFILE *file, char testchar);
+ void nfclose(NEWFILE *file);
+ 
+ /* main.h */
+ 
+ void logfullmessage(int error, unsigned int ip);
+ VSERVER *findvserver(unsigned int ip, int port);
+ void smartbind(SELECTER *selset, VSERVERCONN *list, int dodie);
+ void dumbbind(SELECTER *selset, VSERVERCONN *list, int dodie);
+ void sighandler(int signum);
+ int mainprog(char *fconfig, int runforeground, int verbose);
+ void mainprog_inetd(char *fconfig, unsigned int ip);
+ void diehandler(int signum);
+ int main(int argc, char **argv);
+ 
+ /* datasock.h */
+ 
+ int datasock_connect(SELECTER *sel, int fd, void *peerv);
+ int startdatasocket(FTPSTATE *peer, int filefd, int trans_type, off_t tsize);
+ void closedatasocket(FTPSTATE *peer);
+ void abortdatasocket(FTPSTATE *peer);
+ 
+ /* auth.h */
+ 
+ void converttorealstr(char *strbuf);
+ int chkpassword(char *encrypass, char *password);
+ int checkexploits(FTPSTATE *peer);
+ int authlogmsg(char *directive, char *groupname);
+ int checkabsdir(char *dir);
+ void clearauth(FTPSTATE *peer);
+ char *mkconfrealstr(int sectionid, char *item, char *defaul);
+ char *mktokconfstr(TOKENSET *tset, int sectionid, char *item, char *defaul);
+ int mktokconfint(TOKENSET *tset, int sectionid, char *item, char *format,
+                char *defaultstr, int defaultint);
+ void setupacls(FTPSTATE *peer, TOKENSET *tset, int section, char *funcname, 
int aclfunc);
+ int transferconfig(FTPSTATE *peer, TOKENSET *tset, int section);
+ int auth_getcursectionid(FTPSTATE *peer);
+ void *auth_getconfigcache(void);
+ char *setuseropts(FTPSTATE *peer, char *password);
+ 
+ /* file.h */
+ 
+ void file_becomeuser(FTPSTATE *peer);
+ void file_becomeroot(FTPSTATE *peer);
+ int checkdir(char *dir);
+ int checkfile(char *file);
+ int checkchdir(FTPSTATE *peer, char *dir);
+ void convertperms(char *permstr, int mode);
+ char *file_expand(FTPSTATE *peer, char *filename);
+ int file_isfdregularfile(int fd);
+ int file_isfdadir(int fd);
+ int file_ustoreopen(FTPSTATE *peer, char *, int *, char **rname);
+ int file_storeopen(FTPSTATE *peer, char *infile, char **rname);
+ int file_readopen(FTPSTATE *peer, char *infile, char **rname);
+ NEWFILE *file_nfopen(FTPSTATE *peer, char *filename);
+ MYGLOBDATA *file_glob(FTPSTATE *peer, char *dir, char *pattern, int allfiles);
+ int file_unlink(FTPSTATE *peer, char *infile);
+ char *file_rename(FTPSTATE *peer, char *infile, char *infile2);
+ int file_stat(FTPSTATE *peer, char *infile, struct stat *output);
+ int file_mkdir(FTPSTATE *peer, char *infile);
+ int file_rmdir(FTPSTATE *peer, char *infile);
+ int file_chmod(FTPSTATE *peer, char *infile, int mode);
+ 
+ /* cfloader.h */
+ 
+ char *gethostname2(void);
+ void configerror(char *str);
+ void vserver_kill(VSERVER *c);
+ VSERVER *vserver_load(CONFIGDATA *cf, char *sectionname, VSERVER *def, 
VSERVERCONN ***vp);
+ void ftpd_killconfig(CONFIGDATA *dc);
+ CONFIGDATA *ftpd_loadconfig(char *inidata, int as_inetd, int use_umask);
+ int ftpd_checkvserver(CONFIGDATA *cdat, VSERVER *vs);
+ int ftpd_checkconfig(CONFIGDATA *cdat);
+ 
+ /* ratio.h */
+ 
+ int lockarea(int fd, int pos, int len, int locktype, int do_wait);
+ void lock_n_read(RATIOHANDLE *rh);
+ void write_n_unlock(RATIOHANDLE *rh);
+ int openratiofile(char *filename);
+ int decoderatiostr(char *ratstr, int *pa, int *pb);
+ RATIOHANDLE *ratio_loaduser(char *username, int section);
+ void ratio_uploadbytes(RATIOHANDLE *rh, int bytes);
+ int ratio_downloadbytes(RATIOHANDLE *rh, int bytes);
+ void ratio_uploadfile(RATIOHANDLE *rh);
+ int ratio_downloadfile(RATIOHANDLE *rh);
+ void ratio_reread(RATIOHANDLE *rh);
+ void ratio_stat(FTPSTATE *peer, RATIOHANDLE *rh, char *replystr);
+ void ratio_settokens(RATIOHANDLE *rh, TOKENSET *ts);
+ void ratio_finish(RATIOHANDLE *rh);
+ 
+ 
Index: muddleftpd/src/proxy.c
diff -c /dev/null muddleftpd/src/proxy.c:1.1.8.1
*** /dev/null   Tue Nov  4 10:41:23 2003
--- muddleftpd/src/proxy.c      Tue Nov  4 10:41:22 2003
***************
*** 0 ****
--- 1,454 ----
+ /* Copyright (C) 1999 Beau Kuiper
+ 
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+ 
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+ 
+ #include "ftpd.h"
+ #include "ftpcmd.h"
+ #include "reply.h"
+ 
+ typedef struct overload_info
+ {
+       int pos;
+       char *msg;
+       int len;
+ } OVERLOAD_INFO;
+ 
+ extern FTPCMD mainftpcmd[];
+ extern pid_t *deadlist;
+ 
+ int controlportgotdata(SELECTER *sel, int fd, void *peerdata)
+ {
+       FTPSTATE *peer = (FTPSTATE *)peerdata;
+       char *instring = NULL;
+       int connclosed = readcmd(peer);
+       int exits = FALSE;
+       INPUTLINE cmd;
+ 
+       if (connclosed)
+               return(TRUE);
+       else 
+       {
+               if (popcmd(peer, &instring))
+               {
+                       ftp_write(peer, FALSE, 500, REPLY_ONECMDONLY);
+                       return(TRUE);
+               }
+               if (instring)   /* we have a command */
+               {
+                       cmd_split(peer, &cmd, instring, mainftpcmd, TRUE, 
+                                 (peer->loggedin ? peer->cmddisableset : 
NULL));
+                       if (!peer->dport)
+                               if (cmd.command->ftpfunc != ftp_pass)
+                                       shinfo_changeop(instring);
+                               
+                       exits = ftp_run(peer, &cmd, "FTP"); 
+                       freeifnotnull(cmd.parameters);
+                       if (!peer->dport)
+                               shinfo_changeop("idle");
+                                                       
+                       string_clear(&(peer->inbuffer));
+               }       
+               return(exits);
+       }
+ }
+ 
+ char *remove_rootcomponent(FTPSTATE *peer, char *filename, char *descript)
+ {     
+       int baselen = strlen(peer->basedir);
+       pathname_simplify(filename);
+       
+       if (strncmp(peer->basedir, filename, baselen) == 0)
+               memmove(filename, filename + baselen, strlen(filename) - 
baselen + 1);
+       else
+       {
+               log_giveentry(MYLOG_INFO, NULL, safe_snprintf("%s(%s) is 
outside of rootdir(%s) with chroot enabled!", descript, filename, 
peer->basedir));
+               freewrapper(filename);
+               return(NULL);
+       }
+       return(filename);
+ }
+ 
+ void dochroot(FTPSTATE *peer)
+ {
+       /* this prevents bugs in resolving filenames */
+       if (strcmp(peer->basedir, "/") == 0)
+       {
+               peer->chroot = FALSE;
+               return;
+       }
+       if (config->rootmode)
+       {
+               int res = chroot(peer->basedir);
+               if (res == 0)
+               {
+                       /* put ourselves into the tree */
+                       chdir("/");
+ 
+                       /* once chroot is done, nothing can undo it,
+                          so user cannot relogin */
+                       peer->jailenabled = TRUE;
+ 
+                       /* now change dumped file names */
+                       pathname_simplify(peer->basedir);
+                       if (peer->logindump)
+                               peer->logindump = remove_rootcomponent(peer, 
peer->logindump, "welcome");
+                       if (peer->quitdump)
+                               peer->quitdump = remove_rootcomponent(peer, 
peer->quitdump, "quitdump");
+                       if (peer->cwddump)
+                               if ((peer->cwddump)[0] == '/')
+                                       peer->cwddump = 
remove_rootcomponent(peer, peer->cwddump, "cddump");
+  
+                       freewrapper(peer->basedir);
+                       peer->basedir = strdupwrapper("/");
+                       
+               }
+               else
+                       log_giveentry(MYLOG_INFO, NULL, 
safe_snprintf("chroot('%s') gave error %s", peer->basedir, strerror(errno)));
+       }
+       else
+               log_addentry(MYLOG_INFO, NULL, "Cannot use chroot() without 
root permission.");
+ }                     
+ 
+ void rotatelogs(FTPSTATE *peer)
+ {
+       char *logfile;
+       int newlogcontext;
+                                
+       if (peer->vserver->logfile)
+               logfile = peer->vserver->logfile;
+       else
+               logfile = config->defaults->logfile;
+       
+       if ((!peer->chroot) || (!peer->droproot))
+       {
+               /* use root to open log */
+               file_becomeroot(peer);
+               newlogcontext = log_initcontext(logfile);
+               file_becomeuser(peer);
+                                       
+               if (newlogcontext == -1)
+                       log_giveentry(MYLOG_INFO, NULL, safe_snprintf("Could 
not open log file '%s', reopen failed", peer->vserver->logfile));
+               else
+               {
+                       log_shutdown();
+                       config->logout = newlogcontext;
+                       log_setcontext(config->logout, peer->vserver->loglevel);
+               }
+       }
+       else
+               log_addentry(MYLOG_INFO, NULL, "Cannot reopen log file in 
chroot or droproot mode");
+ }                     
+ 
+ int vserver_select(FTPSTATE *peer, VSERVER *vserver)
+ {
+       int error, result;
+       int newlogcontext;
+ 
+       peer->vserver = vserver;
+ 
+       /* initalize the new log context */
+       newlogcontext = config->logout;
+       
+       if (vserver->logfile)
+               if (strcmp(config->defaults->logfile, vserver->logfile) != 0)
+               {
+                       newlogcontext = log_initcontext(vserver->logfile);
+                       if (newlogcontext == -1)
+                       {
+                               log_giveentry(MYLOG_INFO, NULL, 
safe_snprintf("Dropping connection, error opening virtual server logfile '%s': 
%s", vserver->logfile, strerror(errno)));
+                               return(1);
+                       }
+                       log_shutdown();
+                       config->logout = newlogcontext;
+               }
+       
+       log_setcontext(config->logout, vserver->loglevel);
+ 
+       /* see if the user is allowed in this vserver */
+       if (!ipacllist_query(vserver->ipaccess, peer->remoteip, peer->hostname))
+       {
+               log_addentry(MYLOG_DACCESS, peer, NULL);
+               return(2);
+       }
+       
+       /* set the procdata up */
+       result = shinfo_setvserver(peer->threadnum, vserver->sectionname, 
+                                  peer->remoteip, vserver->maxusers,
+                                  vserver->maxperip, &error);
+       if (!result)
+       { 
+               logfullmessage(error, peer->remoteip);
+               return(3);
+       }
+       
+       return(0);
+ }
+ 
+ void ftpserverside_main(int remotefd, int remoteip, int threadnum, int 
portnum, VSERVER *vserver)
+ {
+       int newlogcontext, exits = FALSE;
+       int flag = 1;
+       struct rlimit mylimits = { MAXMEMUSAGE, MAXMEMUSAGE };
+       FTPSTATE *peer = mallocwrapper(sizeof(FTPSTATE));
+       char *greetline;
+       
+       ftpstate_init(peer, remotefd, remoteip, threadnum, portnum, vserver); 
+               /* initalize ftp state data struture */
+       shinfo_sethost(peer->hostname);
+ 
+       if (!ipacllist_query(config->defaults->ipaccess, remoteip, 
peer->hostname))
+       {
+               log_addentry(MYLOG_DACCESS, peer, NULL);
+               close(remotefd);
+               return;
+       }
+ 
+       newlogcontext = config->logout;
+       
+       if (vserver->logfile)
+               if (strcmp(config->defaults->logfile, vserver->logfile) != 0)
+               {
+                       newlogcontext = log_initcontext(vserver->logfile);
+                       if (newlogcontext == -1)
+                       {
+                               log_giveentry(MYLOG_INFO, NULL, 
safe_snprintf("Dropping connection, error opening virtual server logfile '%s': 
%s", vserver->logfile, strerror(errno)));
+                               close(remotefd);
+                               return;
+                       }
+                       log_shutdown();
+                       config->logout = newlogcontext;
+               }
+       
+       log_setcontext(config->logout, vserver->loglevel);
+ 
+       setrlimit(RLIMIT_DATA, &mylimits);
+       
+       peer->vserver = vserver;
+       
+       /* see if user is allowed to enter */
+ 
+       if (!ipacllist_query(vserver->ipaccess, remoteip, peer->hostname))
+       {
+               log_addentry(MYLOG_DACCESS, peer, NULL);
+               close(remotefd);
+               return;
+       }
+ 
+       /* initalize the PRNG */
+ 
+       srandom((unsigned int)time(NULL));
+       
+       peer->sel = select_new();
+       
+       setsockopt(peer->remotefd, IPPROTO_TCP, TCP_NODELAY, &flag, 
sizeof(int));
+       
+       /* add the remote fd for reading */
+       select_addfd(peer->sel, remotefd);
+       select_addread(peer->sel, remotefd, controlportgotdata, (void *)peer);
+       
+       /* output our greeting */
+       shinfo_changeop("idle");
+       
+       if (vserver->greetline)
+               greetline = vserver->greetline;
+       else
+               greetline = REPLY_GREET;
+ 
+       if (vserver->prelogindumpdata)
+               exits = ftp_dumpstr(peer, vserver->prelogindumpdata, 220, 
greetline, TRUE);
+       else
+               exits = ftp_dumper(peer, nfopen(vserver->prelogindump), 220, 
greetline, TRUE, TRUE);
+ 
+       while(!exits)
+       {
+               int fd, signalnum;
+               
+               fd = select_do(peer->sel, &signalnum, peer->timeout);
+               /* if we get 3 back, simply loop and update timeout */
+               if (fd == -1)
+               {
+                       if ((signalnum == SIGTERM) || (signalnum == SIGHUP))
+                       {
+                               /* server is shutting down! */
+                               ftp_write(peer, FALSE, 421, 
REPLY_SERVERSHUTDOWN);
+                               exits = TRUE;
+                       }
+                       else if (signalnum == SIGUSR1)
+                       {
+                               rotatelogs(peer);
+                       }                               
+                       else if (signalnum == 0)
+                               exits = TRUE;
+               
+               }
+               else if (fd == 0)
+               {
+                       /* timeout has occurred */
+                       ftp_write(peer, FALSE, 421, 
REPLY_TIMEOUT(peer->timeout));
+                       exits = TRUE;
+               } 
+               else if (fd != 3)
+                       exits = TRUE;
+       }
+ 
+       log_addentry(MYLOG_LOGIN, peer, "User logged out.");
+       if (peer->acldata)
+               acllist_dest(peer->acldata);
+       select_shutdown(peer->sel);
+       ftpstate_dest(peer);
+ }
+ 
+ int inport_bind(int portid, unsigned int bindip, int dodie)
+ {
+       int incon;
+       
+       if ((portid < 1024) && (getuid() != 0))
+       {
+               char *error = safe_snprintf("root access required to bind port 
%d (less than 1024), skipping", portid);
+               if (dodie)
+                       ERRORMSGFATAL(error);
+               else
+                       log_giveentry(MYLOG_INFO, NULL, error);
+               return(-1);
+       }
+       
+       incon = listenport(portid, bindip, MAXPENDCONN);
+ 
+       if (incon == -1)
+       {
+               char *error = safe_snprintf("Cannot bind to port %d, ip %s, 
skipping!", portid, getipstr(bindip));
+               if (dodie)
+                       ERRORMSGFATAL(error); 
+               else    
+                       log_giveentry(MYLOG_INFO, NULL, error);
+       }
+       return(incon);
+ }
+ 
+ int overload_sendmessage(SELECTER *mainsel, int fd, void *in)
+ {
+       OVERLOAD_INFO *i = (OVERLOAD_INFO *)in;
+       int c;
+       
+       c = write(fd, i->msg + i->pos, i->len);
+       
+       i->len -= c;
+       i->pos += c;
+       
+       if ((c <= 0) || (i->len <= 0))
+       {
+               freewrapper(i);
+               return(2);
+       }
+       
+       return(FALSE);
+ }
+ 
+ int inport_getconn(SELECTER *mainsel, int fd, void *vs)
+ {
+       int newconn;
+       unsigned int ipaddress, lip;
+       VSERVER *vserver;
+       int tnum, port, error, result;
+       pid_t fresult;
+       
+       newconn = get_conn(fd, &ipaddress); 
+       if (newconn == -1)
+       {
+               log_giveentry(MYLOG_INFO, NULL, safe_snprintf("Error accepting 
control connection: %s", strerror(errno)));
+               return(FALSE);
+       }
+ 
+       if (config->hostvservers)
+       {
+               getsockinfo(newconn, &lip, &port);
+               vserver = config->defaults;
+       }
+       else if (vs != NULL)
+       {
+               vserver = ((VSERVERCONN *)vs)->vptr;
+               port = ((VSERVERCONN *)vs)->port;
+       }
+       else
+       {
+               getsockinfo(newconn, &lip, &port);
+               vserver = findvserver(lip, port);
+       }
+ 
+       /* was not able to find a vserver for the port/ip they connected to */
+       if (vserver == NULL)
+       {
+               close(newconn);
+               return(FALSE);
+       }
+       
+       result = TRUE;
+       tnum = shinfo_newuser_standalone(ipaddress, config->defaults->maxperip, 
&error);
+       if ((tnum != -1) && (config->vservers) && (!config->hostvservers))
+               result = shinfo_setvserver(tnum, vserver->sectionname, 
ipaddress,
+                                          vserver->maxusers,
+                                          vserver->maxperip,
+                                          &error);
+       
+       if ((tnum != -1) && (result))
+       {
+               fresult = fork();
+               if ((int)fresult == 0)
+               {       
+                       shinfo_setpid(tnum, (int)getpid());
+                       select_shutdown(mainsel);
+                       freewrapper(deadlist);
+                       ftpserverside_main(newconn, ipaddress, tnum, port, 
vserver);
+                       ftpd_killconfig(config);
+                       kill_uidgidfiles();
+                       log_shutdown();
+                       exit(0);
+               }
+               else if ((int)fresult == -1)
+               {
+                       shinfo_freebynum(tnum);
+                       log_addentry(MYLOG_INFO, NULL, "Could not fork, 
dropping connection.");
+                       ERRORMSG("Could not fork, dropping connection!");
+                       close(newconn);
+               }
+               else
+               {
+                       close(newconn);
+               }
+       }
+       else 
+       {
+               /* server is full, or too many connections */
+               if (tnum != -1)
+                       shinfo_freebynum(tnum);
+               logfullmessage(error, ipaddress);
+               if (config->toobusycount <= MAXTOOMANYUSERS)
+               {
+                       OVERLOAD_INFO *i = mallocwrapper(sizeof(OVERLOAD_INFO));
+                       
+                       i->msg = vserver->toobusy;
+                       if (i->msg == NULL)
+                               i->msg = REPLY_SERVERBUSY;
+                       i->pos = 0;
+                       i->len = strlen(i->msg);
+                       fcntl(newconn, F_SETFL, O_NONBLOCK);
+                       select_addfd(mainsel, newconn);
+                       select_addwrite(mainsel, newconn, overload_sendmessage, 
i);
+               }
+               else
+                       close(newconn);
+       }
+       return(FALSE);
+ }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]