]> git.sesse.net Git - betaftpd/commitdiff
Initial revision
authorsgunderson <sgunderson>
Tue, 25 Jul 2000 23:38:21 +0000 (23:38 +0000)
committersgunderson <sgunderson>
Tue, 25 Jul 2000 23:38:21 +0000 (23:38 +0000)
26 files changed:
Makefile.in [new file with mode: 0644]
README [new file with mode: 0644]
acconfig.h [new file with mode: 0644]
ascii.c [new file with mode: 0644]
ascii.h [new file with mode: 0644]
betaftpd.lsm [new file with mode: 0644]
cmds.c [new file with mode: 0644]
cmds.h [new file with mode: 0644]
config.h.in [new file with mode: 0644]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
disp.c [new file with mode: 0644]
doc/CHANGES [new file with mode: 0644]
doc/CHANGES-0.0.8 [new file with mode: 0644]
doc/COPYING [new file with mode: 0644]
doc/CREDITS [new file with mode: 0644]
doc/KNOWN-BUGS [new file with mode: 0644]
doc/README.nonroot [new file with mode: 0644]
doc/README.platforms [new file with mode: 0644]
doc/README.rights [new file with mode: 0644]
doc/RFC-COMPLIANCE [new file with mode: 0644]
ftpd.c [new file with mode: 0644]
ftpd.h [new file with mode: 0644]
nonroot.c [new file with mode: 0644]
nonroot.h [new file with mode: 0644]
strip-exec [new file with mode: 0755]

diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..de5f217
--- /dev/null
@@ -0,0 +1,66 @@
+VERSION=0.0.8pre17-dev
+
+# main target
+all: betaftpd
+
+# various defines
+CC = @CC@
+VPATH = @srcdir@
+prefix = @prefix@
+
+CFLAGS = @CFLAGS@ @DEFS@ -I@srcdir@ -DVERSION=\"$(VERSION)\"
+LIBS   = @LIBS@
+OBJS   = disp.o ftpd.o cmds.o nonroot.o ascii.o
+ASSMS  = disp.s ftpd.s cmds.s nonroot.s ascii.s
+CPPS   = disp.i ftpd.i cmds.i nonroot.i ascii.i
+
+# Since we use VPATH, override .c.o rule
+.c.o:  $*.c @srcdir@/config.h
+       $(CC) $(CFLAGS) -c @srcdir@/$*.c
+.c.s:  $*.c @srcdir@/config.h
+       $(CC) $(CFLAGS) -S @srcdir@/$*.c
+
+# deps
+cmds.o:                @srcdir@/cmds.c @srcdir@/ftpd.h @srcdir@/cmds.h @srcdir@/nonroot.h @srcdir@/config.h
+ftpd.o:                @srcdir@/ftpd.c @srcdir@/ftpd.h @srcdir@/cmds.h @srcdir@/config.h
+disp.o:                @srcdir@/disp.c @srcdir@/ftpd.h @srcdir@/config.h
+nonroot.o:     @srcdir@/nonroot.c @srcdir@/nonroot.h @srcdir@/config.h
+ascii.o:       @srcdir@/ascii.c @srcdir@/config.h
+
+betaftpd: $(OBJS)
+       $(CC) $(CFLAGS) $(LIBS) -o betaftpd $(OBJS)
+assembly-files: $(ASSMS)
+betaftpd-from-assembly-files: $(ASSMS)
+       $(CC) $(LIBS) -o betaftpd -Wl,--sort-common $(ASSMS)
+
+clean:
+       rm -f $(OBJS) $(ASSMS) $(CPPS)
+       rm -f core
+       rm -f betaftpd
+
+distclean: clean
+       rm -f config.log config.cache config.status Makefile config.h
+
+install: betaftpd
+       install betaftpd $(prefix)/bin/betaftpd
+
+#
+# NOTE: This is not intended for end users.
+#
+package:
+#      strip-exec
+       rm -rf betaftpd-$(VERSION)
+       mkdir betaftpd-$(VERSION)
+       mkdir betaftpd-$(VERSION)/doc
+#      cp betaftpd betaftpd-$(VERSION)
+       make distclean
+       autoconf
+       autoheader configure.in > config.h.in
+       cp README Makefile.in acconfig.h cmds.c cmds.h betaftpd-$(VERSION)
+       cp config.h.in configure configure.in disp.c ftpd.c betaftpd-$(VERSION)
+       cp nonroot.c ascii.c ascii.h nonroot.h ftpd.h strip-exec betaftpd.lsm betaftpd-$(VERSION)
+       cp doc/CREDITS doc/CHANGES doc/CHANGES-0.0.8 doc/COPYING betaftpd-$(VERSION)/doc
+       cp doc/KNOWN-BUGS doc/RFC-COMPLIANCE doc/README.nonroot betaftpd-$(VERSION)/doc
+       cp doc/README.rights doc/README.platforms betaftpd-$(VERSION)/doc
+       tar cf - betaftpd-$(VERSION)/ | gzip -9vv > betaftpd-$(VERSION).tar.gz
+       tar cf - betaftpd-$(VERSION)/ | bzip2 -3vv > betaftpd-$(VERSION).tar.bz2
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..1c03da5
--- /dev/null
+++ b/README
@@ -0,0 +1,175 @@
+Hmmmmm... :-)
+
+[Insert insanely cool ASCII logo here yourself -- I'd rather spend my time
+ coding]
+
+0. Before you start
+-------------------
+This is NOT a stable version of BetaFTPD. The latest stable, tried and tested
+version is 0.0.7. Use this version at your own risk. That being said, this
+version is rather stable now, actually _more_ stable than 0.0.7, which contains
+quite a few lethal bugs, and is almost a year old.
+
+1. What is it?
+--------------
+BetaFTPD is a _single_-threaded FTP daemon (server). This makes it _faster_
+than most other FTP daemons (in most situations), contrary to popular belief.
+BetaFTPD is, as the name implies, work in progress. Please don't expect it to
+be finished yet!
+
+2. How do you build it?
+-----------------------
+BetaFTPD uses GNU autoconf. Just type:
+
+./configure
+make
+
+And see what happens. (Run the resulting file called `betaftpd', of
+course...) There are some flags that you can (and probably want to) give
+to `configure':
+
+--enable-xferlog       Makes the server output some logging information
+                       to /var/log/xferlog (or /usr/adm/xferlog if that fails).
+--enable-fullscreen    Makes the server start in fullscreen mode, where you
+                       can see what the users are doing, their transfer
+                       speed etc. (There is other no way of turning on or off
+                       fullscreen mode -- this switch is the only way.)
+--enable-upload                Enable STOR (uploading) and APPE commands, and all necessary
+                       code for uploading.
+--enable-stat          Enable STAT command (server statistics). Note that I see
+                       very little use for such a command, and you shouldn't enable
+                       it unless you have a very good reason. Such commands are
+                       one of the best weapons in a cracker's arsenal. It is only
+                       included for RFC1123 compliance, and disabled by default.
+                       In addition, it tries to give as little `cracker-help' data
+                       as possible. 
+--enable-shadow                Enable support for shadow passwords.
+--enable-fork          Makes the server fork into the background, so you
+                       can logout etc. without `disturbing' it.
+--enable-nonroot       Enable running without root privilegies. Please read    
+                       README.nonroot _first_, it will save you a lot of
+                       grief.
+--enable-ascii         Enable ASCII mode transfers (ASCII translation on-the-fly).
+--enable-dcache                Enable directory listing caches, for faster directory
+                       listings (the listings are only generated the first time
+                       they are needed). Note that this still quite experimental
+                       code, and the cache is only invalidated when it expires
+                       (ie. if nobody uses the entry in 15 minutes), or when
+                       files are added or deleted in the cached directory.
+                       (In other words, if a file is changed, the directory
+                       cache entry is _not_ updated. Do a `touch' on the directory
+                       to force invalidation of the dcache entry.)
+--enable-message       Enable welcome.msg/.message files and README notes, like
+                       `normal' ftpds do. Note that this is relatively crude
+                       (and probably will stay so) -- for instance, README notes
+                       and .message files are printed every time the user enters
+                       the directory, even if he/she has been there before.
+
+To get the smallest executable possible, _first_ check everything
+in `strip-exec' (it's unsupported -- don't complain to me if it doesn't
+work, fix it yourself! :-) ), and type:
+
+strip-exec
+
+3. Some things to note
+----------------------
+a) BetaFTPD now listen on port 21 by default (it used to run on port 121). To
+   change it, change FTP_PORT in ftpd.h to whatever you'd like it to listen on.
+b) BetaFTPD must definitely be run as root (unless you use --enable-nonroot
+   as mentioned above).
+c) Some commands and features are missing, but probably not many that you'd
+   really miss. See the KNOWN-BUGS and RFC-COMPLIANCE files.
+d) BetaFTPD can _not_ be run from inetd. This is a design decision.
+
+4. Design decisions
+-------------------
+BetaFTPD was originally designed to be a single-threaded FTP server,
+nothing else. After a while, work was done to reduce the executable size,
+and from then, design was more and more concentrated around a
+`minimalistic' server (after all, the fewer lines of code there are to
+execute, the faster will things go, and the fewer bugs are possible).
+
+However, as GNU autoconf support was added, it was realized that
+features could be added and selected by users without hurting overall
+speed or size for the others. Therefore, more and more features are
+(and will be) implemented in BetaFTPD.
+
+There are intendedly no command line switches it BetaFTPD. Such switches
+are generally there to make for quick change of options without
+recompiling the program. This is ideal for most command-line utilities,
+but is rather pointless for an FTP server. FTP servers are designed
+to be online 24 hours a day, 7 days a week, and serve users with files.
+Rapid confiuration changes (in the form of command line switches,
+configuration files et al) are usually not important, and has thus been
+omitted from BetaFTPD.
+
+There is no (and will never be a) ratio function in BetaFTPD, as such
+features are only used for warez/MP3/porn servers, and are quite stupid
+even then.
+
+5. Eek! `top' says BetaFTPD uses tons of memory!
+------------------------------------------------
+Yes, but top really is wrong :-) Well, actually, I don't think it could
+do better -- every file BetaFTPD mmap()s is counted towards the total
+number. So if you were serving the same 128MB file 10 times, top would
+claim you used over a gigabyte of memory, still only a couple of
+megabytes would be used at most, as cache. If you ran out of memory,
+this could actually shrink to zero. It's just that the kernel tries to
+be more effective (and it succeeds) by reducing the number of reads, and
+instead reading larger chunks at once.
+
+If sendfile() support is enabled, and the circumstances allow it (binary
+mode downloading), BetaFTPD will not mmap() at all, bringing the memory
+total down to a more realistic value.
+
+Bragging:
+
+On my Intel system, every user needs 1104 bytes (`struct conn').
+When a user has a file transfer, an additional 324 bytes (`struct ftran')
+would be needed, or 1428 bytes per user in total. In addition, you have
+some `startup costs', of course. `ps' (again on my system) reports a
+startup memory usage of 432 kB (RSS, I don't know enough about memory
+to know if this is a realistic number ;-) ), compared to 484 kB for a
+single wu-ftpd session (again RSS). To be honest, I don't think BetaFTPD
+uses much memory. [Note that this information is a bit old -- I haven't
+tested it lately; expect 2-300 bytes extra per user.]
+
+Oh, while we're at it: A 486 running BetaFTPD will easily be able to
+saturate a T1 line, with room to spare. The bottleneck will most likely
+lie somewhere else (like the bandwidth, or how fast the disk can deliver
+data). Unless you've got a 386 running gigabit, of course... (I must
+admit, I've never really tried it with more than 100Mbit/sec, but the
+problem then was clearly on the client machine ;-) BetaFTPD has later
+shown itself to be more than able of saturating a 10mbit link in real-
+world tests, with about 3% CPU utilization (PII/450MHz, IDE disks all
+the way, about 2GB of files in all, 7-8 users at once).)
+
+Recent load tests (see http://www.kegel.com/dkftpbench/results.html)
+show that a P90 is able to serve about 250 clients (each at 28.800-speeds)
+on a 10Mbit line.
+
+6. Misc.
+--------
+Have fun -- after all, it's about 5% the size of wu-ftpd (about 20k
+compiled!) and provides (IMHO) about 98% of the needed, everyday
+functionality -- in fact, if you don't need the _really_ exotic commands,
+it's faster (=better?) and uses *much* less memory (especially when
+you have many users logged in at once) than the majority of the FTP
+servers out there... (6 line sentence.)
+
+This program is GPLed, version 2 (see the file COPYING), not any later
+or earlier version (later versions will probably `upgrade' to new GPLs
+as they become available, I just want to be able to choose what license
+I put my code under). For more info on the GPL, visit http://www.gnu.org/ .
+
+7. Where to get new versions, contact me etc.
+---------------------------------------------
+E-mail:                        sgunderson@bigfoot.com (all spam/flame go
+                       to antispam@bigfoot.com or /dev/null)
+Homepage, new versions:        http://members.xoom.com/sneeze/
+                       (Click the link, and see under Projects.)
+Mirror site:           ftp://metalab.unc.edu/pub/Linux/system/network/daemons/
+                       (Note that Metalab will always lag behind a few days, and
+                        doesn't carry beta versions.)
+
+                       There are more mirror sites at the homepage.
diff --git a/acconfig.h b/acconfig.h
new file mode 100644 (file)
index 0000000..06373c4
--- /dev/null
@@ -0,0 +1,30 @@
+/* Define if we want to do ascii transfers.  */
+#undef WANT_ASCII
+
+/* Define if we need transfer logging.  */
+#undef WANT_XFERLOG
+
+/* Define if we need fullscreen mode. */
+#undef WANT_FULLSCREEN
+
+/* Define if we need upload support. */
+#undef WANT_UPLOAD
+
+/* Define if we want statistics support. */
+#undef WANT_STAT
+
+/* Define if we need shadow support. */
+#undef WANT_SHADOW
+
+/* Define if we need to fork. */
+#undef WANT_FORK
+
+/* Define if we don't have root privilegies. */
+#undef WANT_NONROOT
+
+/* Define if we want directory listing cache */
+#undef WANT_DCACHE
+
+/* Define if we want to support .message files et al */
+#undef WANT_MESSAGE
+@TOP@
diff --git a/ascii.c b/ascii.c
new file mode 100644 (file)
index 0000000..3fdb6ab
--- /dev/null
+++ b/ascii.c
@@ -0,0 +1,85 @@
+/*  ascii.c: BetaFTPD ascii filters, written by Beau Kuiper
+    Copyright (C) 1999-2000 Steinar H. Gunderson
+
+    This program is is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License, version 2 if the
+    License as published by the Free Software Foundation.
+
+    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if WANT_ASCII
+
+/*
+ * ascii_findlength():
+ *             Figures out how much read data was transferred if
+ *             a full buffer wasn't sent.
+ */
+int ascii_findlength(const char * const buffer, const int tranlen)
+{
+       int count = 0, pos = 0;
+               
+       while (pos < tranlen) {
+               const char ch = buffer[count++];
+               if (ch != 13) {
+                       if (ch == 10) pos++;
+                       pos++;
+               }
+       }
+       return count;
+}
+
+/*
+ * ascii_downloadfilter():
+ *             Changes LF to CR/LF on the fly (from buffer to
+ *             outbuffer), for ASCII downloads.
+ */
+int ascii_downloadfilter(const char * const buffer, char * const outbuffer, const int length)
+{
+       int count;
+       char *b2ptr = outbuffer;
+               
+       for (count = 0; count < length; count++) {
+               const char ch = buffer[count];
+
+               if (ch != 13) {
+                       if (ch == 10) {
+                               *b2ptr++ = 13;
+                       }
+                       *b2ptr++ = ch;
+               }
+       }
+       return (b2ptr - outbuffer);
+}
+
+/*
+ * ascii_uploadfilter():
+ *             Removes all CRs (ASCII 13) from buffer on the fly,
+ *             for ASCII uploads.
+ */
+int ascii_uploadfilter(char * const buffer, const int length)
+{
+       int count;
+       char *b2ptr = buffer;
+               
+       for (count = 0; count < length; count++) {
+               const char ch = buffer[count];
+               if (ch != 13) {
+                       *b2ptr++ = ch;
+               }
+       }
+       return (b2ptr - buffer);
+}
+
+#endif /* WANT_ASCII */
diff --git a/ascii.h b/ascii.h
new file mode 100644 (file)
index 0000000..7b2519c
--- /dev/null
+++ b/ascii.h
@@ -0,0 +1,20 @@
+/*  ascii.h: BetaFTPD ASCII filter prototypes
+    Copyright (C) 1999-2000 Steinar H. Gunderson
+
+    This program is is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License, version 2 if the
+    License as published by the Free Software Foundation.
+
+    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+int ascii_findlength(const char * const buffer, const int tranlen);
+int ascii_downloadfilter(const char * const buffer, char * const outbuffer, const int length);
+int ascii_uploadfilter(char * const buffer, const int length);
diff --git a/betaftpd.lsm b/betaftpd.lsm
new file mode 100644 (file)
index 0000000..391884d
--- /dev/null
@@ -0,0 +1,12 @@
+Begin3
+Title:         BetaFTPD
+Version:       0.0.8
+Entered-date:  30MAY00
+Description:   Single-threaded, fast, small FTP daemon (server)
+Keywords:      FTP server, single-threaded
+Author:                sgunderson@bigfoot.com (Steinar H. Gunderson)
+Primary-site:  metalab.unc.edu /pub/Linux/system/network/daemons
+               64k betaftpd-0.0.8.tar.bz2
+               72k betaftpd-0.0.8.tar.gz
+Copying-policy:        GPL
+End
diff --git a/cmds.c b/cmds.c
new file mode 100644 (file)
index 0000000..131df34
--- /dev/null
+++ b/cmds.c
@@ -0,0 +1,1879 @@
+/*  cmds.c: BetaFTPD command handlers
+    Copyright (C) 1999-2000 Steinar H. Gunderson
+
+    This program is is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License, version 2 if the
+    License as published by the Free Software Foundation.
+
+    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#define _GNU_SOURCE
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+
+#if HAVE_SYS_CONF_H
+#include <sys/conf.h>
+#endif
+
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+#if HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#if HAVE_GLOB_H
+#include <glob.h>
+#endif
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#if HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#if HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#if HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+
+#if HAVE_SYS_CONF_H
+#include <sys/conf.h>
+#endif
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#if HAVE_SHADOW_H
+#include <shadow.h>
+#endif
+
+#if HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#if HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+
+#if WANT_NONROOT
+#define NO_SETUID
+#define DO_SETUID
+#else
+#define NO_SETUID ,0
+#define DO_SETUID ,1
+#endif
+
+#ifndef NBBY
+#define NBBY 8
+#endif
+
+#include <ftpd.h>
+#include <cmds.h>
+#include <nonroot.h>
+
+#define lstat stat
+
+extern struct conn *first_conn;
+#if WANT_DCACHE
+extern struct dcache *first_dcache;
+#endif
+
+#if HAVE_POLL
+extern struct pollfd fds[];
+#else
+extern fd_set master_fds, master_send_fds;
+#endif
+
+struct handler {
+       char cmd_name[6];
+       char add_cmlen;         /* =1 if the command takes an argument */
+       int (*callback)(struct conn * const);
+       char min_auth;
+#if !WANT_NONROOT
+       char do_setuid;         /* =1 if root is not *really* needed */
+#endif
+};
+
+static const struct handler handler_table[] = {
+       { "user ", 1, cmd_user, 0       NO_SETUID },
+       { "pass ", 1, cmd_pass, 1       NO_SETUID },
+       { "retr ", 1, cmd_retr, 3       DO_SETUID },
+       { "acct ", 1, cmd_acct, 0       NO_SETUID },
+       { "port ", 1, cmd_port, 3       DO_SETUID },
+       { "pasv" , 0, cmd_pasv, 3       DO_SETUID },
+       { "pwd"  , 0, cmd_pwd,  3       DO_SETUID },
+       { "cwd " , 1, cmd_cwd,  3       DO_SETUID },
+       { "cdup" , 0, cmd_cdup, 3       DO_SETUID },
+       { "rest ", 1, cmd_rest, 3       DO_SETUID },
+       { "list" , 0, cmd_list, 3       DO_SETUID },
+       { "nlst" , 0, cmd_nlst, 3       DO_SETUID },
+       { "type ", 1, cmd_type, 3       DO_SETUID },
+       { "mode ", 1, cmd_mode, 3       DO_SETUID },
+       { "stru ", 1, cmd_stru, 3       DO_SETUID },
+       { "size ", 1, cmd_size, 3       DO_SETUID },
+       { "mdtm ", 1, cmd_mdtm, 3       DO_SETUID },
+       { "abor" , 0, cmd_abor, 3       DO_SETUID },
+       { "dele ", 1, cmd_dele, 3       DO_SETUID },
+       { "rnfr ", 1, cmd_rnfr, 3       DO_SETUID },
+       { "rnto ", 1, cmd_rnto, 3       DO_SETUID },
+       { "mkd " , 1, cmd_mkd,  3       DO_SETUID },
+       { "rmd " , 1, cmd_rmd,  3       DO_SETUID },
+       { "allo ", 1, cmd_allo, 3       DO_SETUID },
+       { "stat" , 0, cmd_stat, 0       NO_SETUID },
+       { "noop" , 0, cmd_noop, 0       DO_SETUID },
+       { "syst" , 0, cmd_syst, 0       DO_SETUID },
+       { "help" , 0, cmd_help, 0       NO_SETUID },
+       { "quit" , 0, cmd_quit, 0       DO_SETUID },
+       { "rein" , 0, cmd_rein, 0       DO_SETUID },
+
+       /* deprecated forms */
+       { "xcup" , 0, cmd_cdup, 3       DO_SETUID },
+       { "xcwd ", 1, cmd_cwd,  3       DO_SETUID },
+       { "xpwd" , 0, cmd_pwd,  3       DO_SETUID },
+       { "xmkd ", 1, cmd_mkd,  3       DO_SETUID },
+       { "xrmd ", 1, cmd_rmd,  3       DO_SETUID },
+#if WANT_UPLOAD
+       { "stor ", 1, cmd_stor, 3       DO_SETUID },
+       { "appe ", 1, cmd_appe, 3       DO_SETUID },
+#endif
+#if DOING_PROFILING
+#warning Use DOING_PROFILING with caution, and NEVER on a production server! :-)
+       { "exit",  0, cmd_exit, 0       NO_SETUID },
+#endif
+       { ""    ,  0, NULL,     0       NO_SETUID }
+};
+
+/*
+ * do_chdir(): Does a chdir() to newd on c, staying inside the
+ *             limits of root_dir. Use this instead of a chdir() whenever
+ *             you can, and possibly even when you can't :-)
+ *
+ *             This command quirks around some problems in the rest of
+ *             the code (namely translate_path()), so a blank newdir is
+ *             interpreted as the root directory.
+ */
+int do_chdir(struct conn * const c, const char * const newd)
+{
+       char chd[512], temp[512];
+
+       TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1);
+
+       /* handle `absolute' paths */
+       if (newd[0] == '/' || newd[0] == '\0') {
+               strcpy(temp, c->root_dir);
+
+               /*
+                * is this the root directory? if not, remove the trailing `/'
+                * and concatenate the new directory on
+                */
+               if (newd[1] != '\0' && newd[0] != '\0') {
+                       temp[strlen(temp) - 1] = 0;
+                       strcat(temp, newd);
+               }
+       } else {
+               strcpy(temp, newd);
+       }
+
+#if WANT_NONROOT
+       if (nr_check_permission(c->uid, temp, 1, 1, NULL) == -1) {
+               numeric(c, 550, "Permission denied");
+               return -1;
+       }
+#endif
+
+       TRAP_ERROR(chdir(temp) == -1, 550, return -1);
+
+       getcwd(chd, 254);
+       if (chd[strlen(chd) - 1] != '/') {
+               strcat(chd, "/");
+       }
+
+       if (strncmp(chd, c->root_dir, strlen(c->root_dir)) != 0) {
+               numeric(c, 550, "No such file or directory.");
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * cmd_user(): Handles the USER command, and does most of the initial
+ *             authentication work. User names are limited to 16
+ *             characters, by force...
+ */
+int cmd_user(struct conn * const c)
+{
+       strncpy(c->username, c->recv_buf, 16);
+       c->username[16] = 0;
+
+       if (strcasecmp(c->username, "anonymous") == 0) {
+               strcpy(c->username, "ftp");
+       }
+               if (strcasecmp(c->username, "ftp") == 0) {
+               numeric(c, 331, "Login OK, send password (your e-mail).");
+               c->auth = 1;
+       } else {
+               numeric(c, 331, "Password required for %s.", c->username);
+               c->auth = 2;
+       }
+       return 1;
+}
+
+/*
+ * cmd_pass(): Handles the PASS command, and checks the password.
+ *             This function is rather long and complicated, mostly
+ *             because there are so many ways of doing users
+ *             (including my nonroot system) out there... And we
+ *             don't even support PAM or real shadow passwords (with
+ *             expiry etc) yet...
+ */
+int cmd_pass(struct conn * const c)
+{
+#if WANT_NONROOT
+       c->auth = nr_userinfo(c->username, &c->uid, c->curr_dir, c->root_dir,
+                             c->recv_buf);
+#else /* !WANT_NONROOT */
+#if WANT_SHADOW && HAVE_SHADOW_H
+       struct spwd *s;
+#endif
+       struct passwd *p;
+
+               p = getpwnam(c->username);
+#if WANT_SHADOW && HAVE_SHADOW_H
+       s = getspnam(c->username);
+#endif
+               
+       if (p == NULL) {
+               c->auth = 0;
+       } else {
+               c->uid = p->pw_uid;
+               strncpy(c->curr_dir, p->pw_dir, 254);
+               c->curr_dir[254] = 0;
+       }
+
+               if (c->auth == 1) {
+               if (c->curr_dir[strlen(c->curr_dir) - 1] != '/') {
+                       strcat(c->curr_dir, "/");
+               }
+               strcpy(c->root_dir, c->curr_dir);       
+               c->auth = 3;
+       } else if (c->auth != 0) {
+               strcpy(c->root_dir, "/");
+               if (strcmp(crypt(c->recv_buf, p->pw_passwd), p->pw_passwd) != 0
+#if WANT_SHADOW && HAVE_SHADOW_H
+                   && (s == NULL || strcmp(crypt(c->recv_buf, s->sp_pwdp), s->sp_pwdp) != 0)
+#endif
+               ) {
+                       c->auth = 0;
+               } else {
+                       c->auth = 3;
+               }
+       }
+#endif /* !WANT_NONROOT */
+
+       if (c->auth == 0) {
+               numeric(c, 530, "Login incorrect.");
+       } else {
+#if WANT_MESSAGE
+               chdir(c->curr_dir);
+               dump_file(c, 230, "welcome.msg");
+#endif
+               numeric(c, 230, "User logged in.");
+       }
+       return 1;
+}
+
+/*
+ * cmd_acct(): Handle (ignore) the ACCT command. I don't see how we
+ *             could make use of this command... wu-ftpd doesn't, either.
+ *             However, wu-ftpd (at least the version I have here) uses
+ *             502, which isn't a legal error code according to RFC959.
+ *             202, on the other hand, is, and seems to be applicable.
+ *
+ *             I'm unsure if this one should require setuid or not, but
+ *             I feel that the RFC959 intention is having it _before_
+ *             USER/PASS. Therefore, this one runs with root privilegies :-)
+ */
+int cmd_acct(struct conn * const c)
+{
+       numeric(c, 202, "ACCT ignored OK -- not applicable on this system.");
+       return 1;
+}
+
+/*
+ * cmd_port(): Handles the PORT command, and sets up the data socket.
+ *             Making a brief uid=0 (root) appearance (to bind the socket) --
+ *             I feel it's safer that way (instead of running as root
+ *             the whole way), in case there are some weird overflows
+ *             somewhere.
+ */
+int cmd_port(struct conn * const c)
+{
+       short int a0, a1, a2, a3, p0, p1;
+       int i, sock, err;
+       struct ftran *f;
+       struct sockaddr_in sin;
+    
+       if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
+               numeric(c, 500, "Sorry, only one transfer at a time.");
+               return 1;
+       }
+
+       sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+       TRAP_ERROR(sock == -1, 500, return 1);
+
+       destroy_ftran(c->transfer);
+       c->transfer = f = alloc_new_ftran(sock, c);
+
+       i = sscanf(c->recv_buf, "%3hu,%3hu,%3hu,%3hu,%3hu,%3hu", &a0, &a1, &a2, &a3, &p0, &p1);
+       if (i < 6) {
+               numeric(c, 501, "Parse error.");
+       } else {
+               const int one = 1;
+               int tmp;
+
+               /* bind to own address, port 20 (FTP data) */
+               tmp = sizeof(sin);
+               err = getsockname(c->sock, (struct sockaddr *)&sin, &tmp);
+               TRAP_ERROR(err == -1, 500, return 1);
+               sin.sin_port = FTP_PORT - 1;
+
+               numeric(c, 200, "PORT command OK.");
+
+               /* note that bind() might well fail, so we don't error check */
+#if !WANT_NONROOT
+               /* need root privilegies for a short while */
+               seteuid(getuid());
+#endif
+               bind(sock, (struct sockaddr *)&sin, sizeof(sin));
+#if !WANT_NONROOT
+               seteuid(c->uid);
+#endif
+
+               f->sin.sin_family = AF_INET;
+               f->sin.sin_addr.s_addr = htonl(
+                       ((unsigned char)(a0) << 24) +
+                       ((unsigned char)(a1) << 16) +
+                       ((unsigned char)(a2) <<  8) +
+                       ((unsigned char)(a3)      ));
+               f->sin.sin_port = htons(
+                       ((unsigned char)(p0) << 8) +
+                       ((unsigned char)(p1)     ));
+               f->sock = sock;
+               f->state = 3;
+
+               i = 1;          
+               ioctl(f->sock, FIONBIO, &one);
+       }
+       return 1;
+}
+
+/*
+ * cmd_pasv(): Handles the PASV command, and sets up the data socket.
+ *             Uses port numbers > 1024, since it doesn't run as root.
+ */
+int cmd_pasv(struct conn * const c)
+{
+       struct ftran *f;
+       int tmp, sock, err;
+       unsigned int one = 1;
+       struct sockaddr_in addr;
+
+       if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
+               numeric(c, 503, "Sorry, only one transfer at once.");
+               return 1;
+       }
+       destroy_ftran(c->transfer);
+
+       sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+       TRAP_ERROR(sock == -1, 500, return 1);
+       err = add_fd(sock, POLLIN);
+       TRAP_ERROR(err != 0, 501, return 1);
+
+       c->transfer = f = alloc_new_ftran(sock, c);
+
+       ioctl(sock, FIONBIO, &one);
+
+       /* setup socket */
+       tmp = sizeof(addr);
+       err = getsockname(c->sock, (struct sockaddr *)&addr, &tmp);
+       TRAP_ERROR(err == -1, 500, return 1);
+
+       addr.sin_port = 0;      /* let the system choose */
+       err = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr));
+       TRAP_ERROR(err == -1, 500, return 1);
+
+       tmp = sizeof(addr);
+       err = getsockname(sock, (struct sockaddr *)&addr, &tmp);
+       TRAP_ERROR(err == -1, 500, return 1);
+
+       err = listen(f->sock, 1);
+       TRAP_ERROR(err == -1, 500, return 1);
+       f->state = 1;
+
+       numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)",
+               (htonl(addr.sin_addr.s_addr) & 0xff000000) >> 24,
+               (htonl(addr.sin_addr.s_addr) & 0x00ff0000) >> 16,
+               (htonl(addr.sin_addr.s_addr) & 0x0000ff00) >>  8,
+               (htonl(addr.sin_addr.s_addr) & 0x000000ff),
+               (htons(addr.sin_port) & 0xff00) >> 8,
+               (htons(addr.sin_port) & 0x00ff));
+       return 1;
+}
+
+/*
+ * cmd_pwd():  Handles PWD command (print working directory).
+ *
+ *             Note that if somebody contacts you with the message `the server
+ *             says curr_dir() is outside root_dir()', you should fix your
+ *             /betaftpd.users file, if you use nonroot. If not, it's a bug.
+ *             Try to get it _reproducible_, and mail it to me.
+ */
+int cmd_pwd(struct conn * const c)
+{
+       char temp[512], *cdir = NULL;
+
+       cdir = do_pwd(c, temp, c->curr_dir);
+       if (cdir != NULL) {
+               numeric(c, 257, "\"%s\" is current working directory.", cdir);
+       }
+       return 1;
+}
+
+/*
+ * do_pwd():   Translates an absolute path to a path suitable for viewing
+ *             to the user (ie. removes the root_dir, and removes a trailing
+ *             slash if it exists). Note that the retbuf is only used as a
+ *             storage place -- the pointer to the right place within retbuf
+ *             is _returned_.
+ */
+char *do_pwd(struct conn * const c, char * const retbuf, const char * const dir) 
+{
+       char *cdir = NULL;
+
+       strcpy(retbuf, dir);
+       if (strncmp(retbuf, c->root_dir, strlen(c->root_dir)) != 0) {
+               numeric(c, 550, "curr_dir is outside root_dir, please contact site administrator.");
+               return NULL;
+       }
+
+       cdir = retbuf + strlen(c->root_dir) - 1;
+       if (cdir[strlen(cdir) - 1] == '/' && strlen(cdir) > 1) {
+               cdir[strlen(cdir) - 1] = 0;
+       } else if (strlen(cdir) == 0) {
+               strcpy(cdir, "/");
+       }       
+
+       return cdir;
+}
+
+/*
+ * cmd_cwd():  Handles CWD command (change working directory). Uses
+ *             cmd_cwd_internal() (see below).
+ */
+int cmd_cwd(struct conn * const c)
+{
+       cmd_cwd_internal(c, c->recv_buf);
+       return 1;
+}
+
+/*
+ * cmd_cdup():  Handles a CDUP command (identical to `CWD ..'). Note that
+ *             RFC959 gives two different response codes (250 and 200) --
+ *             250 is the same as CWD gives, which sounds logical to me.
+ *             wu-ftpd uses it as well.
+ *
+ *             Note that using a CDUP to try to get outside root_dir returns
+ *             an error, instead of just staying in the root directory (as
+ *             the OS and thus wu-ftpd does).
+ */
+int cmd_cdup(struct conn * const c)
+{
+       cmd_cwd_internal(c, "..");
+       return 1;
+}
+
+/*
+ * cmd_cwd_internal():
+ *             Does the work for CWD and CDUP (modularized to save some
+ *             space and have clearer code). Mostly, it just uses do_chdir(),
+ *             and sees where that takes us. It adds a trailing slash if needed.
+ */
+void cmd_cwd_internal(struct conn * const c, const char * const newd)
+{
+       if (do_chdir(c, newd) != -1) {
+               int i;
+
+               getcwd(c->curr_dir, 254);
+               i = strlen(c->curr_dir);
+               if (c->curr_dir[i - 1] != '/') {
+                       c->curr_dir[i++] = '/';
+                       c->curr_dir[i] = '\0';
+               }
+
+#if WANT_MESSAGE
+               dump_file(c, 250, ".message");
+               list_readmes(c);
+#endif
+
+               numeric(c, 250, "CWD successful.");
+       }
+}
+
+/*
+ * cmd_rest(): Handles the REST command. All it does is tell the file
+ *             sending functions to start at the correct number. We should
+ *             perhaps add some better error checking to this?
+ */
+int cmd_rest(struct conn * const c)
+{
+       c->rest_pos = abs(atoi(c->recv_buf));
+       numeric(c, 350, "Setting resume at %u bytes.", c->rest_pos);
+       return 1;
+}
+
+/*
+ * cmd_retr(): Handles the RETR command. This command doesn't send the
+ *             file, but it opens it and tells the socket handling code
+ *             to check for activity on the data socket. When the
+ *             connection occurs (or succeeds, if we're using PORT mode),
+ *             the actual file transfer begins.
+ */
+int cmd_retr(struct conn * const c)
+{
+       struct ftran *f = c->transfer;
+
+       if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
+               numeric(c, 425, "No data connection set up; please use PASV or PORT.");
+               return 1;
+       }
+
+#if WANT_ASCII
+       if ((c->rest_pos > 0) && (c->ascii_mode == 1)) {
+               numeric(c, 500, "Cannot resume while in ASCII mode.");
+               return 1;
+       }
+#endif
+
+       f->local_file = do_openfile(c, c->recv_buf, f->filename, O_RDONLY
+#if WANT_NONROOT
+               , 4
+#endif
+       );
+       f->dir_listing = 0;
+
+       if (f->local_file == -1) {
+               numeric(f->owner, 550, strerror(errno));
+               destroy_ftran(f);
+       } else if (f->local_file == -2) {
+               f->local_file = -1;
+               destroy_ftran(f);
+       } else {
+#if WANT_UPLOAD
+               f->upload = 0;
+#endif
+               prepare_for_transfer(f);
+       }
+       return 1;
+}
+
+#if WANT_UPLOAD
+/*
+ * cmd_stor(): Handles the STOR command (upload file). Pushes the
+ *             work down to do_store(), below.
+ */
+int cmd_stor(struct conn * const c)
+{
+       do_store(c, 0);
+       return 1;
+}
+
+/*
+ * cmd_appe(): Handles the APPE command (append to file). Pushes
+ *             the work down to do_store(), below.
+ */
+int cmd_appe(struct conn * const c)
+{
+       do_store(c, 1);
+       return 1;
+}
+
+/*
+ * do_store(): Initiate an upload. Most of the comments to do_retr()
+ *             (above) apply to this one as well.
+ */
+void do_store(struct conn * const c, const int append)
+{
+       struct ftran *f = c->transfer;
+
+       if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
+               numeric(c, 425, "No data connection set up; please use PASV or PORT.");
+               return;
+       }
+
+#if WANT_ASCII
+       if ((c->rest_pos > 0) && (c->ascii_mode == 1)) {
+               numeric(c, 500, "Cannot resume while in ASCII mode.");
+               return;
+       }
+#endif
+
+       f->local_file = do_openfile(c, c->recv_buf, f->filename, O_WRONLY |
+               O_CREAT | ((append || c->rest_pos > 0) ? 0 : O_TRUNC)
+#if WANT_NONROOT
+               , 2
+#endif
+       );
+       f->dir_listing = 0;
+
+       if (f->local_file == -1) {
+               numeric(f->owner, 550, strerror(errno));
+       } else if (f->local_file == -2) {
+               f->local_file = -1;
+       } else {
+               f->upload = 1;
+               f->append = append;
+#if WANT_ASCII
+               f->ascii_mode = c->ascii_mode;
+#endif
+               prepare_for_transfer(f);
+       }
+}
+#endif /* WANT_UPLOAD */
+
+/*
+ * cmd_size(): Handle the SIZE command -- returns the size of a
+ *             file. Note that this command is not part of RFC959,
+ *             and thus there is no clear specification (except
+ *             for some ftpext documents, which we try to follow
+ *             as closely as we can). BetaFTPD deviates from wu-ftpd
+ *             in that it lets you check the `size' of directories
+ *             as well (instead of giving 550). This is _not_ the
+ *             size of all the files in the directory, rather how
+ *             much space the directory inode uses.
+ */
+int cmd_size(struct conn * const c)
+{
+#if WANT_ASCII
+       if (c->ascii_mode) {
+               numeric(c, 550, "SIZE not available in ASCII mode.");
+               return 1;
+       }
+#endif
+       {
+               const char * const fname = translate_path(c, c->recv_buf);
+               struct stat buf;
+       
+               TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
+       
+               numeric(c, 213, "%lu", (unsigned long)(buf.st_size));
+               return 1;
+       }
+}
+
+/*
+ * cmd_mdtm(): Handle the MDTM command -- returns the modification
+ *             date/time of a file. See the comments on cmd_size(),
+ *             above.
+ */
+int cmd_mdtm(struct conn * const c)
+{
+       const char * const fname = translate_path(c, c->recv_buf);
+       struct stat buf;
+       struct tm *m;
+
+       TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
+
+       m = gmtime(&(buf.st_mtime));    /* at least wu-ftpd does it in GMT */
+       numeric(c, 213, "%u%02u%02u%02u%02u%02u", m->tm_year + 1900,
+               m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec);
+       return 1;
+}
+
+/*
+ * cmd_abor(): Handle the ABOR command (abort a file transfer). This should
+ *             be clean enough, but isn't tested extensively.
+ */
+int cmd_abor(struct conn * const c)
+{
+       if (c->transfer != NULL) {
+               numeric(c, 426, "File transfer aborted.");
+               destroy_ftran(c->transfer);
+       }
+       numeric(c, 226, "ABOR command processed OK.");
+       return 1;
+}
+
+/*
+ * cmd_dele(): Handle the DELE command (delete a file).
+ */
+int cmd_dele(struct conn * const c)
+{
+       const char * const fname = translate_path(c, c->recv_buf);
+       
+       TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return 1);
+       numeric(c, 250, "File deleted OK.");
+       return 1;
+}
+
+/*
+ * cmd_rnfr(): Handle the RNFR command (take a filename to rename from).
+ */
+int cmd_rnfr(struct conn * const c)
+{
+       const char * const fname = translate_path(c, c->recv_buf);
+       char cwd[256];
+       struct stat buf;
+
+       c->rename_from[0] = '\0';
+       if (fname == NULL) return 1;
+       
+       getcwd(cwd, 256);
+       snprintf(c->rename_from, 256, "%s/%s", cwd, fname);
+
+       /* Just check that the file exists. */
+       TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, return 1);
+
+       numeric(c, 350, "File exists, send RNTO.");
+       return 1;
+}
+
+/*
+ * cmd_rnto(): Handle the RNTO command (do the actual renaming).
+ */
+int cmd_rnto(struct conn * const c)
+{
+       const char * const fname = translate_path(c, c->recv_buf);
+
+       if (fname == NULL) return 1;
+       if (c->rename_from[0] == '\0') {
+               numeric(c, 503, "Please send RNFR first.");
+               return 1;
+       }
+
+       TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, return 1);
+
+       numeric(c, 250, "File renamed successfulyy.");
+       return 1;
+}
+
+/*
+ * cmd_mkd():  Handle the MKD/XMKD command (create a new directory).
+ *             RFC959 is not clear on the error codes for this command --
+ *             one place, 521 is cited as the correct error, but is
+ *             mentioned nowhere else. Different FTP servers differ here
+ *             as well. Thus, I've followed what appears to be the intention
+ *             (having `analogous' errors with STOR), and use 550 instead.
+ *
+ *             Making directories is probably the topic covered most
+ *             extensively by RFC959 (and in the most confusing way as
+ *             well). I try to follow the conventions, but it isn't always
+ *             easy :-) (This code isn't quite easy to understand, because
+ *             temp2 is used twice, in two different roles.)
+ */
+int cmd_mkd(struct conn * const c)
+{
+       const char * const fname = translate_path(c, c->recv_buf);
+       char temp[512], temp2[1024], *cdir;
+       int i, j;
+
+       TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return 1);
+
+       chdir(fname);
+       getcwd(temp2, 512);
+       cdir = do_pwd(c, temp, temp2);
+
+       /* double the quotes in the output */ 
+       for (i = 0, j = 0; i <= strlen(cdir); i++, j++) {
+               temp2[j] = cdir[i];
+               if (cdir[i] == '"') {
+                       temp2[++j] = '"';
+               }
+       }
+       numeric(c, 257, "\"%s\" created.", temp2);
+       return 1;
+}
+
+/*
+ * cmd_rmd():  Handle the RMD/XRMD command. Works just like DELE, only for
+ *             directories.
+ */
+int cmd_rmd(struct conn * const c)
+{
+       const char * const fname = translate_path(c, c->recv_buf);
+
+       TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return 1);
+       numeric(c, 250, "Directory deleted.");
+       return 1;
+}
+
+/*
+ * cmd_allo(): Handle the ALLO command. The command does not do anything, except
+ *             sit around and play compliant. Some Windows FTP servers (Serv-U,
+ *             for instance), verifies that there is enough space on the disk,
+ *             but since we have no idea on what the filesystem will be stored on,
+ *             we just ignore the command.
+ *
+ *             We could theoretically use this information to give more information
+ *             to the full-screen mode, but close to no FTP clients send this
+ *             command, and it would touch too much code.
+ */
+int cmd_allo(struct conn * const c)
+{
+       numeric(c, 202, "No storage allocation necessary.");
+       return 1;
+}
+
+/*
+ * cmd_stat(): Handle the STAT command. Please see README for more details.
+ *             Note that this command is run with euid=root, since it has
+ *             to be able to run before USER.
+ *
+ *             Note that we need to bypass numeric(), to get a multi-line
+ *             reply.
+ */
+#if WANT_STAT
+char conn_state[5][27] = {
+       "Not logged in",
+       "Waiting for e-mail address",
+       "Waiting for password",
+       "Logged in",
+       "Waiting for password",         /* actually non-existant user */
+};
+
+char ftran_state[6][42] = {
+       "Not initialized",
+       "Decided PASV address/port",
+       "Waiting on PASV socket",
+       "Got PORT address/port",
+       "Connecting on PORT address/port",
+       "Transferring file (or connecting on PORT)"
+};
+#endif
+
+int cmd_stat(struct conn * const c)
+{ 
+#if WANT_STAT
+       char buf[1024];
+       int i, err;
+       struct ftran *f = c->transfer;
+
+       snprintf(buf, 1024, "211- FTP server status:\r\n"
+                           "     BetaFTPD version " VERSION " (http://members.xoom.com/sneeze/betaftpd.html)\r\n"
+                           "     Connected to %s\r\n"
+                           "     Control connection state: %s\r\n"
+                           "     TYPE: Image; STRUcture: File; transfer MODE: Stream\r\n"
+                           "     Data connection state: %s\r\n"
+                           "211 End of status\r\n",
+                               inet_ntoa(((struct sockaddr_in *)(&(c->addr)))->sin_addr),
+                               conn_state[c->auth], (f) ? ftran_state[f->state] : ftran_state[0]);
+
+       i = strlen(buf);
+
+       err = send(c->sock, buf, i, 0);
+               if (err == -1 && errno == EPIPE) {
+                       destroy_conn(c);
+               return 0;
+       }
+#else
+       numeric(c, 502, "STAT command disabled for security reasons.");
+#endif
+       return 1;
+}
+
+#if HAVE_MMAP
+/*
+ * _mwrite():  This define is for mmap-listing. It works as a write()
+ *             (not in parameter, but in function), and is used in
+ *             cmd_list() and cmd_nlst() only.
+ *
+ *             Note that this function returns the new position in the
+ *             `file'. The caller is expected to send this information
+ *             back in `pos' at the next call to _mwrite().
+ */
+int _mwrite(const char * const buf, const struct ftran * const f,
+           const int pos, const int count, const int size)
+{
+       if (pos + count >= size) return size;   /* out of space */
+       memcpy(f->file_data + pos, buf, count);
+       return pos + count;
+}
+#endif
+
+/*
+ * mwrite:     This is a short_hand define, making calls to _mwrite() very
+ *             similiar to calls to write().
+ */
+#define mwrite(buf, count) pos = _mwrite((buf), (f), (pos), (count), (size));
+
+/*
+ * long_listing():
+ *             Formats output in `ls -l' style. It returns one line for the
+ *             file PATHNAME, and returns it in retbuf. Setting do_classify
+ *             to nonzero has the same effect as `ls -F'.
+ *
+ *             This command is so long, because simply there is so much to
+ *             be done. GNU ls has some extra functions, but it's close to
+ *             3000 lines too...
+ */
+int long_listing(char * const retbuf, const char * const pathname, const int do_classify)
+{
+       int i, year;
+       char newd[512], temp[1026];
+       struct stat buf;
+       struct tm *t;
+       time_t now;
+       char username[17], groupname[17];
+
+       time(&now);
+       year = localtime(&now)->tm_year;
+       {
+#if !WANT_NONROOT
+               struct passwd *p;
+               struct group *g;
+#endif
+
+               if (lstat(pathname, &buf) == -1) return 0;
+
+#if WANT_NONROOT
+               strcpy(username, nr_get_uname(buf.st_uid));
+               strcpy(groupname, nr_get_gname(buf.st_gid));
+#else
+               p = getpwuid(buf.st_uid);
+               if (p != NULL) {
+                       strncpy(username, p->pw_name, 16);
+                       username[16] = 0;
+               } else {
+                       snprintf(username, 16, "%u", buf.st_uid);
+               }
+
+               g = getgrgid(buf.st_gid);
+               if (g != NULL) {
+                       strncpy(groupname, g->gr_name, 16);
+                       groupname[16] = 0;
+               } else {
+                       snprintf(groupname, 16, "%u", buf.st_gid);
+               }
+#endif
+       }
+
+       /*
+        * This POSIX approximation is based on GNU ls code (and obfuscated
+        * a bit...), to be compatible with `real' ls implementations.
+        */
+       t = localtime(&(buf.st_mtime));
+       strftime(newd, 512, ((now > buf.st_mtime + 6L * 30L * 24L * 60L * 60L) ||
+                            (now < buf.st_mtime - 60L * 60L))
+                       ? "%b %e  %Y" : "%b %e %H:%M", t);
+
+       {
+#if WANT_NONROOT
+               char rights[16];
+
+               if (nr_check_permission(0, pathname, 0, (buf.st_mode & S_IFDIR), rights) == -1) {
+                       /* no permission to even see this file */
+                       return 0;
+               }
+
+               i = snprintf(temp, 1024, "%c%s %3u %-8s %-8s %8lu %12s %s\r\n",
+#else
+               i = snprintf(temp, 1024, "%c%c%c%c%c%c%c%c%c%c %3u %-8s %-8s %8lu %12s %s",
+#endif
+                       decode_mode(buf.st_mode),
+#if WANT_NONROOT
+                       rights,
+#else
+                       (buf.st_mode & S_IRUSR) ? 'r' : '-', 
+                       (buf.st_mode & S_IWUSR) ? 'w' : '-',
+                       (buf.st_mode & S_IXUSR) ? ((buf.st_mode & S_ISUID) ? 's' : 'x') : '-',
+                       (buf.st_mode & S_IRGRP) ? 'r' : '-',
+                       (buf.st_mode & S_IWGRP) ? 'w' : '-',
+                       (buf.st_mode & S_IXGRP) ? ((buf.st_mode & S_ISGID) ? 's' : 'x') : '-',
+                       (buf.st_mode & S_IROTH) ? 'r' : '-',
+                       (buf.st_mode & S_IWOTH) ? 'w' : '-',
+                       (buf.st_mode & S_IXOTH) ? ((buf.st_mode & S_ISVTX) ? 't' : 'x') : '-',
+#endif
+                       buf.st_nlink, username, groupname,
+                       (unsigned long)(buf.st_size), newd, pathname);
+
+#if 0
+               /*
+                * vim needs this extra character for some reason... It's too 
+                * bad I'll have to do it this way, but syntax colouring
+                * that works properly is almost a `must' for me :-)
+                */
+               )
+#endif
+               /* add an extra classification `sign' if we got -F */
+               if (do_classify) {
+                       int len = strlen(temp);
+                       temp[len] = classify(buf.st_mode);
+                       temp[len + 1] = '\0';
+               }
+       }
+
+       strcpy(retbuf, temp);
+       return 1;
+}
+
+/*
+ * cmd_list(): Handles the LIST command (directory listing). Does a
+ *             long listing (of type `ls -l'). The listing work is
+ *             done by do_listing(), below.
+ */
+int cmd_list(struct conn * const c)
+{
+       struct list_options lo;
+
+/*     lo.recursive = 0; */
+       lo.long_listing = 1;
+       lo.classify = 0;
+
+       do_listing(c, &lo);
+       return 1;
+}
+
+/*
+ * cmd_nlst(): Handles the NLST command (plain directory listing).
+ *             Does a plain listing (no dates etc.), unless overridden
+ *             by the `-l' or `-L' flag (case insensitivity because most
+ *             FTP clients don't have a clue about what they send out). 
+ *             The listing work is done by do_listing(), below.
+ */    
+int cmd_nlst(struct conn * const c)
+{
+       struct list_options lo;
+
+/*     lo.recursive = 0; */
+       lo.long_listing = 0;
+       lo.classify = 0;
+
+       do_listing(c, &lo);
+       return 1;
+}
+
+/*
+ * do_listing():
+ *             Prepares any listing buffers, temp files, etc., before
+ *             pushing the work one step further :-)
+ *
+ *             If the directory listing cache is enabled, the cache
+ *             is checked first, to see if we still have a valid entry.
+ */
+void do_listing(struct conn * const c, struct list_options * const lo)
+{
+       int i;
+       char *ptr;
+#if HAVE_MMAP
+       int size;
+#endif
+       struct ftran * const f = c->transfer;
+
+#if WANT_DCACHE
+       char cwd[256];
+#endif
+
+#if WANT_NONROOT
+#warning No nonroot checking for list_core() yet
+#endif
+
+       i = prepare_for_listing(c, &ptr, lo);
+       if (i == -1) {
+               destroy_ftran(c->transfer);
+               return;
+       }
+
+#if WANT_DCACHE
+       getcwd(cwd, 256);
+#endif
+
+#if HAVE_MMAP
+       strcpy(f->filename, "(directory listing)");
+#endif
+
+#if WANT_DCACHE
+       {
+               struct dcache *d = NULL, *next = first_dcache->next_dcache;
+               struct stat buf;
+
+               if (stat(cwd, &buf) > -1) {
+                       /* run through the linked list */
+                       while (next != NULL) {
+                               d = next;
+                               next = d->next_dcache;
+       
+                               if (buf.st_mtime <= d->generated &&
+                                   strcmp(d->dir_name, cwd) == 0 &&
+                                   strcmp(d->pattern, ptr) == 0 &&
+                                   memcmp(&(d->lo), lo,
+                                          sizeof(struct list_options)) == 0) {
+                                       d->use_count++;
+                                       f->dir_cache = d;
+                                       f->file_data = d->dir_data;
+                                       f->size = d->dir_size;
+                                       f->dir_listing = 1;
+                                       f->pos = 0;
+                                       prepare_for_transfer(f);
+                                       return;
+                               }
+                       }
+               }
+       }
+#endif
+
+#if HAVE_MMAP
+       {
+               int num_files = get_num_files(c, ptr, lo);
+               if (num_files == -1) return;
+
+               size = num_files * 160;
+               f->file_data = malloc(size + 1);
+               TRAP_ERROR(f->file_data == NULL, 550, return);
+               list_core(c, ptr, lo, size);
+       }
+#else
+       list_core(c, ptr, lo);
+#endif
+
+#if WANT_DCACHE
+       /* populate the directory listing cache */
+       {
+               struct stat buf;
+               struct dcache *d = alloc_new_dcache();
+               if (d != NULL && stat(cwd, &buf) > -1) {
+                       d->use_count++;
+                       f->dir_cache = d;
+                       d->dir_data = f->file_data;
+                       d->dir_size = f->size;
+                       d->generated = buf.st_mtime;
+
+                       strcpy(d->dir_name, cwd);
+                       strncpy(d->pattern, ptr, 255);
+                       d->pattern[255] = 0;
+                       d->lo = *lo;
+               }
+       }
+#endif
+
+#if HAVE_MMAP
+       f->pos = 0;
+#endif
+       prepare_for_transfer(f);
+}
+
+/*
+ * get_num_files():
+ *             Get the number of files in PATHNAME (optionally matching
+ *             a pattern). Note that c is needed for TRAP_ERROR.
+ */
+int get_num_files(struct conn * const c, const char * const pathname,
+                  struct list_options * const lo)
+{
+       int num_files;
+       glob_t pglob;
+
+       /*
+        * glob() fails to set errno correctly, so we simply guess on
+        * `permission denied'... The others are far less likely to happen.
+        */
+       switch (glob(pathname, 0, NULL, &pglob)) {
+#ifdef GLOB_NOMATCH
+       case GLOB_NOMATCH:
+               return 0;
+#endif
+       case 0:
+               num_files = pglob.gl_pathc;
+               break;
+       default:
+               numeric(c, 550, strerror(EACCES));
+               return -1;
+       }
+
+#if 0  /* the rest of the code doesn't support recursion yet */
+       if (lo->recursive) {
+                       for (i = 0; i < pglob.gl_pathc; i++) {
+                       char *temp = pglob.gl_pathv[i];
+                       struct stat buf;
+
+                       lstat(temp, &buf);
+                       if (S_ISDIR(buf.st_mode)) {
+                               chdir(temp);
+                               num_files += get_num_files(c, "*", lo);
+                               chdir("..");
+                       }
+               }
+       }
+#endif
+
+       return num_files;
+}
+
+/*
+ * list_core():        Enumerate all the files in PATHNAME, and formats them
+ *             according to list_options (calling format functions if
+ *             required).
+ *
+ *             Note that we don't do any realloc() yet, so if your
+ *             _average_ file name length is over a certain size (a little
+ *             under 80 for long listings, and a little under 160 for
+ *             short listings), the list will be truncated. Fix...
+ *
+ *             This function is rather long.
+ */
+void list_core(struct conn * const c, const char * const pathname,
+              struct list_options * const lo
+#if HAVE_MMAP
+               , const int size
+#endif
+               )
+{
+       int i;
+       glob_t pglob;
+#if HAVE_MMAP
+       int pos = 0;
+#endif
+       struct ftran * const f = c->transfer;
+
+        /*
+         * glob() fails to set errno correctly, so we simply guess on
+         * `permission denied'... The others are far less likely to happen.
+         */
+        switch (glob(pathname, GLOB_MARK, NULL, &pglob)) {
+        case 0:
+#ifdef GLOB_NOMATCH
+       case GLOB_NOMATCH:
+#endif
+                break;
+        default:
+                numeric(c, 550, strerror(EACCES));
+                return;
+        }
+
+       if (lo->long_listing) {
+               /* FIX: we may get too high total number if we are running nonroot! */
+               struct stat buf;
+               long unsigned int total = 0;
+               char temp[1024];
+
+               for (i = 0; i < pglob.gl_pathc; i++) {
+                       if (lstat(pglob.gl_pathv[i], &buf) != -1) {
+                               total += buf.st_blocks;
+                       }
+               }
+               i = snprintf(temp, 1024, "total %lu\r\n", total >> 1); 
+#if HAVE_MMAP
+               mwrite(temp, i);
+#else
+               write(f->local_file, temp, i);
+#endif
+       }
+
+       for (i = 0; i < pglob.gl_pathc; i++) {
+               char * const temp = pglob.gl_pathv[i];
+               char buf[2048];
+
+               /* strip `/' away from the pathname -- add it later if -F */
+               {
+                       int len = strlen(temp);
+                       if (temp[len - 1] == '/') {
+                               temp[len - 1] = '\0';
+                       }
+               }
+
+               if (lo->long_listing) {
+                       if (long_listing(buf, temp, lo->classify) == 0) continue;
+               } else {
+                       strcpy(buf, temp);
+                       if (lo->classify) {
+                               struct stat statbuf;
+
+                               if (lstat(buf, &statbuf) != -1) {
+                                       const int len = strlen(buf);
+
+                                       buf[len] = classify(statbuf.st_mode);
+                                       buf[len + 1] = 0;
+                               }
+                       }
+               }
+
+               /* support recursion here some day... */
+
+#if HAVE_MMAP
+               mwrite(buf, strlen(buf));
+               mwrite("\r\n", 2);
+#else
+               write(f->local_file, buf, strlen(buf));
+               write(f->local_file, "\r\n", 2);
+#endif
+       }
+
+#if HAVE_MMAP
+       f->size = pos;
+#else
+       lseek(f->local_file, 0, SEEK_SET);
+#endif
+
+       globfree(&pglob);
+}
+
+/*
+ * cmd_noop(): Handles the NOOP command. Does nothing, doesn't even
+ *             reset the timeout.
+ */
+int cmd_noop(struct conn * const c)
+{
+       numeric(c, 200, "NOOP command successful.");
+       return 1;
+}
+
+/*
+ * cmd_syst(): Handles the SYST command. Returns the system identification.
+ */
+int cmd_syst(struct conn * const c)
+{
+       numeric(c, 215, "UNIX Type: L%u", NBBY);
+       return 1;
+}
+
+/*
+ * cmd_type(): Handles the TYPE command.
+ */
+int cmd_type(struct conn * const c)
+{
+#if WANT_ASCII
+       c->recv_buf[0] &= (255-32);     /* convert to upper case */
+       if (c->recv_buf[0] == 'A') {
+               c->ascii_mode = 1;
+               numeric(c, 200, "Type is ASCII.");
+       } else if (c->recv_buf[0] == 'I') {
+               c->ascii_mode = 0;
+               numeric(c, 200, "Type is IMAGE.");
+       } else {
+               numeric(c, 504, "Unknown type.");
+       }
+#else
+       numeric(c, 200, "TYPE ignored (always I)");
+#endif
+       return 1;
+}
+
+/*
+ * cmd_mode(): Handles the MODE command. We always use stream mode,
+ *             so the argument is ignored.
+ */
+int cmd_mode(struct conn * const c)
+{
+       numeric(c, 200, "MODE ignored (always S)");
+       return 1;
+}
+
+/*
+ * cmd_stru(): Handles the STRU command. We always use file mode,
+ *             so the argument is ignored.
+ */
+int cmd_stru(struct conn * const c)
+{
+       numeric(c, 200, "STRU ignored (always F)");
+       return 1;
+}
+
+/*
+ * cmd_help(): Handle the HELP command. I'm sorry, but I'm unwilling
+ *             to use a lot of space to explain the RFCs in such a message,
+ *             and BetaFTPD doesn't have any special things that should
+ *             be noted anywhere. Thus, this message is close to empty. I
+ *             feel that a 5xx entry would have been better, but that is
+ *             disallowed.
+ *
+ *             As with ACCT, this command is supposed to be executed from
+ *             everywhere, so we have to run without setuid. I don't like
+ *             it, but at the same time I have to idea what could go
+ *             wrong...
+ *
+ *             Perhaps I should make this message sound a little less
+ *             like an error, since the error code is intended for helpful
+ *             messages? :-)
+ */
+int cmd_help(struct conn * const c)
+{
+       numeric(c, 414, "Sorry, no detailed help; use standard FTP commands.");
+       return 1;
+}
+
+/*
+ * cmd_quit(): Handles the QUIT command, which shuts down the control
+ *             and data sockets.
+ */
+int cmd_quit(struct conn * const c)
+{
+       numeric(c, 221, "Have a nice day!");
+       destroy_conn(c);
+       return 0;
+}
+
+/*
+ * cmd_rein():  Handle the REIN command, which does close to a full reset
+ *             of the connection. Much of the code here is intentionally
+ *             copied directly from alloc_new_conn() -- perhaps we should
+ *             modularize this?
+ */
+int cmd_rein(struct conn * const c)
+{
+       destroy_ftran(c->transfer);
+       c->buf_len = c->auth = c->rest_pos = 0;
+
+       /* equals: strcpy(c->curr_dir, "/") ; strcpy(c->last_cmd, ""); */
+       c->curr_dir[0] = '/';
+#if WANT_FULLSCREEN
+       c->curr_dir[1] = c->last_cmd[0] = '\0';
+#else
+       c->curr_dir[1] = '\0';
+#endif
+
+       time(&(c->last_transfer));
+       numeric(c, 220, "BetaFTPD " VERSION " ready.");
+
+       return 1;
+}
+
+#if DOING_PROFILING
+/*
+ * cmd_exit(): Handles the EXIT command, my own `extension' to the
+ *             FTP protocol... IMPORTANT: Only to be used for profiling
+ *             purposes!! (It's needed to get some profiling data out
+ *             of the server after compiling it with -pg, since such data
+ *             is only written on a clear exit()). Any user (even those
+ *             not logged in) can issue an EXIT, and make the server shut
+ *             down without clearing any sockets etc. In other words:
+ *             Don't use it on a production site.
+ */
+void cmd_exit(struct conn * const c)
+{
+       while (first_conn->next_conn)
+               destroy_conn(first_conn->next_conn);
+       exit(0);
+}
+#endif
+
+/*
+ * parse_command():
+ *             Gets a command from c->recv_buf, determines which command
+ *             it is, sets proper effective user-ID and calls the command
+ *             handler. Finally, it cleans up.
+ *
+ *             To me, this command seems optimizable, but I'm not really
+ *             sure where :-)
+ */
+void parse_command(struct conn *c)
+{
+       int cmlen;
+       const struct handler *h = handler_table;        /* first entry */
+
+       if (c == NULL) return;
+
+       /* strip any leading non-ASCII characters (including CR/LFs) */
+       while (c->buf_len > 0 && (c->recv_buf[0] < 'a' || c->recv_buf[0] > 'z')
+                             && (c->recv_buf[0] < 'A' || c->recv_buf[0] > 'Z')) {
+               remove_bytes(c, 1);             /* not good */
+       }
+
+       /* scan, searching for CR or LF */      
+       cmlen = strcspn(c->recv_buf, "\r\n");
+       if (cmlen >= c->buf_len) return;
+
+#if WANT_FULLSCREEN
+       strncpy(c->last_cmd, c->recv_buf, cmlen);
+       c->last_cmd[cmlen] = 0;
+#endif
+
+       do {
+               if ((cmlen >= (strlen(h->cmd_name) + h->add_cmlen)) &&
+                   (strncasecmp(c->recv_buf, h->cmd_name, strlen(h->cmd_name)) == 0)) {
+                       if (c->auth < h->min_auth) {
+                               numeric(c, 503, "Please login with USER and PASS.");
+                               while (c->recv_buf[0] != '\n') remove_bytes(c, 1);
+                       } else {
+                               char schar;
+
+#if !WANT_NONROOT
+                               if (h->do_setuid) {
+                                       seteuid(c->uid);
+                               } else {
+                                       seteuid(0);
+                               }
+#endif
+
+                               remove_bytes(c, strlen(h->cmd_name));
+                               cmlen -= strlen(h->cmd_name);
+                               while (c->recv_buf[0] == ' ') {
+                                       remove_bytes(c, 1);
+                                       cmlen--;
+                               }
+
+                               schar = c->recv_buf[cmlen];
+                               c->recv_buf[cmlen] = 0;
+
+                               /* result of zero means the connection is freed */
+                               if (h->callback(c)) {
+                                       c->recv_buf[cmlen] = schar;
+#if !WANT_NONROOT
+                                       if (h->do_setuid) seteuid(getuid());
+#endif
+                                       remove_bytes(c, cmlen);
+                               }
+                       }
+                       return;
+               }
+       } while ((++h)->callback != NULL);
+
+       numeric(c, 500, "Sorry, no such command.");
+       remove_bytes(c, cmlen); 
+}
+
+/*
+ * prepare_for_transfer():
+ *             Prepares an ftran object for a file transfer, setting
+ *             file size, opening sockets etc.
+ *
+ *             nonroot notice: prepare_for_transfer() assumes all access
+ *             checks are already done.
+ */
+void prepare_for_transfer(struct ftran *f)
+{
+#if WANT_NONROOT
+#warning No nonroot checking for prepare_for_transfer() yet
+#endif
+
+#if HAVE_MMAP
+       /* mmap doesn't make temp files for dir listings */
+       if (!f->dir_listing) {
+#endif
+
+               f->size = lseek(f->local_file, 0, SEEK_END);
+               errno = 0;
+#if WANT_UPLOAD
+               if (f->upload == 0 || f->append == 0 || f->owner->rest_pos != 0)
+#endif 
+                       lseek(f->local_file, f->owner->rest_pos, SEEK_SET);
+#if HAVE_MMAP
+       }
+#endif
+       
+       if (f->state == 1) {            /* PASV connection */
+               f->state = 2;           /* waiting */
+       } else if (f->state == 3) {     /* PORT connection */
+               f->state = 4;
+               connect(f->sock, (struct sockaddr *)&f->sin, sizeof(f->sin));
+               add_fd(f->sock, POLLOUT);
+       }
+       time(&(f->tran_start));
+}
+
+/*
+ * decode_mode():
+ *             Takes a mode_t argument (from a `struct stat'), and
+ *             returns the proper dirlist letter for that type.
+ *
+ *             Note: S_IFLNK seems to be broken, or perhaps I just have
+ *             missed something (S_IFLNK is always set for all *files* on
+ *             my glibc 2.0.111 system).
+ *
+ *             The most common cases are put first, for speed :-)
+ */
+char decode_mode(mode_t mode) {
+       if (mode & S_IFREG)  return '-';
+       if (mode & S_IFDIR)  return 'd';
+       if (mode & S_IFLNK)  return 'l';
+       if (mode & S_IFBLK)  return 'b';
+       if (mode & S_IFCHR)  return 'c';
+       if (mode & S_IFSOCK) return 's';
+       if (mode & S_IFIFO)  return 'f';
+
+       return '-';
+}
+
+/*
+ * translate_path():
+ *             Take an FTP path, do all neccessary root_dir checks,
+ *             change to the correct directory and return the proper
+ *             file name to open/stat/whatever. The path returned is
+ *             relative to the current directory (NOT absolute). chdir()
+ *             in any way will `destroy' this argument.
+ *
+ *             Note that `path' will be _changed_, and used as a return pointer
+ *             base. Do not attempt to free the result from this function --
+ *             if you need to, free path instead.
+ */
+char *translate_path(struct conn * const c, char * const path)
+{
+       char *ptr = NULL;
+
+       /* chdir to the right dir, then chop it off */
+       chdir(c->curr_dir);
+
+       ptr = strrchr(path, '/');
+       if (ptr != NULL) {
+               char save_char = ptr[0];
+               ptr[0] = 0;
+
+               if (do_chdir(c, path) == -1) {
+                       return NULL;
+               }
+               ptr[0] = save_char;
+               ptr++;
+       } else {
+               ptr = path;
+       }
+       return ptr;
+}
+
+/*
+ * do_openfile():
+ *             Opens the file PATH with access parameters FLAGS, translating
+ *             paths and checking permissions as neccessary. Generally, this
+ *             should be used whenever you need an open().
+ *
+ *             The parameters might be a bit confusing. To clarify them a bit:
+ *             c:              IN/OUT (will be changed)
+ *             path:           IN (but _will_ be changed)
+ *             filename:       OUT
+ *             flags:          IN
+ *             check_perm:     IN
+ */
+int do_openfile(struct conn * const c, char * const path,
+               char * const filename, const int flags
+#if WANT_NONROOT
+               , const int check_permission
+#endif
+)
+{
+       char *ptr;
+       struct stat buf;
+
+#if WANT_NONROOT
+       if (nr_check_permission(c->uid, path, check_permission, 0, NULL) == -1) {
+               return -1;
+       }
+#endif
+
+       ptr = translate_path(c, c->recv_buf);
+       if (ptr == NULL) return -1;
+
+#if WANT_UPLOAD
+       if ((flags & O_CREAT) == 0) {
+#endif
+               stat(ptr, &buf);
+               if (!S_ISREG(buf.st_mode)) {
+                       numeric(c, 550, "%s: Not a plain file.", ptr);
+                       return -2;
+               }
+#if WANT_UPLOAD
+       }
+#endif
+
+       if (filename != NULL) { /* filename should always be != NULL */
+               strcpy(filename, ptr);
+       }
+       return open(ptr, flags, 0666);
+}
+
+/*
+ * prepare_for_listing():
+ *             Parse list options, put them back into the list_options
+ *             structure lo, and make temporary room for the list.
+ */
+int prepare_for_listing(struct conn * const c, char ** const ptr,
+                       struct list_options * const lo)
+{
+#if !HAVE_MMAP
+       char *tfname;
+#endif
+       struct ftran *f = c->transfer;
+       char *tmp;
+       char *optr = NULL, *fptr = NULL; 
+#if WANT_NONROOT
+       char chd[512];
+#endif
+
+#if WANT_NONROOT
+#warning No nonroot checking for prepare_for_listing() yet
+#endif
+
+       if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
+               numeric(c, 425, "No data connection set up; please use PASV or PORT.");
+               return -1;
+       }
+
+       /*
+        * A little parameter scanning is required here. There can only
+        * be two parts: the directory name, and any options. We'll find
+        * any options first.
+        */
+       if (c->recv_buf[0] == '-') {
+               optr = c->recv_buf;
+       } else {
+               optr = strstr(c->recv_buf, " -");
+       }
+
+       /* Then see if there are any options to parse. */
+       if (optr != NULL) {
+               while (*++optr) {
+                       switch (*optr & (255-32)) {     /* uppercase */
+#if 0
+                       case 'R':       /* actually case sensitive... */
+                               lo->recursive = 1;
+                               break;
+#endif
+                       case 'L':
+                               lo->long_listing = 1;
+                               break;
+                       case 'F':
+                               lo->classify = 1;
+                               break;
+                       case ' ':
+                               fptr = optr + 1;
+                               *(optr--) = 0;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       } else {
+               fptr = c->recv_buf;
+       }
+       
+       /* then we chdir to the dir in fptr (if any) */
+       tmp = fptr ? strrchr(fptr, '/') : NULL;
+       if (tmp != NULL) {
+               tmp[0] = 0;
+               if (do_chdir(c, fptr) == -1) return -1;
+               fptr = tmp + 1;
+       } else {
+               /* current directory */
+               TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1);
+       }
+
+       /* if no argument, choose all files */
+       if (fptr == NULL || fptr[0] == 0) fptr = "*";
+       *ptr = fptr;
+
+#if WANT_NONROOT
+       getcwd(chd, 512);
+       if (nr_check_permission(c->uid, chd, 4, 1, NULL) == -1) {
+               numeric(c, 550, "Permission denied");
+               return -1;
+       }
+#endif
+
+#if !HAVE_MMAP
+       tfname = tempnam(NULL, "ftp");
+
+#if WANT_NONROOT
+       if (tfname == NULL) tfname = tempnam("/", "ftp");
+#endif
+
+       TRAP_ERROR(tfname == NULL, 550, return -1);
+       strcpy(f->filename, tfname);
+       free(tfname);
+
+       f->local_file = open(f->filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
+       TRAP_ERROR(f->local_file == -1, 550, return -1);
+#endif
+       f->dir_listing = 1;
+#if WANT_UPLOAD
+       f->upload = 0;
+#endif
+
+       return 0;
+}
+
+/*
+ * classify(): Takes a mode_t argument (from `struct stat'), and returns
+ *             the parameter to be used in an `ls -F'-style listing.
+ */
+char classify(const mode_t mode)
+{
+       if (S_ISREG(mode)) {
+               if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+                       return '*';
+               } else {
+                       return '\0';
+               }
+       }
+       if (S_ISDIR(mode)) return '/';
+       if (S_ISLNK(mode)) return '@';
+       if (S_ISSOCK(mode)) return '=';
+       if (S_ISFIFO(mode)) return '|';
+       return '\0'; 
+}
diff --git a/cmds.h b/cmds.h
new file mode 100644 (file)
index 0000000..b7735ff
--- /dev/null
+++ b/cmds.h
@@ -0,0 +1,120 @@
+/*  cmds.c: BetaFTPD command handler prototypes
+    Copyright (C) 1999-2000 Steinar H. Gunderson
+
+    This program is is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License, version 2 if the
+    License as published by the Free Software Foundation.
+
+    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * TRAP_ERROR: This is a quick way of doing a test for an error condition.
+ *             if an error occurs (or more precisely, if the value supplied is
+ *             true), an error message is sent back to the client. This is _not_
+ *             a debugging macro. The condition is allowed to have side effects,
+ *             and is only evaluated once.
+ *
+ *             The second argument is the FTP error code to send back, in case
+ *             of an error (the error message itself will be supplied by the
+ *             system). The third argument is code to execute after the FTP error
+ *             has been sent (typically `return' or `return -1').
+ *
+ *             If TRAP_ERROR_DEBUG is defined, some extra debugging info is
+ *             sent. Don't enable this for a normal server, it could be a
+ *             security risk.
+ */
+#undef TRAP_ERROR_DEBUG
+/* #define TRAP_ERROR_DEBUG 1 */
+
+#ifdef TRAP_ERROR_DEBUG
+#define TRAP_ERROR(x, num, y) TRAP_ERROR_INTERNAL(x, num, y, __FILE__, __LINE__)
+#define TRAP_ERROR_INTERNAL(x, num, y, fl, ln) \
+       if (x) { \
+               numeric(c, num, "%s (%s:%d)", strerror(errno), fl, ln); \
+               y; \
+       }
+#else
+#define TRAP_ERROR(x, num, y) \
+       if (x) { \
+               numeric(c, num, strerror(errno)); \
+               y; \
+       }
+#endif
+
+#define CMD_PROTO(cmd) int cmd_ ## cmd ## (struct conn * const c)
+
+int do_chdir(struct conn * const c, const char * const newd);
+CMD_PROTO(user);
+CMD_PROTO(pass);
+CMD_PROTO(acct);
+CMD_PROTO(port);
+CMD_PROTO(pasv);
+CMD_PROTO(pwd);
+CMD_PROTO(cwd);
+CMD_PROTO(cdup);
+CMD_PROTO(rest);
+CMD_PROTO(retr);
+CMD_PROTO(size);
+CMD_PROTO(mdtm);
+CMD_PROTO(abor);
+CMD_PROTO(dele);
+CMD_PROTO(rnfr);
+CMD_PROTO(rnto);
+CMD_PROTO(mkd);
+CMD_PROTO(rmd);
+CMD_PROTO(allo);
+CMD_PROTO(stat);
+CMD_PROTO(list);
+CMD_PROTO(nlst);
+CMD_PROTO(syst);
+CMD_PROTO(noop);
+CMD_PROTO(type);
+CMD_PROTO(mode);
+CMD_PROTO(stru);
+CMD_PROTO(help);
+CMD_PROTO(quit);
+CMD_PROTO(rein);
+
+#if WANT_UPLOAD
+CMD_PROTO(stor);
+CMD_PROTO(appe);
+#endif
+
+#if DOING_PROFILING
+CMD_PROTO(exit);       
+#endif
+
+void cmd_cwd_internal(struct conn * const c, const char * const newd);
+void parse_command(struct conn *c);
+void prepare_for_transfer(struct ftran *f);
+char decode_mode(mode_t mode);
+char *translate_path(struct conn * const c, char * const path);
+int do_openfile(struct conn * const c, char * const path,
+               char * const filename, const int flags
+#if WANT_NONROOT
+               , const int check_permission
+#endif
+);
+int prepare_for_listing(struct conn * const c, char ** const ptr,
+                       struct list_options * const lo);
+void do_listing(struct conn * const c, struct list_options * const lo);
+int get_num_files(struct conn * const c, const char * const pathname,
+                   struct list_options * const lo);
+void list_core(struct conn * const c, const char * const pathname,
+              struct list_options * const lo
+#if HAVE_MMAP
+               , const int size
+#endif
+);
+char classify(const mode_t mode);
+void do_store(struct conn * const c, int append);
+char *do_pwd(struct conn * const c, char * const retbuf, const char * const dir);
+
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..68a07dd
--- /dev/null
@@ -0,0 +1,172 @@
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+/* Define if we want to do ascii transfers.  */
+#undef WANT_ASCII
+
+/* Define if we need transfer logging.  */
+#undef WANT_XFERLOG
+
+/* Define if we need fullscreen mode. */
+#undef WANT_FULLSCREEN
+
+/* Define if we need upload support. */
+#undef WANT_UPLOAD
+
+/* Define if we want statistics support. */
+#undef WANT_STAT
+
+/* Define if we need shadow support. */
+#undef WANT_SHADOW
+
+/* Define if we need to fork. */
+#undef WANT_FORK
+
+/* Define if we don't have root privilegies. */
+#undef WANT_NONROOT
+
+/* Define if we want directory listing cache */
+#undef WANT_DCACHE
+
+/* Define if we want to support .message files et al */
+#undef WANT_MESSAGE
+
+/* Define to empty if the keyword does not work.  */
+#undef const
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#undef gid_t
+
+/* Define if you have a working `mmap' system call.  */
+#undef HAVE_MMAP
+
+/* Define as the return type of signal handlers (int or void).  */
+#undef RETSIGTYPE
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#undef uid_t
+
+/* Define if you have the getpagesize function.  */
+#undef HAVE_GETPAGESIZE
+
+/* Define if you have the snprintf function.  */
+#undef HAVE_SNPRINTF
+
+/* Define if you have the vsnprintf function.  */
+#undef HAVE_VSNPRINTF
+
+/* Define if you have the <arpa/inet.h> header file.  */
+#undef HAVE_ARPA_INET_H
+
+/* Define if you have the <assert.h> header file.  */
+#undef HAVE_ASSERT_H
+
+/* Define if you have the <crypt.h> header file.  */
+#undef HAVE_CRYPT_H
+
+/* Define if you have the <dirent.h> header file.  */
+#undef HAVE_DIRENT_H
+
+/* Define if you have the <errno.h> header file.  */
+#undef HAVE_ERRNO_H
+
+/* Define if you have the <fcntl.h> header file.  */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <glob.h> header file.  */
+#undef HAVE_GLOB_H
+
+/* Define if you have the <grp.h> header file.  */
+#undef HAVE_GRP_H
+
+/* Define if you have the <netdb.h> header file.  */
+#undef HAVE_NETDB_H
+
+/* Define if you have the <netinet/in.h> header file.  */
+#undef HAVE_NETINET_IN_H
+
+/* Define if you have the <netinet/in_systm.h> header file.  */
+#undef HAVE_NETINET_IN_SYSTM_H
+
+/* Define if you have the <netinet/ip.h> header file.  */
+#undef HAVE_NETINET_IP_H
+
+/* Define if you have the <netinet/tcp.h> header file.  */
+#undef HAVE_NETINET_TCP_H
+
+/* Define if you have the <pwd.h> header file.  */
+#undef HAVE_PWD_H
+
+/* Define if you have the <shadow.h> header file.  */
+#undef HAVE_SHADOW_H
+
+/* Define if you have the <signal.h> header file.  */
+#undef HAVE_SIGNAL_H
+
+/* Define if you have the <stdarg.h> header file.  */
+#undef HAVE_STDARG_H
+
+/* Define if you have the <stdio.h> header file.  */
+#undef HAVE_STDIO_H
+
+/* Define if you have the <stdlib.h> header file.  */
+#undef HAVE_STDLIB_H
+
+/* Define if you have the <string.h> header file.  */
+#undef HAVE_STRING_H
+
+/* Define if you have the <strings.h> header file.  */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <stropts.h> header file.  */
+#undef HAVE_STROPTS_H
+
+/* Define if you have the <sys/conf.h> header file.  */
+#undef HAVE_SYS_CONF_H
+
+/* Define if you have the <sys/filio.h> header file.  */
+#undef HAVE_SYS_FILIO_H
+
+/* Define if you have the <sys/ioctl.h> header file.  */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the <sys/param.h> header file.  */
+#undef HAVE_SYS_PARAM_H
+
+/* Define if you have the <sys/poll.h> header file.  */
+#undef HAVE_SYS_POLL_H
+
+/* Define if you have the <sys/sendfile.h> header file.  */
+#undef HAVE_SYS_SENDFILE_H
+
+/* Define if you have the <sys/signal.h> header file.  */
+#undef HAVE_SYS_SIGNAL_H
+
+/* Define if you have the <sys/socket.h> header file.  */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define if you have the <sys/stat.h> header file.  */
+#undef HAVE_SYS_STAT_H
+
+/* Define if you have the <sys/time.h> header file.  */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <sys/types.h> header file.  */
+#undef HAVE_SYS_TYPES_H
+
+/* Define if you have the <time.h> header file.  */
+#undef HAVE_TIME_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+/* Define if you have poll(). */
+#undef HAVE_POLL
+
+/* Define if you have sendfile() with the Linux semantics. */
+#undef HAVE_LINUX_SENDFILE
+
+/* Define if you have sendfile() with the BSD semantics. */
+#undef HAVE_BSD_SENDFILE
+
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..c5fcf7c
--- /dev/null
+++ b/configure
@@ -0,0 +1,3008 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+  --enable-xferlog        Enable transfer log support"
+ac_help="$ac_help
+  --enable-ascii          Enable ASCII mode support"
+ac_help="$ac_help
+  --enable-fullscreen     Run in fullscreen mode (implies --disable-fork)"
+ac_help="$ac_help
+  --enable-fork           Make the server fork into the background"
+ac_help="$ac_help
+  --enable-upload         Enable upload support"
+ac_help="$ac_help
+  --enable-stat           Enable STAT command (see README first)"
+ac_help="$ac_help
+  --enable-dcache         Enable directory listing cache"
+ac_help="$ac_help
+  --enable-message        Enable .message files etc."
+ac_help="$ac_help
+  --enable-shadow         Enable shadow password support"
+ac_help="$ac_help
+  --enable-nonroot        Do not need root access (implies --disable-shadow)"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.13"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=cmds.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+if test "x$prefix" = xNONE; then
+echo $ac_n "checking for prefix by $ac_c" 1>&6
+# Extract the first word of "ftpd", so it can be a program name with args.
+set dummy ftpd; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:550: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_FTPD'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$FTPD" in
+  /*)
+  ac_cv_path_FTPD="$FTPD" # Let the user override the test with a path.
+  ;;
+  ?:/*)                         
+  ac_cv_path_FTPD="$FTPD" # Let the user override the test with a dos path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do 
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_FTPD="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac
+fi
+FTPD="$ac_cv_path_FTPD"
+if test -n "$FTPD"; then
+  echo "$ac_t""$FTPD" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -n "$ac_cv_path_FTPD"; then
+    prefix=`echo $ac_cv_path_FTPD|sed 's%/[^/][^/]*//*[^/][^/]*$%%'`
+  fi
+fi
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:591: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:621: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+       continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:672: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:704: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 715 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:720: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:746: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:751: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:760: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:779: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+
+
+LIBS=""
+
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+echo "configure:815: checking for working const" >&5
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 820 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this.  */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this.  */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this.  */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+   It does not let you subtract one const X* pointer from another in an arm
+   of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this.  */
+  char *t;
+  char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+  *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+  int x[] = {25, 17};
+  const int *foo = &x[0];
+  ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+  typedef const int *iptr;
+  iptr p = 0;
+  ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+     "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+  struct s { int j; const int *ap[3]; };
+  struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+  const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:869: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_c_const=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_c_const=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+  cat >> confdefs.h <<\EOF
+#define const 
+EOF
+
+fi
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:890: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 905 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:911: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 922 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:928: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -nologo -E"
+  cat > conftest.$ac_ext <<EOF
+#line 939 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:945: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+for ac_hdr in unistd.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:973: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 978 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:983: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in getpagesize
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1012: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1017 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1040: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+echo $ac_n "checking for working mmap""... $ac_c" 1>&6
+echo "configure:1065: checking for working mmap" >&5
+if eval "test \"`echo '$''{'ac_cv_func_mmap_fixed_mapped'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_mmap_fixed_mapped=no
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1073 "configure"
+#include "confdefs.h"
+
+/* Thanks to Mike Haertel and Jim Avera for this test.
+   Here is a matrix of mmap possibilities:
+       mmap private not fixed
+       mmap private fixed at somewhere currently unmapped
+       mmap private fixed at somewhere already mapped
+       mmap shared not fixed
+       mmap shared fixed at somewhere currently unmapped
+       mmap shared fixed at somewhere already mapped
+   For private mappings, we should verify that changes cannot be read()
+   back from the file, nor mmap's back from the file at a different
+   address.  (There have been systems where private was not correctly
+   implemented like the infamous i386 svr4.0, and systems where the
+   VM page cache was not coherent with the filesystem buffer cache
+   like early versions of FreeBSD and possibly contemporary NetBSD.)
+   For shared mappings, we should conversely verify that changes get
+   propogated back to all the places they're supposed to be.
+
+   Grep wants private fixed already mapped.
+   The main things grep needs to know about mmap are:
+   * does it exist and is it safe to write into the mmap'd area
+   * how to use it (BSD variants)  */
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+/* This mess was copied from the GNU getpagesize.h.  */
+#ifndef HAVE_GETPAGESIZE
+# ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+# endif
+
+/* Assume that all systems that can run configure have sys/param.h.  */
+# ifndef HAVE_SYS_PARAM_H
+#  define HAVE_SYS_PARAM_H 1
+# endif
+
+# ifdef _SC_PAGESIZE
+#  define getpagesize() sysconf(_SC_PAGESIZE)
+# else /* no _SC_PAGESIZE */
+#  ifdef HAVE_SYS_PARAM_H
+#   include <sys/param.h>
+#   ifdef EXEC_PAGESIZE
+#    define getpagesize() EXEC_PAGESIZE
+#   else /* no EXEC_PAGESIZE */
+#    ifdef NBPG
+#     define getpagesize() NBPG * CLSIZE
+#     ifndef CLSIZE
+#      define CLSIZE 1
+#     endif /* no CLSIZE */
+#    else /* no NBPG */
+#     ifdef NBPC
+#      define getpagesize() NBPC
+#     else /* no NBPC */
+#      ifdef PAGESIZE
+#       define getpagesize() PAGESIZE
+#      endif /* PAGESIZE */
+#     endif /* no NBPC */
+#    endif /* no NBPG */
+#   endif /* no EXEC_PAGESIZE */
+#  else /* no HAVE_SYS_PARAM_H */
+#   define getpagesize() 8192  /* punt totally */
+#  endif /* no HAVE_SYS_PARAM_H */
+# endif /* no _SC_PAGESIZE */
+
+#endif /* no HAVE_GETPAGESIZE */
+
+#ifdef __cplusplus
+extern "C" { void *malloc(unsigned); }
+#else
+char *malloc();
+#endif
+
+int
+main()
+{
+       char *data, *data2, *data3;
+       int i, pagesize;
+       int fd;
+
+       pagesize = getpagesize();
+
+       /*
+        * First, make a file with some known garbage in it.
+        */
+       data = malloc(pagesize);
+       if (!data)
+               exit(1);
+       for (i = 0; i < pagesize; ++i)
+               *(data + i) = rand();
+       umask(0);
+       fd = creat("conftestmmap", 0600);
+       if (fd < 0)
+               exit(1);
+       if (write(fd, data, pagesize) != pagesize)
+               exit(1);
+       close(fd);
+
+       /*
+        * Next, try to mmap the file at a fixed address which
+        * already has something else allocated at it.  If we can,
+        * also make sure that we see the same garbage.
+        */
+       fd = open("conftestmmap", O_RDWR);
+       if (fd < 0)
+               exit(1);
+       data2 = malloc(2 * pagesize);
+       if (!data2)
+               exit(1);
+       data2 += (pagesize - ((int) data2 & (pagesize - 1))) & (pagesize - 1);
+       if (data2 != mmap(data2, pagesize, PROT_READ | PROT_WRITE,
+           MAP_PRIVATE | MAP_FIXED, fd, 0L))
+               exit(1);
+       for (i = 0; i < pagesize; ++i)
+               if (*(data + i) != *(data2 + i))
+                       exit(1);
+
+       /*
+        * Finally, make sure that changes to the mapped area
+        * do not percolate back to the file as seen by read().
+        * (This is a bug on some variants of i386 svr4.0.)
+        */
+       for (i = 0; i < pagesize; ++i)
+               *(data2 + i) = *(data2 + i) + 1;
+       data3 = malloc(pagesize);
+       if (!data3)
+               exit(1);
+       if (read(fd, data3, pagesize) != pagesize)
+               exit(1);
+       for (i = 0; i < pagesize; ++i)
+               if (*(data + i) != *(data3 + i))
+                       exit(1);
+       close(fd);
+       unlink("conftestmmap");
+       exit(0);
+}
+
+EOF
+if { (eval echo configure:1213: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_func_mmap_fixed_mapped=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_func_mmap_fixed_mapped=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_func_mmap_fixed_mapped" 1>&6
+if test $ac_cv_func_mmap_fixed_mapped = yes; then
+  cat >> confdefs.h <<\EOF
+#define HAVE_MMAP 1
+EOF
+
+fi
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:1236: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1241 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:1258: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_type_signal=void
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+for ac_func in snprintf vsnprintf
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1279: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1284 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1307: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
+echo "configure:1333: checking for socket in -lsocket" >&5
+ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lsocket  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1341 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:1352: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  result=yes
+else
+  echo "$ac_t""no" 1>&6
+result=no
+fi
+
+if test "$result" = "yes"; then
+  LIBS="$LIBS -lsocket"
+else
+  echo $ac_n "checking for connect in -lsocket""... $ac_c" 1>&6
+echo "configure:1377: checking for connect in -lsocket" >&5
+ac_lib_var=`echo socket'_'connect | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lsocket  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1385 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char connect();
+
+int main() {
+connect()
+; return 0; }
+EOF
+if { (eval echo configure:1396: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  result=yes
+else
+  echo "$ac_t""no" 1>&6
+result=no
+fi
+
+  if test "$result" = "yes"; then
+    LIBS="$LIBS -lsocket"
+  fi
+fi
+
+echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6
+echo "configure:1423: checking for crypt in -lcrypt" >&5
+ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lcrypt  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1431 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char crypt();
+
+int main() {
+crypt()
+; return 0; }
+EOF
+if { (eval echo configure:1442: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  result=yes
+else
+  echo "$ac_t""no" 1>&6
+result=no
+fi
+
+if test "$result" = "yes"; then
+  LIBS="$LIBS -lcrypt"
+fi
+
+
+
+
+echo $ac_n "checking whether to enable xferlog""... $ac_c" 1>&6
+echo "configure:1471: checking whether to enable xferlog" >&5
+# Check whether --enable-xferlog or --disable-xferlog was given.
+if test "${enable_xferlog+set}" = set; then
+  enableval="$enable_xferlog"
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define WANT_XFERLOG 1
+EOF
+
+fi
+echo "$ac_t""$enableval" 1>&6
+
+
+echo $ac_n "checking whether to enable ascii""... $ac_c" 1>&6
+echo "configure:1494: checking whether to enable ascii" >&5
+# Check whether --enable-ascii or --disable-ascii was given.
+if test "${enable_ascii+set}" = set; then
+  enableval="$enable_ascii"
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define WANT_ASCII 1
+EOF
+
+fi
+echo "$ac_t""$enableval" 1>&6
+
+
+echo $ac_n "checking whether to enable fullscreen""... $ac_c" 1>&6
+echo "configure:1517: checking whether to enable fullscreen" >&5
+# Check whether --enable-fullscreen or --disable-fullscreen was given.
+if test "${enable_fullscreen+set}" = set; then
+  enableval="$enable_fullscreen"
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define WANT_FULLSCREEN 1
+EOF
+
+fi
+echo "$ac_t""$enableval" 1>&6
+
+
+if test "$enableval" = "no"; then
+       
+echo $ac_n "checking whether to enable fork""... $ac_c" 1>&6
+echo "configure:1542: checking whether to enable fork" >&5
+# Check whether --enable-fork or --disable-fork was given.
+if test "${enable_fork+set}" = set; then
+  enableval="$enable_fork"
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define WANT_FORK 1
+EOF
+
+fi
+echo "$ac_t""$enableval" 1>&6
+
+fi
+
+
+echo $ac_n "checking whether to enable upload""... $ac_c" 1>&6
+echo "configure:1567: checking whether to enable upload" >&5
+# Check whether --enable-upload or --disable-upload was given.
+if test "${enable_upload+set}" = set; then
+  enableval="$enable_upload"
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define WANT_UPLOAD 1
+EOF
+
+fi
+echo "$ac_t""$enableval" 1>&6
+
+
+echo $ac_n "checking whether to enable stat""... $ac_c" 1>&6
+echo "configure:1590: checking whether to enable stat" >&5
+# Check whether --enable-stat or --disable-stat was given.
+if test "${enable_stat+set}" = set; then
+  enableval="$enable_stat"
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define WANT_STAT 1
+EOF
+
+fi
+echo "$ac_t""$enableval" 1>&6
+
+
+echo $ac_n "checking whether to enable dcache""... $ac_c" 1>&6
+echo "configure:1613: checking whether to enable dcache" >&5
+# Check whether --enable-dcache or --disable-dcache was given.
+if test "${enable_dcache+set}" = set; then
+  enableval="$enable_dcache"
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define WANT_DCACHE 1
+EOF
+
+fi
+echo "$ac_t""$enableval" 1>&6
+
+
+echo $ac_n "checking whether to enable message""... $ac_c" 1>&6
+echo "configure:1636: checking whether to enable message" >&5
+# Check whether --enable-message or --disable-message was given.
+if test "${enable_message+set}" = set; then
+  enableval="$enable_message"
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define WANT_MESSAGE 1
+EOF
+
+fi
+echo "$ac_t""$enableval" 1>&6
+
+
+
+echo $ac_n "checking whether to enable shadow""... $ac_c" 1>&6
+echo "configure:1660: checking whether to enable shadow" >&5
+# Check whether --enable-shadow or --disable-shadow was given.
+if test "${enable_shadow+set}" = set; then
+  enableval="$enable_shadow"
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define WANT_SHADOW 1
+EOF
+
+fi
+echo "$ac_t""$enableval" 1>&6
+
+
+nonroot_support=yes
+
+if test "$enableval" = "yes"; then
+       for ac_hdr in shadow.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1689: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1694 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1699: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+       nonroot_support=no
+else
+                                                                               echo
+fi
+
+if test "$nonroot_support" = "yes"; then
+       
+echo $ac_n "checking whether to enable nonroot""... $ac_c" 1>&6
+echo "configure:1733: checking whether to enable nonroot" >&5
+# Check whether --enable-nonroot or --disable-nonroot was given.
+if test "${enable_nonroot+set}" = set; then
+  enableval="$enable_nonroot"
+  result=yes
+else
+  result=no
+fi
+
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define WANT_NONROOT 1
+EOF
+
+fi
+echo "$ac_t""$enableval" 1>&6
+
+else
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       echo "configure: warning: " 1>&2
+        echo "configure: warning: Please read the README.nonroot file before using --enable-nonroot" 1>&2
+       echo "configure: warning: " 1>&2
+else
+
+       echo $ac_n "checking how to get effective uid""... $ac_c" 1>&6
+echo "configure:1765: checking how to get effective uid" >&5
+       if test -n "$EUID"; then
+               B_UID=$EUID
+               echo "$ac_t""\$EUID ($EUID)" 1>&6
+       elif test -n "$euid"; then
+               B_UID=$euid
+               echo "$ac_t""\$euid ($euid)" 1>&6
+       elif test -n "$UID"; then
+               B_UID=$UID
+               echo "$ac_t""\$UID ($UID)" 1>&6
+       elif test -n "$uid"; then
+               B_UID=$uid
+               echo "$ac_t""\$uid ($uid)" 1>&6
+       elif test "`whoami 2>/dev/null`" = "root"; then
+               B_UID=0
+               echo "$ac_t""whoami (root)" 1>&6
+       else
+               # assume we're not root
+               B_UID=1
+               echo "$ac_t""not found" 1>&6
+       fi
+
+       if test "$B_UID" -ne 0; then
+               echo "configure: warning: " 1>&2
+               echo "configure: warning: You do not seem to have root privilegies. If you" 1>&2
+               echo "configure: warning: want to run BetaFTPD without being root, please" 1>&2
+               echo "configure: warning: consider giving the --enable-nonroot flag to" 1>&2
+               echo "configure: warning: configure, and read the README.nonroot file." 1>&2
+               echo "configure: warning: " 1>&2
+       fi
+fi
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1798: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1803 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1811: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1828 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1846 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1867 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1878: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  :
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for uid_t""... $ac_c" 1>&6
+echo "configure:1902: checking for uid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1907 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])uid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_uid_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_uid_t" 1>&6
+if test $ac_cv_type_uid_t = no; then
+  cat >> confdefs.h <<\EOF
+#define uid_t int
+EOF
+
+fi
+
+echo $ac_n "checking for gid_t""... $ac_c" 1>&6
+echo "configure:1935: checking for gid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_gid_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1940 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])gid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_gid_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_gid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_gid_t" 1>&6
+if test $ac_cv_type_gid_t = no; then
+  cat >> confdefs.h <<\EOF
+#define gid_t int
+EOF
+
+fi
+
+
+for ac_hdr in crypt.h unistd.h time.h sys/time.h errno.h netinet/in.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1972: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1977 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1982: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in netinet/ip.h stropts.h sys/conf.h arpa/inet.h sys/filio.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2012: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2017 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2022: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in netinet/tcp.h sys/types.h netdb.h glob.h stdio.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2052: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2057 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2062: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in stdlib.h stdarg.h stdlib.h string.h strings.h fcntl.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2092: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2097 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2102: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in sys/ioctl.h sys/socket.h sys/stat.h sys/param.h signal.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2132: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2137 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2142: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in sys/signal.h dirent.h pwd.h grp.h netinet/in_systm.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2172: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2177 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2182: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_hdr in assert.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2212: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2217 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2222: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+ac_safe=`echo "netinet/tcp.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for netinet/tcp.h""... $ac_c" 1>&6
+echo "configure:2251: checking for netinet/tcp.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2256 "configure"
+#include "confdefs.h"
+#include <netinet/tcp.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2261: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  result=yes
+else
+  echo "$ac_t""no" 1>&6
+result=no
+fi
+
+
+
+if test "$result" = "yes"; then
+     echo $ac_n "checking if netinet/tcp.h is enough""... $ac_c" 1>&6
+echo "configure:2287: checking if netinet/tcp.h is enough" >&5
+     cat > conftest.$ac_ext <<EOF
+#line 2289 "configure"
+#include "confdefs.h"
+#include <netinet/tcp.h>
+#ifndef TCP_NODELAY
+      yes
+#endif
+     
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "yes" >/dev/null 2>&1; then
+  rm -rf conftest*
+  result=no
+else
+  rm -rf conftest*
+  result=yes
+fi
+rm -f conftest*
+
+     echo "$ac_t""$result" 1>&6
+fi
+
+if test "$result" = "no"; then
+       ac_safe=`echo "linux/socket.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for linux/socket.h""... $ac_c" 1>&6
+echo "configure:2313: checking for linux/socket.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2318 "configure"
+#include "confdefs.h"
+#include <linux/socket.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2323: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+for ac_hdr in sys/poll.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2350: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2355 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2360: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+echo $ac_n "checking for poll()""... $ac_c" 1>&6
+echo "configure:2388: checking for poll()" >&5
+cat > conftest.$ac_ext <<EOF
+#line 2390 "configure"
+#include "confdefs.h"
+
+#if HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+
+int main() {
+
+       struct pollfd fds[1];
+       fds[0].fd = 0;
+       fds[0].events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL;
+
+       poll(fds, 1, 1000);
+
+; return 0; }
+EOF
+if { (eval echo configure:2407: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  enableval=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  enableval=no
+fi
+rm -f conftest*
+echo "$ac_t""$enableval" 1>&6
+
+if test "$enableval" = "yes"; then
+       cat >> confdefs.h <<\EOF
+#define HAVE_POLL 1
+EOF
+
+fi
+
+ac_safe=`echo "sys/sendfile.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for sys/sendfile.h""... $ac_c" 1>&6
+echo "configure:2428: checking for sys/sendfile.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2433 "configure"
+#include "confdefs.h"
+#include <sys/sendfile.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2438: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  result=yes
+else
+  echo "$ac_t""no" 1>&6
+result=no
+fi
+
+
+for ac_hdr in sys/sendfile.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2465: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2470 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2475: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+if test "$result" = "yes"; then
+       echo $ac_n "checking for Linux sendfile()""... $ac_c" 1>&6
+echo "configure:2504: checking for Linux sendfile()" >&5
+       cat > conftest.$ac_ext <<EOF
+#line 2506 "configure"
+#include "confdefs.h"
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_SYS_LIMITS_H
+#include <sys/limits.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+int main() {
+
+       int out_fd = 1, in_fd = 0;
+       off_t offset = 0;
+       size_t size = 1024;
+
+       sendfile(out_fd, in_fd, &offset, size);
+
+; return 0; }
+EOF
+if { (eval echo configure:2542: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  enableval=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  enableval=no
+fi
+rm -f conftest*
+       echo "$ac_t""$enableval" 1>&6
+
+       if test "$enableval" = "yes"; then
+               cat >> confdefs.h <<\EOF
+#define HAVE_LINUX_SENDFILE 1
+EOF
+
+       fi
+fi
+
+ac_safe=`echo "sys/uio.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for sys/uio.h""... $ac_c" 1>&6
+echo "configure:2564: checking for sys/uio.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2569 "configure"
+#include "confdefs.h"
+#include <sys/uio.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2574: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  result=yes
+else
+  echo "$ac_t""no" 1>&6
+result=no
+fi
+
+if test "$result" = "yes"; then
+       echo $ac_n "checking for BSD sendfile()""... $ac_c" 1>&6
+echo "configure:2598: checking for BSD sendfile()" >&5
+       cat > conftest.$ac_ext <<EOF
+#line 2600 "configure"
+#include "confdefs.h"
+
+#if HAVE_SYS_LIMITS_H
+#include <sys/limits.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+int main() {
+
+        int in_fd = 0, out_sock = 3;
+        off_t offset;
+        size_t size = 1024;
+       struct sf_hdtr hdtr;
+
+       hdtr.hdr_cnt = 0;
+       hdtr.trl_cnt = 0;
+
+        sendfile(in_fd, out_sock, offset, size, &hdtr, &offset, 0);
+
+; return 0; }
+EOF
+if { (eval echo configure:2641: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  enableval=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  enableval=no
+fi
+rm -f conftest*
+       echo "$ac_t""$enableval" 1>&6
+
+       if test "$enableval" = "yes"; then
+               cat >> confdefs.h <<\EOF
+#define HAVE_BSD_SENDFILE 1
+EOF
+
+       fi
+fi
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+
+trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@FTPD@%$FTPD%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='\([     ][      ]*\)[^  ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='\([     ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..b280bc1
--- /dev/null
@@ -0,0 +1,288 @@
+AC_INIT(cmds.c)
+AC_PREFIX_PROGRAM(ftpd)
+AC_CONFIG_HEADER(config.h)
+AC_PROG_CC
+
+dnl
+dnl Set of libraries to use
+dnl
+LIBS=""
+AC_SUBST(LIBS)
+
+AC_C_CONST
+AC_FUNC_MMAP
+AC_TYPE_SIGNAL
+AC_CHECK_FUNCS(snprintf vsnprintf)
+
+dnl
+dnl Check if we need -lsocket -- taken from wu-ftpd
+dnl
+AC_CHECK_LIB(socket,socket,result=yes,result=no)
+if test "$result" = "yes"; then
+  LIBS="$LIBS -lsocket"
+else
+  AC_CHECK_LIB(socket,connect,result=yes,result=no)
+  if test "$result" = "yes"; then
+    LIBS="$LIBS -lsocket"
+  fi
+fi
+
+dnl
+dnl Check if we need -lcrypt
+dnl
+AC_CHECK_LIB(crypt,crypt,result=yes,result=no)
+if test "$result" = "yes"; then
+  LIBS="$LIBS -lcrypt"
+fi
+
+dnl
+dnl The following weirdness is to support --disable as well as --enable
+dnl (but long live m4!)
+dnl 
+define(ARG_ENABLE_BETAFTPD,[
+AC_MSG_CHECKING(whether to enable $1)
+AC_ARG_ENABLE($1,[$2],result=yes,result=no)
+if test "$result" = "no"; then
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       $3
+fi
+AC_MSG_RESULT($enableval)
+])
+
+dnl
+dnl Check for options
+dnl
+ARG_ENABLE_BETAFTPD(xferlog,[  --enable-xferlog        Enable transfer log support],AC_DEFINE(WANT_XFERLOG))
+ARG_ENABLE_BETAFTPD(ascii,[  --enable-ascii          Enable ASCII mode support],AC_DEFINE(WANT_ASCII))
+ARG_ENABLE_BETAFTPD(fullscreen,[  --enable-fullscreen     Run in fullscreen mode (implies --disable-fork)],AC_DEFINE(WANT_FULLSCREEN))
+
+if test "$enableval" = "no"; then
+       ARG_ENABLE_BETAFTPD(fork,[  --enable-fork           Make the server fork into the background],AC_DEFINE(WANT_FORK))
+fi
+
+ARG_ENABLE_BETAFTPD(upload,[  --enable-upload         Enable upload support],AC_DEFINE(WANT_UPLOAD))
+ARG_ENABLE_BETAFTPD(stat,[  --enable-stat           Enable STAT command (see README first)],AC_DEFINE(WANT_STAT))
+ARG_ENABLE_BETAFTPD(dcache,[  --enable-dcache         Enable directory listing cache],AC_DEFINE(WANT_DCACHE))
+ARG_ENABLE_BETAFTPD(message,[  --enable-message        Enable .message files etc.],AC_DEFINE(WANT_MESSAGE))
+
+dnl
+dnl The following line _must_ be last...
+dnl
+ARG_ENABLE_BETAFTPD(shadow,[  --enable-shadow         Enable shadow password support],AC_DEFINE(WANT_SHADOW))
+
+nonroot_support=yes
+
+if test "$enableval" = "yes"; then
+       AC_CHECK_HEADERS(shadow.h)
+       nonroot_support=no
+else
+       dnl Causes unknown trouble on SunOS, so it's disabled
+       dnl if test -e "/etc/shadow"; then
+       dnl     AC_MSG_WARN()
+       dnl     AC_MSG_WARN([It looks like you're using shadow passwords, consider])
+       dnl     AC_MSG_WARN([giving the --enable-shadow flag to configure. Proceeding])
+       dnl     AC_MSG_WARN([_without_ support for shadow passwords -- you might not])
+       dnl     AC_MSG_WARN([be able to log in!])
+       dnl     AC_MSG_WARN()
+       dnl fi
+       echo
+fi
+
+if test "$nonroot_support" = "yes"; then
+       ARG_ENABLE_BETAFTPD(nonroot,[  --enable-nonroot        Do not need root access (implies --disable-shadow)],AC_DEFINE(WANT_NONROOT))
+else
+       enableval=no
+fi
+
+if test "$enableval" = "yes"; then
+       AC_MSG_WARN()
+        AC_MSG_WARN([Please read the README.nonroot file before using --enable-nonroot])
+       AC_MSG_WARN()
+else
+
+dnl
+dnl Try to find our effective userid (use our own C program?)
+dnl
+       AC_MSG_CHECKING(how to get effective uid)
+       if test -n "$EUID"; then
+               B_UID=$EUID
+               AC_MSG_RESULT([\$EUID ($EUID)])
+       elif test -n "$euid"; then
+               B_UID=$euid
+               AC_MSG_RESULT([\$euid ($euid)])
+       elif test -n "$UID"; then
+               B_UID=$UID
+               AC_MSG_RESULT([\$UID ($UID)])
+       elif test -n "$uid"; then
+               B_UID=$uid
+               AC_MSG_RESULT([\$uid ($uid)])
+       elif test "`whoami 2>/dev/null`" = "root"; then
+               B_UID=0
+               AC_MSG_RESULT([whoami (root)])
+       else
+               # assume we're not root
+               B_UID=1
+               AC_MSG_RESULT(not found, assuming not root)
+       fi
+
+       if test "$B_UID" -ne 0; then
+               AC_MSG_WARN()
+               AC_MSG_WARN([You do not seem to have root privilegies. If you])
+               AC_MSG_WARN([want to run BetaFTPD without being root, please])
+               AC_MSG_WARN([consider giving the --enable-nonroot flag to])
+               AC_MSG_WARN([configure, and read the README.nonroot file.])
+               AC_MSG_WARN()
+       fi
+fi
+
+AC_CHECK_TYPE(uid_t,int)
+AC_CHECK_TYPE(gid_t,int)
+
+AC_CHECK_HEADERS(crypt.h unistd.h time.h sys/time.h errno.h netinet/in.h)
+AC_CHECK_HEADERS(netinet/ip.h stropts.h sys/conf.h arpa/inet.h sys/filio.h)
+AC_CHECK_HEADERS(netinet/tcp.h sys/types.h netdb.h glob.h stdio.h)
+AC_CHECK_HEADERS(stdlib.h stdarg.h stdlib.h string.h strings.h fcntl.h)
+AC_CHECK_HEADERS(sys/ioctl.h sys/socket.h sys/stat.h sys/param.h signal.h)
+AC_CHECK_HEADERS(sys/signal.h dirent.h pwd.h grp.h netinet/in_systm.h)
+AC_CHECK_HEADERS(assert.h)
+
+AC_CHECK_HEADER(netinet/tcp.h,result=yes,result=no)
+
+dnl
+dnl linux/socket.h can create some problems on Debian GNU/Linux systems
+dnl
+
+if test "$result" = "yes"; then
+     AC_MSG_CHECKING(if netinet/tcp.h is enough)
+     AC_EGREP_CPP(yes,
+     [#include <netinet/tcp.h>
+#ifndef TCP_NODELAY
+      yes
+#endif
+     ], result=no, result=yes)
+     AC_MSG_RESULT($result)
+fi
+
+if test "$result" = "no"; then
+       AC_CHECK_HEADER(linux/socket.h)
+fi
+
+AC_CHECK_HEADERS(sys/poll.h)
+
+AC_MSG_CHECKING([for poll()])
+AC_TRY_COMPILE([
+#if HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+],[
+       struct pollfd fds[1];
+       fds[0].fd = 0;
+       fds[0].events = POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL;
+
+       poll(fds, 1, 1000);
+],enableval=yes,enableval=no)
+AC_MSG_RESULT($enableval)
+
+if test "$enableval" = "yes"; then
+       AC_DEFINE(HAVE_POLL, 1, [Define if you have poll().])
+fi
+
+dnl
+dnl sendfile() is not standard -- we'll have to check the different
+dnl versions one by one :-)
+dnl
+AC_CHECK_HEADER(sys/sendfile.h,result=yes,result=no)
+
+dnl
+dnl For some reason, we have to check it AGAIN... Otherwise it's never defined!
+dnl
+AC_CHECK_HEADERS(sys/sendfile.h)
+
+if test "$result" = "yes"; then
+       AC_MSG_CHECKING([for Linux sendfile()])
+       AC_TRY_COMPILE([
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_SYS_LIMITS_H
+#include <sys/limits.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif],[
+       int out_fd = 1, in_fd = 0;
+       off_t offset = 0;
+       size_t size = 1024;
+
+       sendfile(out_fd, in_fd, &offset, size);
+],enableval=yes,enableval=no)
+       AC_MSG_RESULT($enableval)
+
+       if test "$enableval" = "yes"; then
+               AC_DEFINE(HAVE_LINUX_SENDFILE, 1, [Define if you have sendfile() with the Linux semantics.])
+       fi
+fi
+
+AC_CHECK_HEADER(sys/uio.h,result=yes,result=no)
+if test "$result" = "yes"; then
+       AC_MSG_CHECKING([for BSD sendfile()])
+       AC_TRY_COMPILE([
+#if HAVE_SYS_LIMITS_H
+#include <sys/limits.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+],[
+        int in_fd = 0, out_sock = 3;
+        off_t offset;
+        size_t size = 1024;
+       struct sf_hdtr hdtr;
+
+       hdtr.hdr_cnt = 0;
+       hdtr.trl_cnt = 0;
+
+        sendfile(in_fd, out_sock, offset, size, &hdtr, &offset, 0);
+],enableval=yes,enableval=no)
+       AC_MSG_RESULT($enableval)
+
+       if test "$enableval" = "yes"; then
+               AC_DEFINE(HAVE_BSD_SENDFILE, 1, [Define if you have sendfile() with the BSD semantics.])
+       fi
+fi
+
+AC_OUTPUT(Makefile)
diff --git a/disp.c b/disp.c
new file mode 100644 (file)
index 0000000..406e2f9
--- /dev/null
+++ b/disp.c
@@ -0,0 +1,90 @@
+/*  disp.c: BetaFTPD full-screen stats
+    Copyright (C) 1999-2000 Steinar H. Gunderson
+
+    This program is is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License, version 2 if the
+    License as published by the Free Software Foundation.
+
+    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif 
+
+#include <ftpd.h>
+
+#if WANT_FULLSCREEN
+time_t last_update = 0;
+
+void update_display(const struct conn * const first_conn)
+{
+       struct conn *c = first_conn->next_conn;
+       int i = 0;
+       time_t now;
+
+       time(&now);
+       if (now - last_update < 1) return;
+       last_update = now; 
+
+       printf("%c[H", (char)27);        /* clear the screen */
+       printf("%c[44m                                                                         \n", (char)27);
+       printf("%c[44m                                                                         \n", (char)27);
+       printf("%c[38m                         BetaFTPD FTP server                             \n", (char)27);
+       printf("%c[44m                                                                         \n", (char)27);
+       printf("%c[44m                                                                         \n", (char)27);
+       printf("%c[40m", (char)27);
+       while (c != NULL && ++i < 20) {
+               const struct ftran * const f = c->transfer;
+               printf("%4u ", c->sock);
+               if (f == NULL) {
+                       printf("%-16s %-50s\n", c->username, c->last_cmd);
+               } else {
+                       char trunc_filename[256];
+                       strcpy(trunc_filename, f->filename);
+                       trunc_filename[22] = 0;
+
+#if WANT_UPLOAD
+                       if (f->upload) {
+                               printf("%-16s%-22s%12lu %7.2fkb/s (upl)\n", c->username,
+                               trunc_filename, f->size, (float)(f->pos - c->rest_pos) /
+                                       (float)(difftime(now, f->tran_start)) / 1024);
+                       } else
+#endif
+                       printf("%-16s%-22s%12lu %7.2fkb/s %.2f%%\n", c->username,
+                               trunc_filename, f->size, (float)(f->pos - c->rest_pos) /
+                                       (float)(difftime(now, f->tran_start)) / 1024,
+                               (float)(f->pos) / (float)(f->size) * 100.0f);
+               }
+               c = c->next_conn;
+       }
+       printf("%79s", "");
+}
+#endif
diff --git a/doc/CHANGES b/doc/CHANGES
new file mode 100644 (file)
index 0000000..8057df5
--- /dev/null
@@ -0,0 +1,160 @@
+Version 0.0.8 (the Try-to-do-way-too-much-in-one-release Release, originally the
+Portability Release)
+
+0.0.8 has been through a lot of preview/beta releases, and is a _totally_
+different beast from 0.0.7. As a consequence, there have been many minor
+changes from release to release, which are uninteresting in the big picture
+and thus has been removed from the list. For a complete list, see the file
+CHANGES-0.0.8.
+
+- Support for not running as root. This implies using its own database of
+  rights and users. Give --enable-nonroot to configure to enable it. Users
+  running BetaFTPD as root are not affected by this support in any way.
+  BetaFTPD is still small with this feature, expect about 1 kB (6%) extra
+  for database routines and hooks in the main code. 
+
+  This is still very experimental. Do not expect this to be better until
+  _at least_ 0.0.9.
+- Fixed a LOT of bugs, some segfaulting, some minor.
+- MAJOR performance improvements in almost all areas. One of the main
+  contributors in this area has been Dan Kegel (see the CREDITS file).
+- Some changes to compile better under Debian GNU/Linux. A .deb-file was
+  ready, but due to lost access of a Debian-system, this was discontinued :-(
+- BetaFTPD will now try to install itself (wow! (not)) in the same directory
+  as the file called `ftpd', if you have that file.
+- Support for non-Linux platforms! For now, only SunOS/SVR4 and FreeBSD.
+  SunOS just barely works, and FreeBSD lacks sendfile() support. Both are 
+  generally untested.
+- Gave the version its own name, like Samba does :-)
+- If you have mmap(), BetaFTPD will no longer generate temporary files for directory
+  listings, but generate them in-place in memory.
+- Completely restructured the internal listing functions, to make them more
+  standards-compliant and smaller. (-l and -F flags now supported, but not
+  -R.)
+- Make an EXIT command (must be manually enabled -- NEVER use it on a
+  production machine, ANYONE will be able to halt your server!) to stop
+  the server -- useful for profiling, which requires an exit() to generate
+  profile data. (Be aware that gmon.out will be where the server last did
+  a chdir() to, though. In a debugging server, you'll know where that is
+  anyway...)
+- strip-exec now has a warning. In addition, it gives -D__OPTIMIZE_SIZE__
+  to keep glibc2 (libc6) from adding some inlines. It also kills
+  -fomit-frame-pointer. So now strip-exec gives a 20 kB executable instead
+  of 21 kB (one extra kB goes to new functionality) :-)
+- Now strips out any non-ASCII chars from the commands (not any parameters),
+  to fix the ABOR problems.
+- Documentation changes. 
+- Commented all the functions in cmds.c and ftpd.c, to make it easier to get
+  into the code.
+- Implemented the APPE, DELE, MKD, RMD, ABOR, REIN, ACCT, HELP and STAT commands.
+- Added makefile dependencies.
+- BetaFTPD can now handle non-DES crypt()s, thanks to Beau Kuiper
+  <ekuiperba@cc.curtin.edu.au>.
+- ASCII mode added, once again thanks to Beau Kuiper <ekuiperba@cc.curtin.edu.au>.
+- Linux sendfile() support (only for mmap()-able machines -- I don't think
+  any machine has sendfile() but not mmap()). Other systems will be added
+  later.
+- Moved all documentation (except README) into a separate doc/ directory. Added
+  CREDITS file.
+- The internal ls now can cache the listings (--enable-dcache to configure).
+  Please see the README file for some problems that still exist. This
+  option requires quite a bit of code (1028 bytes compiled, on my
+  system), and some RAM for the directory listings and support structure.
+  I think, however, that the increased performance is worth it.
+- BetaFTPD can now use poll() if you have it. This removes the old 1024-
+  socket limit, as well as reducing the code size by 496 bytes (on my
+  system).
+- Added renaming support. (Whee! Almost full RFC1123 compliance!)
+- Changed all copyright strings to say `1999-2000' (well, it only got to
+  May 2000 before I remembered it...) instead of just `1999'.
+- A new option, --enable-message, makes the server show info about README files,
+  as well as printing any .message or welcome.msg files that may be available.
+  (This is a quite standard function in other ftpds.)
+- Made a simple FTP server test suite (not included, since it's really not good 
+  enough to live its own life as a standalone product), used in testing.
+- BetaFTPD now listens on port 21.
+
+Version 0.0.7:
+- Shadow support. (Give --enable-shadow to configure to enable it.)
+- No longer ships with precompiled binary -- there are way too many
+  configurations to take care of now (shadow, fork, etc.).
+- Cleans up a connection if no file transfer has been done in 15 minutes.
+- More checks in configure script.
+- Made README file a bit nicer.
+
+Version 0.0.6:
+- Fixed PASV bug.
+- Added upload support! (Give --enable-upload to configure to enable it.)
+  Not 100% tried and tested yet, and requires extra code a lot of places.
+  Amazingly enough, it only gives 700 bytes (uncompressed) extra executable
+  size, and requires about 4 bytes per new connection.
+- Fixed cosmetic bug in configure script.
+- All .c files now depend on config.h.
+- Cleaned up some minor problems in building in a directory different from
+  the source directory.
+- Added metalab as a mirror site. (The official one is still at my homepage,
+  but I can't offer FTP from there.)
+- Added LSM entry.
+- Removed bug that prevented the user from sending _anything_ (including
+  a re-login) if he/she hadn't opened with USER/PASS.
+- Fixed some problems with compiling on mmap-less machines.
+
+Version 0.0.5:
+- Fixed problem in tarfile (aargh!), it should (forever) unpack into its own
+  directory now.
+- Made handler_table[] const and static (that means minor advantages in speed
+  and executable size).
+- Added autoconf support. Still not perfect, but much better than nothing.
+- Re-added support for mmap-less machines as well.
+- Now only licensed under GPL v2 (_not_ any later or earlier version).
+
+Version 0.0.4:
+- Fixed problem where Netscape 4.5 would give double slashes after directory
+  names.
+- Specifying a non-existant user would make the server segfault ;-)
+- Implemented non-RFC MDTM command.
+- Changed prompt for anonymous users to "Login OK, send password (your e-mail)."
+  (added 'your' so people won't begin to send their mail passwords :-)
+- Fixed a bug where anybody could send a fake user name, and access to a
+  random uid. (Don't worry, it was unexploitable -- the server crashed due
+  to another bug...)
+- Made the command prototypes in cmds.h a single macro, to reduce source size
+  and improve readability (?).
+- KNOWN-BUGS file added.
+- Fixed a problem where the server could segfault (due to writing to free()'d
+  memory).
+- Stripped some sections from the precompiled binary, to reduce size a little
+  more... (Added a script named `strip-exec' which does just that.)
+- Fixed a problem where doing a `CWD /' followed by a `PWD' would raise some
+  error messages.
+- Fixed a _possible_ (I couldn't exploit it) problem if a REST was sent,
+  greater than the size of the file downloaded, and libc would have some
+  kind of problem with negative sizes.
+- Added some #define's in ftp.h for those who don't need xferlogs and/or
+  fullscreen. The precompiled binary is supposed to be _small_, so it
+  disables both.
+- Removed all alignment from the precompiled binary (hooray! we're back
+  at 15k!)
+- Compressed precompiled binary with UPX (10k isn't bad...)
+- Fixed some bugs, problems with NcFTP etc. (This file got lost, and I
+  had to restore from a slightly older backup.)
+- Started on an RFC-COMPLIANCE file.
+- Support for nice, full-screen display (not in precompiled binary).
+- Now uses mmap() instead of read() and write().
+- Lots of _real_ testing, in a production environment! That sure fixed
+  a lot of problems :-)
+
+Version 0.0.3:
+- CHANGES file added...
+- Fixed a bug that prevented lynx and IE (?) from getting dir listings.
+- Added VERY crude logging (only to /var/log/xferlog for now). It fakes
+  some of the traditional xferlog fields. (Note: this alone was almost
+  1k)
+- Accepts "ftp" as well as "anonymous".
+- NLST command partly implemented (errr...).
+- Cleaned up some error numbers, shortened some messages.
+- Added pseudo-commands MODE and STRU.
+- Can recognize other home dirs than /home/ftp/ for anonymous users.
+- Made FTP port a #define in ftpd.h.
+- Ignores information on stdin (libc workaround).
+- Globbing support! (ie. you can do things like LIST *.dat)
diff --git a/doc/CHANGES-0.0.8 b/doc/CHANGES-0.0.8
new file mode 100644 (file)
index 0000000..4820144
--- /dev/null
@@ -0,0 +1,211 @@
+Complete list of all changes from 0.0.7 to 0.0.8, encompassing 17 prerelases:
+
+- Support for not running as root. This implies using its own database of
+  rights and users. Give --enable-nonroot to configure to enable it. Users
+  running BetaFTPD as root are not affected by this support in any way.
+  BetaFTPD is still small with this feature, expect about 1 kB (6%) extra
+  for database routines and hooks in the main code. 
+
+  This is still very experimental. Do not expect this to be better until
+  _at least_ 0.0.9.
+- Fixed a segfaulting bug. (Freeing a malloc'ed pointer that had been
+  incremented obviously wasn't a very good idea...)
+- Some autoconf checks (fixes) for libc5.
+- Now shows the sticky bit in directory listings.
+- Cleaned up error cleanup :-)
+- Some changes to compile better under Debian GNU/Linux. A .deb-file was
+  ready, but due to lost access of a Debian-system, this was discontinued :-(
+- BetaFTPD will now try to install itself (wow! (not)) in the same directory
+  as the file called `ftpd', if you have that file.
+- A little better checking for uid (at configure time).
+- Support for non-Linux platforms! (For now, SunOS 5.5/SVR4 only. If this
+  is name confusion, sorry, but the machine belongs to the University
+  of Oslo, where I don't study, but still have a shell account...) These
+  changes should help out a _lot_ when it comes to general portability.
+  (Every single system header file BetaFTPD uses is now checked for. Didn't
+  have stdio.h? I didn't guess so either... :-) I could probably have used
+  AC_HEADER_STDC, but I decided to be on the safe side.)
+
+  Still, sadly, only Linux is really supported and working.
+- Added README.platforms file.
+- Gave the version its own name, like Samba does :-)
+- If you have mmap() (it is not strictly needed, but a _lot_ of code
+  needed was only in place if you used mmap, and I decided to leave it
+  that way), BetaFTPD will no longer generate temporary files for directory
+  listings, but generate them in-place in memory.
+- Completely restructured the internal listing functions, to make them more
+  standards-compliant and smaller. (-l and -F flags now supported, but not
+  -R.)
+- Implemented the APPE command, on user request.
+- Minor speedups (and I do mean minor).
+- The Makefile now uses CFLAGS in linking as well.
+- Make an EXIT command (must be manually enabled -- NEVER use it on a
+  production machine, ANYONE will be able to halt your server!) to stop
+  the server -- useful for profiling, which requires an exit() to generate
+  profile data. (Be aware that gmon.out will be where the server last did
+  a chdir() to, though. In a debugging server, you'll know where that is
+  anyway...)
+- Fixed a segfaulting bug in handling select() errors. The most common case
+  for this was at loads around 30-40 megabytes/sec :-)
+- Fullscreen mode now looks OK for those with more than 80 columns too.
+- Fixed some -pedantic warnings.
+- Made mwrite() a function instead of just a #define (well, it's still a
+  define, but code is now shared), to save 30-40 bytes (and perhaps a
+  tiny bit of speed as well) :-)
+- strip-exec now has a warning. In addition, it gives -D__OPTIMIZE_SIZE__
+  to keep glibc2 (libc6) from adding some inlines. It also kills
+  -fomit-frame-pointer. So now strip-exec gives a 20 kB executable instead
+  of 21 kB (one extra kB goes to new functionality) :-)
+- Now strips out any non-ASCII chars from the commands (not any parameters),
+  to fix the ABOR problems.
+- Documentation changes. 
+- MDTM and SIZE commands do no longer need read access (this is one of the
+  benefits of some modularization in the code). This is more in line with
+  other FTP servers' behaviour.
+- Commented all the functions in cmds.c and ftpd.c, to make it easier to get
+  into the code.
+- Implemented the DELE, MKD, RMD, ABOR, REIN, ACCT, HELP and STAT commands. Note
+  that ABOR was previously noted as `fully RFC compliant', but due to an
+  error, it was not included until now.
+- Added makefile dependencies.
+- `make clean' and `make distclean' now also cleans up after `strip-exec'.
+- NOOP can now be executed without the user being logged in.
+- Fixed an autoconf bug, where --enable-shadow could sometimes be mistaken
+  for --enable-nonroot.
+- Fixed a stupid performance bug, which hit in on systems with slightly
+  lower MTUs than 1500 (actually some with 1500 as well) -- I was telling
+  the kernel _not_ to delay, and thus ended up sending out 12-byte packets
+  and other stupidness. BetaFTPD now uses TCP_CORK when available (read:
+  Linux 2.2.x and up, perhaps some 2.1.x kernels as well), which actually
+  makes it _better_ than wu-ftpd and most other implementations out there
+  (I believe those who use sendfile() also get this done right). BIG thanks
+  to Alan Cox for informing me of this flag, and to the rest of the
+  linux-kernel mailing list for being generally helpful.
+- Fixed some bugs in getting the filesystem block size: Any value over 4096
+  would have segfaulted (rather unlikely on most systems). Any blocksize
+  over 4096 is now simply set to 4096. This value is customizable, by
+  changing MAX_BLOCK_SIZE in ftpd.h.
+- accept() error handling should be more persistent now, before trying to
+  recreate the server socket.
+- server_sock is now non-blocking, just in case.
+- Changes (and ideas for changes) by Beau Kuiper <ekuiperba@cc.curtin.edu.au>:
+  Fixed an overflow bug (I'm unsure if this was exploitable or not), BetaFTPD
+  can now handle non-DES crypt()s and a minor optimization was made.
+- Fixed a bug where listing would fail after uploading.
+- A minor fix for fullscreen mode.
+- non-DES crypt() is actually _working_ now (I hope)...
+- Memory savings (and slight performance improvements) for non-fullscreen mode
+  (thanks to Ryan Cumming <bodnar42@bodnar42.dhs.org>).
+- ASCII mode added, once again thanks to Beau Kuiper <ekuiperba@cc.curtin.edu.au>.
+- SIZE is now disabled for ASCII mode, as the standards say we can't do an
+  estimate.
+- Fixes for 64-bit platforms.
+- A patch for authentication, saving a few bytes per connection and removing crypt()
+  DES/non-DES problems for ever, by Beau Kuiper (I think you've got his e-mail
+  address by now...).
+- More ftpext compliant REST code, together with some duplicate code removal.
+- BetaFTPD no longer crashes if /usr/adm doesn't exist, and xferlog support
+  is enabled. Also, it will try /var/log before /usr/adm.
+- Added CREDITS file.
+- The entire do_download function was rewritten, which resulted in a much
+  smaller and simpler function. In the process, some bugs were also fixed.
+- Fixed a bug, where nonroot users couldn't use PORT...
+- Added a debugging version of the TRAP_ERROR macro.
+- SO_LINGER is no longer set at all. This removes a major performance problem.
+- fd -2 is no longer attempted closed.
+- Sockets are now only checked for timeouts every minute (at most), which
+  can reduce spent CPU time a lot (with 200 clients connected, it used to
+  need 70-80% of the total CPU time for BetaFTPD, now it is close to zero).
+- Closes fd 0 on startup.
+- Now doesn't hang if fd 0 is assigned to server_sock (aaaaaaaargh).
+- Linux sendfile() support (only for mmap()-able machines -- I don't think
+  any machine has sendfile() but not mmap()). Other systems will be added
+  later.
+- More FreeBSD fixes. sendfile() _soon_ works on FreeBSD :-)
+- Nonroot fixes.
+- The reading of the control connection is now done with one syscall (recv)
+  instead of three (recv, ioctl, recv).
+- More do_openfile() fixes, for lynx.
+- The PASV socket is now set in listening mode _before_ announcing its
+  address and port number.
+- Moved all documentation (except README) into a separate doc/ directory.
+- The internal lister now prints years more according to POSIX (dates older
+  than 6 months or in the future are printed with years instead of
+  timestamps -- IMHO this is unneccessary, but it's nice to do the same
+  thing as GNU ls).
+- ascii.h is now included (you could still compile ASCII support without
+  it, it just fixes some warnings) in the distribution.
+- The internal ls now can cache the listings (--enable-dcache to configure).
+  Please see the README file for some problems that still exist. This
+  option requires quite a bit of code (1028 bytes compiled, on my
+  system), and some RAM for the directory listings and support structure.
+  I think, however, that the increased performance is worth it.
+- The 64-bit check is dropped in favour of a simple truncate. Sorry, you
+  can't list files >2GB correctly anymore ;-)
+- BetaFTPD can now use poll() if you have it. This removes the old 1024-
+  socket limit, as well as reducing the code size by 496 bytes (on my
+  system).
+- Many bugfixes and cleanups, by Dan Kegel (see CREDITS).
+- Internal changes -- no more ugly `late freeing'.
+- If you don't have list access and try to list, `permission denied' is
+  printed instead of the previous error message being repeated. (This is
+  actually a workaround for glob()'s weird error handling.)
+- Uploading no longer gives weird permissions on the uploaded files.
+- Listing a symbolic link now gives the attributes (especially size) of the
+  file itself, not the link. This is /bin/ls-incompatible, but can be useful
+  anyways, especially when it comes to anonymous FTP. (I can't think of any
+  real-life situations where it would break anything either.)
+- You can no longer give a negative number to REST. (Thanks to Marten
+  <martbo@dds.nl> for bringing this bug to my attention.)
+- Fixed a possible bug in the internal listing code, triggered only if listing
+  a file with a 16-character username.
+- If a file in a directory can't be stat()'ed, the internal ls will continue
+  instead of just returning.
+- #ifdef'ed out some recursion-specific code, since the internal ls doesn't
+  support recursion as a whole.
+- Added renaming support. (Whee! Almost full RFC1123 compliance!)
+- Changed all copyright strings to say `1999-2000' (well, it only got to
+  May 2000 before I remembered it...) instead of just `1999'.
+- Changed the timeout value from a hardcoded number into a #define'd constant
+  in ftpd.h.
+- Even more recursive-listing code-outcommenting...
+- Much of the linked list code is now reused, which saves 60-70 bytes and
+  increases code simplicity. A very positive side effect is that the dcache now
+  uses the same, bug-fixed linked list code as connections and file transfers.
+- Fixed a few warnings in the configure program, and some in BetaFTPD itself.
+- The poll() version now also closes standard input on startup (previously only the
+  select() version did).
+- The code for accepting new clients has been moved into its own function.
+- A new option, --enable-message, makes the server show info about README files,
+  as well as printing any .message or welcome.msg files that may be available.
+  (This is a quite standard function in other ftpds.)
+- Fixed a bug that made shadow passwords not working at all (AGAIN!). Thanks
+  to Thor Bernhardsen <oscar@comports.com> for notifying me of this bug. 
+- The server didn't clean up connections properly on all errors -- fixed. This
+  led to all sorts of problems. Thanks to Andre Luiz dos Santos
+  <andre@netvision.com.br> for bringing this bug (and others, in fact, most of
+  those fixed in 0.0.8pre14 and 0.0.8pre15) to my attention.
+- Fixed a compilation bug on machines without GLOB_NOMATCH defined.
+- Fixed more ABOR bugs.
+- Made a simple FTP server test suite (not included, since it's really not good 
+  enough to live its own life as a standalone product), used in testing.
+- Fixed a bug where multiple successful PORTs or PASVs would leave stale, unused
+  file descriptors that would never be cleaned up. (Well, previously multiple
+  PORTs wouldn't work at all -- again, thanks to Andre for mentioning this.)
+- Sometimes, when using poll(), the highest-numbered fd would not be checked at
+  all, leaving a client just waiting for a reply.
+- More RFC-compliant MKD handling.
+- Removed a non-used variable (port_allocated) from ftpd.h. Also fixed some code
+  in cmd_pass() that could overwrite the last byte in that variable.
+- Fixed a fatal PORT bug (I have no clue at all how this could have gone unnoticed --
+  that bug must have been there since 0.0.1, but probably not exposed until now
+  due to other smaller bugs (?)).
+- BetaFTPD writes only to the logfile if it already exists now -- this is so you
+  can use --enable-xferlog and not create an xferlog file until you really need
+  logging. Thanks to kromJx <kromJx@crosswinds.net> for the idea.
+- BetaFTPD now listens on port 21.
+- Uploading mode made quicker, by eliminating a syscall. Uploading should be
+  optimal with respect to syscalls now (only a poll/select, a recv and a write
+  per loop iteration). This also reduces the code size by a few bytes.
+- More improvements to upload code. Data received in an upload now goes to a
+  static buffer (which also has been shrunk) instead of to the stack.
diff --git a/doc/COPYING b/doc/COPYING
new file mode 100644 (file)
index 0000000..e77696a
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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 of the License, 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/doc/CREDITS b/doc/CREDITS
new file mode 100644 (file)
index 0000000..dc0a7dd
--- /dev/null
@@ -0,0 +1,29 @@
+Steinar H. Gunderson <sgunderson@bigfoot.com>
+- Wrote most of the code and documentation.
+
+Beau Kuiper <ekuiperba@cc.curtin.edu.au> (author of muddleftpd)
+- Fixes of all sorts, and ASCII mode support.
+
+Ryan Cumming <bodnar42@bodnar42.dhs.org>
+- Minor performance and memory improvements.
+
+Gianmarco Giovannelli <gmarco@giovannelli.it>
+- Provided access to a FreeBSD system, meaning that such systems
+  will be supported in the future.
+
+Justin Hammond <fish@dynam.ac>
+- Leader of the DynamX project, for which I originally wrote the
+  single-threaded core which later evolved into BetaFTPD. DynamX
+  does not use any of my code at the moment, but the mailing list
+  was very helpful in improving my code and at that time.
+
+Dan Kegel <dank@alumni.caltech.edu>
+- Wrote dkftpbench, an FTP benchmarking server program, which in
+  itself helped me fix a lot of performance bugs. He's also been
+  very helpful in tracking down and eliminating these bugs, as
+  well as giving me general hints about stupid code in BetaFTPD.
+
+In addition, there are numerous users who by themselves may have
+done very little, but together have been the driving force behind
+the BetaFTPD development. To all of you who have ever sent in
+feedback of some sort: Thank you.
diff --git a/doc/KNOWN-BUGS b/doc/KNOWN-BUGS
new file mode 100644 (file)
index 0000000..5246eff
--- /dev/null
@@ -0,0 +1,32 @@
+List of known bugs and problems:
+- Some commands are still not implemented (see the file RFC-COMPLIANCE). There
+  are very few I actually miss now... None, actually.
+- There are several hardcoded limits, instead of using constants such as
+  PATH_MAX.
+- We should move the remaining bugs listed from the source (in the comments)
+  to this file ;-)
+- If a command that requires a parameter doesn't have one, BetaFTPD will send
+  500 instead of 501. This behaviour is very useful in the command lookup
+  (for code simplicity) :-)
+- We could (perhaps) use a hash table instead of a straight linear command search?
+- REST does not work in ASCII mode. (This is partly as a security measure --
+  enabling resuming on ASCII files could need a lot of CPU power if we had to
+  resume in the middle of a big file, and still stay RFC/ftpext-compliant.) 
+- In general, nonroot support has problems. For instance, security is generally
+  weak, and anonymous FTP is not supported (all users will need a password).
+  Nonroot is not even worked on at the moment. Don't use it :-)
+- Occasionally, a directory listing may cause unwanted output in the fullscreen
+  mode, for some weird reason.
+- There are reports of the server not working at all in fullscreen mode. We're
+  working to find and fix these problems.
+- Some features like ftpshut and ftpaccess files (yes, root can ftp :-( ) are
+  missing.
+
+Obscure bugs I haven't seen in a long time (probably fixed):
+- The server is still known to segfault from time to time, but unfortunately,
+  most kernel versions won't dump core if the program has changed uid :-(
+- Sometimes (after an error) the server doesn't respond to the connection
+  anymore. Weird.
+- Some times, for no reason at all, mmap() on my machine will fail with a
+  EINPROGRESS, which is a non-legal error code. I'm not sure if this is
+  a bug in BetaFTPD or glibc :-)
diff --git a/doc/README.nonroot b/doc/README.nonroot
new file mode 100644 (file)
index 0000000..42e232f
--- /dev/null
@@ -0,0 +1,60 @@
+Experimental support for running BetaFTPD without being root (0.0.8)
+
+Note: THIS IS CODE IN TESTING, AND SHOULD NOT BE CONSIDERED A STABLE
+PART OF BETAFTPD IN ANY WAY. (Sorry for the shouting, it was just
+in case you were deaf. Well, now you are anyway...)
+
+BetaFTPD now has support (ahem) for running without root privilegies.
+When not running as root, you can't piggyback on the OS anymore, and
+have to employ some extra sort of database. More on this later. 
+
+This is currently just a hack, with a lot of stuff missing, and lots of
+debugging messages still being in. The reason I want to release it in
+this verson, is to get feedback from any users (hello! where are
+you?) :-)
+
+There are several things you must do to get nonroot support working:
+1. configure BetaFTPD with `--enable-nonroot'.
+2. Define FTP_PORT in ftpd.h to a port over 1024. The default setting
+   for nonroot users is 12121, so make sure it is available, or choose
+   another one. Be aware that anybody may pinch this port if you're
+   not holding on to it...
+3. Set up a `betaftpd.users' file in GLOBAL_ROOT_DIR, with the following
+   format:
+
+uid    username        password        homedir         rootdir         gid(s)
+
+uid:           The user's uid (note that these fields do NOT have to match those
+               in /etc/passwd, you're your own boss here).
+username:      The user's username, of course.
+password:      The password, encrypted with crypt().
+homedir:       The directory the user starts in. MUST BE UNDER ROOTDIR.
+rootdir:       The directory the user sees as /. (Note that homedir and
+               rootdir is seen from a chroot()ed perspective. So if
+               you had set GLOBAL_ROOT_DIR to be `/betaftpd' and wanted
+               a user to have the home directory `/betaftpd/foo/', you
+               would use just `/foo/'.)
+
+               Both directory names must be ended with a slash (/).
+gids:          Any groups the user is member of, separated with a space.
+               Should be at least one.
+
+Note: No blank lines or comments allowed. The fields are in a bit unusual
+order. This is to reduce parsing overhead.
+
+4. Set up a `betaftpd.groups' file in GLOBAL_ROOT_DIR, with the following
+   format:
+
+gid    groupname
+
+These fields should be self-explanatory.
+
+5. Set up a `.rights' file in every directory you want to be accessible.
+   More details are in the file `README.rights'.
+
+I hope to add a GUI for all this one day, but for now it is more important
+for me to concentrate on the core development of BetaFTPD.
+
+Good luck!
+
+/* Steinar */
diff --git a/doc/README.platforms b/doc/README.platforms
new file mode 100644 (file)
index 0000000..97ece80
--- /dev/null
@@ -0,0 +1,44 @@
+These are platforms that BetaFTPD is known to run on (`Supported' means
+that each new major version will be tested on this platform):
+
+Linux, libc5
+------------
+Slackware 3.5. Compiles, partly tested. Supported.
+
+Linux, libc6 (i*86-pc-linux-gnu)
+--------------------------------
+Slackware 3.4 (upgraded to glibc 2.1) and Debian GNU/Linux 2.1 (glibc 2.0).
+Debian is previously tested and known to work well, but since I had to
+move the computer away, I can't test it anymore. It should work, though.
+Tested and working on Slackware (my main system). Should work fine on most
+Linux distributions.
+
+SunOS 5.5 (sparc-sun-solaris2.5.1)
+----------------------------------
+Compiles, runs, not very much testing.
+
+FreeBSD
+-------
+Compiles, runs, not very much testing. A user reports that it actually works
+well. (No sendfile() support, though.)
+
+In other words, as of today BetaFTPD works properly on Linux ONLY... If you
+feel an urge to change this, please do step forward and offer to port it. I've
+got access to both FreeBSD and SunOS-machines (thanks to very friendly people
+who've given me shell access), but due to Internet time restrictions and general
+lack of time, I really can't take care of all other patforms.
+
+All others
+----------
+Totally unknown. If you want to help out, here are some things that will
+help (in descending order):
+
+1. That you can take responsibility for adjusting each new version of
+   BetaFTPD to your OS.
+2. If #1 isn't possible, just a note that it works (if it works!) or
+   patches to make the latest version work. (Note that this is now preferred
+   over #3 -- it wasn't so earlier.)
+3. If #2 isn't possible, that I can get (permanent; I'll probably need to
+   check later versions...) shell access to a computer with that OS.
+   Optionally, you could always send me a fresh HD with that OS installed :-)
+
diff --git a/doc/README.rights b/doc/README.rights
new file mode 100644 (file)
index 0000000..6cb573a
--- /dev/null
@@ -0,0 +1,20 @@
+The `.rights' file is laid out as follows:
+
+privilegied.file        rw-r-----       0       1
+(filename)             (rights)        (uid)   (gid)
+
+For any file not in the list, the special case `.default' is checked. If there
+is no such file, _no access is permitted_, and the file will _not show up in
+directory listings_. This is in fact handy in most cases, so be careful with
+adding a `.default' entry. (Note that `.rights' is never influenced by a
+`.default' entry, for security.)
+
+For directory permissions, the file `dir/.rights' is checked for the entry `.'.
+Yes, I know, treating them like normal directories would be great, but
+remember that there is a root directory as well...
+
+The rights are standard r, w and x for now, no setuid, setgid or sticky bit
+unless we really need it. (We could perhaps need the sticky bit later.) Be
+careful with the format of the `.rights' file, as everything you set in the
+rights column will be copied directly to listings. Restrict yourself to
+those 9 characters, no more, no less, and only use r, w, and x.
diff --git a/doc/RFC-COMPLIANCE b/doc/RFC-COMPLIANCE
new file mode 100644 (file)
index 0000000..0884e59
--- /dev/null
@@ -0,0 +1,61 @@
+BetaFTPD does not fully meet the RFC959 minimum requirements for an FTP
+server. However, for all practical uses, it should be considered a legal
+implementation of the FTP protocol, and very close to being fully compliant
+with RFC959.
+
+BetaFTPD is not RFC1123 compliant, but now that renaming is in place, the
+only thing that is left (I think) before it is, would be refusing Telnet
+commands. I'm not sure if I will ever do this -- I simply don't see that
+this could be a problem in today's FTP world.
+
+These commands are believed to be fully compliant with RFC959 and RFC1123:
+PORT, PASV, USER, PASS, CWD, CDUP, QUIT, DELE, PWD, SYST, NOOP, STOR, APPE,
+ABOR, RNFR, RNTO, MKD, RMD, ALLO, REIN, ACCT, HELP and STAT.
+
+These commands are not implemented at all: SMNT, STOU and SITE.
+
+The rest of the commands are implemented, but have minor quirks or
+problems -- see below.
+
+Telnet signals are ignored, to the best of my knowledge. BetaFTPD does not
+speak Telnet, although RFC959 seems to require it. Note that you can still
+use Telnet to connect to the FTP port (to do a manual debugging session,
+e.a.) and speak raw FTP, but BetaFTPD does not follow _all_ the rules about,
+say, Telnet IP and Synch signals.
+
+TYPE:
+The TYPE command is included, but it ignores its argument and always uses
+binary mode. RFC959 violation, but RFC1123 excuses the missing ASCII mode.
+(If you enable ASCII mode (--enable-ascii to configure), it's RFC959- and
+RFC1123-compliant.)
+
+STRU:
+The STRU command is included, but it ignores its argument and always uses
+file structure (if you really need record structure, mail me; when I'm done
+laughing, I will consider implementing it). RFC959 violation, but RFC1123
+excuses the missing record structure.
+
+MODE:
+The MODE command is included, but it ignores its argument and always uses
+stream mode (the other two are never used anyway). RFC959 violation, it
+requires all other modes to be refused. 
+
+RETR:
+The RETR command is believed to be compliant with RFC959. (There is no default
+data port, though -- I'm unsure about this.)
+
+LIST/NLST:
+The LIST and NLST commands ignore some flags (like `-R') given to them. (This
+is much better than it was before, though.) The RFCs say nothing about
+directory listing formats anyway, but I guess this is a violation of GNU ls :-)
+
+REST:
+The REST command is implemented, but it doesn't check that its argument really
+is an integer. REST does not work in ASCII mode.
+
+SIZE:
+The SIZE command (not in any RFC at the moment) does not work in ASCII mode.
+This is in compliance with the ftpext documents. If we were to enable it in
+ASCII mode, a possible attacker could just enable ASCII mode, do a SIZE on a
+big file and thus effectively halt a server for a shorter or longer period of
+time.
diff --git a/ftpd.c b/ftpd.c
new file mode 100644 (file)
index 0000000..473624e
--- /dev/null
+++ b/ftpd.c
@@ -0,0 +1,1610 @@
+/*  ftpd.c: BetaFTPD main
+    Copyright (C) 1999-2000 Steinar H. Gunderson
+
+    This program is is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License, version 2 if the
+    License as published by the Free Software Foundation.
+
+    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * Special note: this file has been overwritten by another (0-byte) file, been
+ * through the dead, and restored (with the help of dd, grep, gpm, vi and less)
+ * with a sucess rate of 99.9%. Show it a little respect -- don't add junk
+ * to it. :-)
+ */
+
+#define _GNU_SOURCE
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#if HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+
+#if HAVE_SYS_CONF_H
+#include <sys/conf.h>
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#if HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#if HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#if HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#if HAVE_LINUX_SOCKET_H
+#include <linux/socket.h>
+#endif
+
+#if HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#if HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#if HAVE_GLOB_H
+#include <glob.h>
+#endif
+
+#if HAVE_SYS_SIGNAL_H
+#include <sys/signal.h>
+#endif
+
+#if HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+
+#if HAVE_SYS_SENDFILE_H
+#include <sys/sendfile.h>
+#endif
+
+/*
+ * <linux/socket.h> does not export this to glibc2 systems, and it isn't
+ * always defined anywhere else.
+ */
+#if !defined(TCP_CORK) && defined(__linux__)
+#define TCP_CORK 3
+#endif
+
+#include <ftpd.h>
+#include <cmds.h>
+
+#if WANT_ASCII
+#include <ascii.h>
+#endif
+
+/* Debug printing -- remove before 0.0.8 final */
+#if 1
+#define DPRINT(s)
+#else
+#define DPRINT(s) printf s 
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED -1
+#endif
+
+struct conn *first_conn = NULL;
+struct ftran *first_ftran = NULL;
+#if WANT_DCACHE
+struct dcache *first_dcache = NULL;
+#endif
+
+#if HAVE_POLL
+unsigned int highest_fds = 0;
+
+#define FD_MAX 1024
+#define fds_send fds
+struct pollfd fds[FD_MAX];
+
+#define MAXCLIENTS FD_MAX
+#else
+fd_set master_fds, master_send_fds;
+#define MAXCLIENTS FD_SETSIZE
+#endif
+
+#if WANT_XFERLOG
+FILE *xferlog = NULL;
+#endif
+
+/*
+ * This variable specifies if it's soon time to check for timed out
+ * clients, and timed out directory listing cache entries. It is
+ * set to 1 by a signal handler every minute, and set to 0 when the
+ * checking has been performed.
+ */
+int time_to_check = 1;
+
+#ifndef HAVE_SPRINTF
+/*
+ * snprintf(): snprintf() replacement for systems that miss it. Note
+ *             that this implementation does _not_ necessarily protect
+ *             against all buffer overflows. Get a real snprintf() in
+ *             your C library. That being said, the 8k limit is
+ *             substantially larger than any other string in BetaFTPD,
+ *             which should make such an attack harder.
+ */
+int snprintf(char *str, size_t n, const char *format, ...)
+{
+       char buf[8192];
+       va_list args;
+       int err;
+
+       va_start(args, format);
+       err = vsprintf(buf, format, args);
+       va_end(args);
+
+       buf[(int)n] = 0;
+       strcpy(str, buf);
+
+       return err;
+}
+#endif
+
+#ifndef HAVE_VSNPRINTF
+/*
+ * vsnprintf:  vsnprintf() replacement for systems that miss it. Please
+ *             see snprintf (above) for more information.
+ */
+int vsnprintf(char *str, size_t n, const char *format, va_list ap)
+{
+       char buf[8192];
+       int err;
+
+       err = vsprintf(buf, format, ap);
+       buf[(int)n] = 0;
+       strcpy(str, buf);
+       return err;
+}
+#endif
+
+/*
+ * add_fd():   Add an fd to the set we monitor. Return 0 on success.
+ *             This code is shared between poll() and select() versions.
+ */
+int add_fd(const int fd, const int events)
+{
+       DPRINT(("add_fd(%d, %x)\n", fd, events));
+#if HAVE_POLL
+       if (fd >= FD_MAX) {
+               printf("add_fd(%d, %x): failed\n", fd, events);
+               return E2BIG;
+       }
+
+       fds[fd].fd = fd;
+       fds[fd].events = events;
+       if (highest_fds < fd) 
+               highest_fds = fd;
+#else 
+       if (fd >= FD_SETSIZE)
+               return E2BIG;
+       if (events & POLLIN)
+               FD_SET(fd, &master_fds);
+       if (events & POLLOUT)
+               FD_SET(fd, &master_send_fds);
+#endif
+       return 0;
+}
+
+/*
+ * del_fd():   Close and remove an fd from the set(s) we monitor. (See also add_fd().)
+ */
+void del_fd(const int fd)
+{
+       DPRINT(("del_fd(%d)\n", fd));
+#if HAVE_POLL
+       if (fd >= FD_MAX)
+               return;
+
+       fds[fd].fd = -1;
+       fds[fd].events = 0;
+
+       /* Reduce poll()'s workload by not making it watch past end of array */
+       while ((highest_fds > 0) && (fds[highest_fds].fd == -1))
+               highest_fds--;
+#else 
+       if (fd >= FD_SETSIZE)
+               return;
+       FD_CLR(fd, &master_fds);
+       FD_CLR(fd, &master_send_fds);
+#endif
+
+       close(fd);
+}
+
+#if 0
+void list_clients()
+{
+       struct conn *c = first_conn;
+       printf("list_clients:\n");
+       while (c && c->next_conn) {
+               c = c->next_conn;
+               printf("list_clients: fd %d\n", c->sock);
+       }
+}
+#endif
+
+/*
+ * add_to_linked_list():
+ *             Inserts an element (conn, ftran or dcache) into its linked list.
+ *             The list is placed at the beginning, right after the (bogus)
+ *             first element of the list.
+ */
+void add_to_linked_list(struct list_element * const first,
+                       struct list_element * const elem)
+{
+       elem->prev = first;
+
+        if (first) {
+                elem->next = first->next;
+                if (elem->next) elem->next->prev = elem;
+                first->next = elem;
+        } else {
+               /* this is the bogus head of the list */
+                elem->next = NULL;
+        }
+}
+
+/*
+ * remove_from_linked_list():
+ *             Removes an element (conn, ftran or dcache) from its linked list,
+ *             then frees it.
+ */
+void remove_from_linked_list(struct list_element * const elem)
+{
+       if (elem->prev != NULL) elem->prev->next = elem->next;
+       if (elem->next != NULL) elem->next->prev = elem->prev;
+       free(elem);
+}
+
+/*
+ * alloc_new_conn():
+ *             Allocates a new control connection (type `struct conn'),
+ *             initializes it, and adds it to the linked list. The connection
+ *             operates on the socket SOCK.
+ */
+struct conn *alloc_new_conn(const int sock)
+{
+       const unsigned int one = 1;
+       struct conn *c = (struct conn *)(malloc(sizeof(struct conn)));
+       DPRINT(("alloc_new_conn(%d)\n", sock));
+
+       if (c == NULL) return c;
+
+       if (sock != -1) {
+               ioctl(sock, FIONBIO, &one);
+               if (add_fd(sock, POLLIN) != 0) {
+                       /* temp unavail */
+                       send(sock, "230 Server too busy, please try again later.\r\n", 46, 0);
+                       close(sock);
+                       return NULL;
+               }
+
+               add_to_linked_list((struct list_element *)first_conn,
+                                  (struct list_element *)c);
+       } else {
+               /* this is the bogus head of the list */
+               c->next_conn = NULL;
+               c->prev_conn = NULL;
+       }
+
+       c->transfer = NULL;
+       c->sock = sock;
+       c->buf_len = c->auth = c->rest_pos = 0;
+#if WANT_ASCII
+       c->ascii_mode = 0;
+#endif
+
+       /*
+        * equals:
+        * strcpy(c->curr_dir, "/");
+        * strcpy(c->last_cmd, "");
+        * strcpy(c->rename_from, "")
+        */
+       c->curr_dir[0] = '/';
+#if WANT_FULLSCREEN
+       c->curr_dir[1] = c->last_cmd[0] = c->rename_from[0] = '\0';
+#else
+       c->curr_dir[1] = c->rename_from[0] = '\0';
+#endif
+
+       time(&(c->last_transfer));
+
+       /*list_clients();*/
+
+       return c;
+}
+
+/*
+ * alloc_new_ftran():
+ *             Allocates a new data connection (type `struct ftran'), and
+ *             adds it to the linked list. The connection operates on the
+ *             socket SOCK, and has the control connection C as its parent.
+ */
+struct ftran *alloc_new_ftran(const int sock, const struct conn * const c)
+{
+       struct ftran *f = (struct ftran *)(malloc(sizeof(struct ftran)));
+
+       if (f == NULL) return f;
+       if (c == NULL) {
+               /* this is the bogus head of the list */
+               f->next_ftran = NULL;
+               f->prev_ftran = NULL;
+       } else {
+               add_to_linked_list((struct list_element *)first_ftran,
+                                  (struct list_element *)f);
+       }
+
+#if HAVE_MMAP
+       f->file_data = NULL;
+#endif
+       f->owner = (struct conn * const)c;
+       f->sock = sock;
+       f->state = 0;
+       f->local_file = -1;
+
+#if WANT_DCACHE
+       f->dir_cache = NULL;
+#endif
+
+       f->dir_listing = 0;
+       return f;
+}
+
+#if WANT_DCACHE
+/*
+ * alloc_new_dcache():
+ *             Allocates a new directory cache entry (type `struct dcache'),
+ *             and adds it to the linked list.
+ */
+struct dcache *alloc_new_dcache()
+{
+       struct dcache *d = (struct dcache *)(malloc(sizeof(struct dcache)));
+
+       if (d == NULL) return d;
+
+       d->use_count = 0;
+       d->last_used = 0;
+       strcpy(d->dir_name, "");
+       d->dir_data = NULL;
+
+       add_to_linked_list((struct list_element *)first_dcache,
+                          (struct list_element *)d);
+
+       return d;
+}
+#endif
+
+/*
+ * destroy_conn():
+ *             Destroy a control connection, remove it from the linked
+ *             list, and clean up after it.
+ */
+void destroy_conn(struct conn * const c)
+{
+       DPRINT(("destroy_conn:\n"));
+       if (c == NULL) return;
+       del_fd(c->sock);
+
+       destroy_ftran(c->transfer);
+       remove_from_linked_list((struct list_element *)c);
+
+       DPRINT(("destroy_conn done.\n"));
+}
+
+/*
+ * destroy_ftran():
+ *             Destroy a data connection, remove it from the linked list,
+ *             and clean up after it.
+ *
+ *             For some reason, TCP_CORK (Linux 2.2.x-only) doesn't flush
+ *             even _after_ the socket is closed, so we zero it just before
+ *             closing. We also zero just before sending the last packet,
+ *             as it seems to be needed on some systems.
+ *
+ *             If you wonder why I check for `defined(SOL_TCP)' and don't
+ *             provide an alternative, see the comments on init_file_transfer().
+ */
+void destroy_ftran(struct ftran * const f)
+{
+       const unsigned int zero = 0;
+
+       if (f == NULL) return;
+#if defined(TCP_CORK) && defined(SOL_TCP)
+       setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero));
+#endif
+       del_fd(f->sock);
+
+#if WANT_DCACHE
+       if (f->dir_cache) {
+               time(&(f->dir_cache->last_used));
+               f->dir_cache->use_count--;
+               f->dir_cache = NULL;
+       } else
+#endif
+#if HAVE_MMAP
+               if (f->file_data) {
+                       if (f->dir_listing) {
+                               free(f->file_data);
+                               f->file_data = NULL;
+                       } else {
+                               munmap(f->file_data, f->size);
+                       }
+               }
+
+       if (!f->dir_listing)
+#endif
+               if (f->local_file != -1) close(f->local_file);
+
+#if !HAVE_MMAP
+       if (f->dir_listing) unlink(f->filename);
+#endif
+
+       f->owner->transfer = NULL;
+
+#if WANT_DCACHE
+       if (f->dir_cache != NULL) f->dir_cache->use_count--;
+#endif
+
+       remove_from_linked_list((struct list_element *)f);
+}
+
+#if WANT_DCACHE
+/*
+ * destroy_dcache():
+ *             Destroy a directory listing cache entry, remove it from the
+ *             linked list, and clean up after it.
+ *
+ *             If you free a cache entry that is in use (use_count > 0),
+ *             BetaFTPD will most likely crash (later). The thing you're supposed
+ *             to do when you're done with a dcache entry, is to decrement
+ *             its use_count, and let the timeout functions do the destroying
+ *             when it's time to do so.
+ */
+void destroy_dcache(struct dcache * const d)
+{
+        if (d == NULL) return;
+
+       if (d->dir_data != NULL) free(d->dir_data);
+       remove_from_linked_list((struct list_element *)d);
+}
+#endif
+
+/*
+ * process_all_clients():
+ *             Processes all the control connections in active_clients
+ *             (normally returned from a select(), there are at max
+ *             NUM_AC active connections in the set), sending them
+ *             through to the command parser if a command has been
+ *             entered.
+ */
+#if HAVE_POLL
+int process_all_clients(const int num_ac)
+#else
+int process_all_clients(const fd_set * const active_clients, const int num_ac)
+#endif
+{
+       struct conn *c = NULL, *next = first_conn->next_conn;
+       int checked_through = 0;
+
+       DPRINT(("process_all_clients: num_ac %d\n", num_ac));
+
+       /* run through the linked list */
+       while (next != NULL && checked_through < num_ac) {
+               int bytes_avail;
+
+               c = next;
+               next = c->next_conn;
+#if HAVE_POLL
+               if (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL)) {
+                       destroy_conn(c);
+                       continue;
+               }
+               if (!fds[c->sock].revents & POLLIN) {
+                       continue;
+               }
+               DPRINT(("process_all_clients: fd %d has POLLIN set\n", c->sock));
+#else
+               if (!FD_ISSET(c->sock, active_clients)) {
+                       continue;
+               }
+#endif
+
+               checked_through++;
+
+               bytes_avail = recv(c->sock, c->recv_buf + c->buf_len,
+                                  255 - c->buf_len, 0);
+               if (bytes_avail <= 0) {
+                       /*
+                        * select() has already told us there's something about
+                        * this socket, so if we get a return value of zero, the
+                        * client has closed the socket. If we get a return value
+                        * of -1 (error), we close the socket ourselves.
+                        *
+                        * Just to be safe, we include this code for poll() as
+                        * well.
+                        */
+                       destroy_conn(c);
+                       continue;
+               }
+
+               /* overrun = disconnect */
+               if (c->buf_len + bytes_avail > 254) {
+                       numeric(c, 503, "Buffer overrun; disconnecting.");
+                       destroy_conn(c);
+                       continue;
+               }
+
+               c->buf_len += bytes_avail;
+               parse_command(c);
+       }
+       return checked_through;
+}
+
+/*
+ * process_all_sendfiles():
+ *             Sends data to all clients that are ready to receive it.
+ *             Also checks for data connections that are newly-connected,
+ *             and handler xferlog entries for the files that are finished.
+ */
+#if HAVE_POLL
+int process_all_sendfiles(const int num_ac)
+#else
+int process_all_sendfiles(fd_set * const active_clients, const int num_ac)
+#endif
+{
+       struct ftran *f = NULL, *next = first_ftran->next_ftran;
+       int checked_through = 0;
+       struct sockaddr tempaddr;
+       int tempaddr_len = sizeof(tempaddr);
+       while (next != NULL && checked_through < num_ac) {
+               f = next;
+               next = f->next_ftran;
+
+#if HAVE_POLL
+               if (fds[f->sock].revents & (POLLHUP|POLLERR|POLLNVAL)) {
+                       destroy_ftran(f);
+                       continue;
+               }
+#endif
+
+               /* state = 2: incoming PASV, state >3: send file */
+#if HAVE_POLL
+               if ((f->state < 2) || (f->state == 3) ||  (fds[f->sock].revents & (POLLIN|POLLOUT)) == 0) {
+#else
+               if ((f->state < 2) || (f->state == 3) || !FD_ISSET(f->sock, active_clients)) {
+#endif
+                       continue;
+               }
+
+               checked_through++;
+
+#if HAVE_POLL
+               /* Nothing is needed for the poll() version? */
+#else
+               FD_CLR(f->sock, active_clients);
+#endif
+
+               if (f->state == 2) {            /* incoming PASV */
+                       const unsigned int one = 1;
+                       const int tempsock = accept(f->sock, (struct sockaddr *)&tempaddr,
+                                                       &tempaddr_len);
+
+                       del_fd(f->sock);
+
+                       if (tempsock == -1) {
+                               destroy_ftran(f);
+                               continue;
+                       }
+
+                       f->sock = tempsock;
+                       ioctl(f->sock, FIONBIO, &one);
+                       init_file_transfer(f);
+#if WANT_UPLOAD
+                       if (f->upload) continue;
+#endif
+               }
+               if (f->state < 5) {
+                       init_file_transfer(f);
+#if WANT_UPLOAD
+                       if (f->upload) continue;
+#endif
+               }
+
+               /* for download, we send the first packets right away */
+#if WANT_UPLOAD
+               if (f->upload) {
+                       if (do_upload(f)) continue;
+               } else
+#endif
+                       if (do_download(f)) continue;
+
+               /* do_{upload,download} returned 0, the transfer is complete */
+                numeric(f->owner, 226, "Transfer complete.");
+                time(&(f->owner->last_transfer));
+
+#if WANT_XFERLOG
+                if (!f->dir_listing) {
+                       write_xferlog(f);
+               }
+#endif
+
+               destroy_ftran(f);
+#if WANT_FULLSCREEN
+                update_display(first_conn);
+#endif
+        }
+
+        return checked_through;
+}
+
+#if WANT_UPLOAD
+int do_upload(struct ftran *f)
+{
+       char upload_buf[16384];
+       int avail, size;
+#if WANT_ASCII
+       /* keep buffer size small in ascii transfers 
+          to prevent process stalling while filtering
+          data on slower computers */
+
+       /* 
+        * This isn't a big problem, since we won't get
+        * packets this big anyway, the biggest I've seen
+        * was 12kB on 100mbit (but that was from a Windows
+        * machine), so I've reduced the buffer from 64 kB
+        * to 16 kB :-) --Steinar
+        */
+       const int maxlen = (f->ascii_mode == 1) ? 4096 : 16384;
+#else
+       const int maxlen = 16384;
+#endif
+
+       errno = 0;
+       size = recv(f->sock, upload_buf, maxlen, 0);
+       if (size >= 0) {
+               f->pos += size;
+       }
+#if WANT_ASCII
+       if (size > 0 && f->ascii_mode == 1) {
+               size = ascii_uploadfilter(upload_buf, size);
+       }
+#endif
+       if (size > 0 && (write(f->local_file, upload_buf, size) == size)) {
+               return 1;
+       } else if (size == -1) {
+               /* don't write xferlog... or? */
+               numeric(f->owner, 426, strerror(errno));
+               destroy_ftran(f);
+               return 1;
+       }
+       return 0;
+} 
+#endif
+
+int do_download(struct ftran *f)
+{
+#if defined(TCP_CORK) && defined(SOL_TCP)
+       unsigned int zero = 0;
+#endif
+       char *sendfrom_buf;
+       int bytes_to_send;
+       int more_to_send = 0;
+
+#if !HAVE_MMAP
+       char buf[MAX_BLOCK_SIZE];
+#endif
+#if WANT_ASCII
+       char buf2[MAX_BLOCK_SIZE * 2];
+#endif
+       int size;
+
+#if HAVE_LINUX_SENDFILE
+       /*
+        * We handle the optimal case first, which is sendfile().
+        * Here we use a rather simplified sending `algorithm',
+        * leaving most of the quirks to the system calls.
+        */
+       if (f->dir_listing == 0
+#if WANT_UPLOAD
+               && f->upload == 0
+#endif
+       ) {
+               int err;
+               size = f->size - f->pos;
+
+               if (size > f->block_size) size = f->block_size;
+               if (size < 0) size = 0;
+
+#ifdef TCP_CORK
+               if (size != f->block_size) {
+                       setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero));
+               }       
+#endif
+
+                       err = sendfile(f->sock, f->local_file, &f->pos, size);
+               return (f->pos < f->size) && (err > -1);
+       }
+#endif
+
+#if HAVE_MMAP
+        size = f->size - f->pos;
+
+        if (size > f->block_size) size = f->block_size;
+        if (size < 0) size = 0;
+
+       bytes_to_send = size;
+       sendfrom_buf = f->file_data + f->pos;
+#else
+       bytes_to_send = read(f->local_file, buf, f->block_size);
+       sendfrom_buf = buf;
+#endif
+
+       if (bytes_to_send == f->block_size) more_to_send = 1;
+
+#if WANT_ASCII
+       if (f->ascii_mode == 1) {
+               bytes_to_send = ascii_downloadfilter(sendfrom_buf,
+                                                    buf2, bytes_to_send);
+               sendfrom_buf = buf2;
+               }
+#endif /* WANT_ASCII */
+
+#if defined(TCP_CORK) && defined(SOL_TCP)
+       /* if we believe this is the last packet, unset TCP_CORK */
+       if (more_to_send == 0) {
+               setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&zero, sizeof(zero));
+       }
+#endif
+
+       size = send(f->sock, sendfrom_buf, bytes_to_send, 0);
+       if (size < bytes_to_send) more_to_send = 1;
+
+#if WANT_ASCII
+       if (f->ascii_mode == 1 && size < bytes_to_send && size > 0) {
+               size = ascii_findlength(sendfrom_buf, size);
+       }
+#endif
+
+#if HAVE_MMAP
+       if (size > 0) f->pos += size;
+#endif
+
+       return more_to_send;
+}
+
+#if WANT_XFERLOG
+void write_xferlog(struct ftran *f)
+{
+       char temp[256];
+               time_t now = time(NULL);
+               struct tm *t = localtime(&now);
+
+       if (xferlog == NULL) return;
+
+       strftime(temp, 256, "%a %b %d %H:%M:%S %Y", t);
+#if WANT_UPLOAD
+       fprintf(xferlog, "%s %u %s %lu %s b _ %c a %s ftp 0 * \n",
+#else
+       fprintf(xferlog, "%s %u %s %lu %s b _ o a %s ftp 0 *\n",
+#endif
+               temp, (int)(difftime(now, f->tran_start)),
+               inet_ntoa(f->sin.sin_addr), f->size,
+               f->filename,
+#if WANT_UPLOAD
+               (f->upload) ? 'i' : 'o',
+#endif
+               f->owner->username);
+               fflush(xferlog);
+
+#if 0
+       /* vim needs this to work properly :-( */
+       )
+#endif
+}
+#endif
+
+#if 0
+/* Reallocate the buggers constantly */
+void screw_clients()
+{
+       struct conn *c = first_conn;
+       int maxloops = MAXCLIENTS;
+
+       while (c && c->next_conn) {
+               struct conn *temp = malloc(sizeof(*temp));
+               if (!temp) break;
+               *temp = *(c->next_conn);
+               if (temp->transfer) temp->transfer->owner = temp;
+               memset(c->next_conn, 0, sizeof(struct conn));
+               free(c->next_conn);
+               temp->prev_conn = c;
+               c->next_conn = temp;
+               c = c->next_conn;
+               maxloops--;
+               assert(maxloops > 0);
+       }
+}
+#endif
+
+/*
+ * main():     Main function. Does the initialization, and contains
+ *             the main server loop. Takes no command-line arguments
+ *             (see README for justification).
+ */
+int main(void)
+{
+       int server_sock;
+
+#if HAVE_POLL
+       /* the sets are declared globally if we use poll() */
+#else
+       fd_set fds, fds_send;
+#endif
+
+       /*setlinebuf(stdout);*/
+       setvbuf(stdout, (char *)NULL, _IOLBF, 0); 
+
+       signal(SIGPIPE, SIG_IGN);
+
+       printf("BetaFTPD version %s, Copyright (C) 1999-2000 Steinar H. Gunderson\n", VERSION);
+       puts("BetaFTPD comes with ABSOLUTELY NO WARRANTY; for details see the file");
+       puts("COPYING. This is free software, and you are welcome to redistribute it");
+       puts("under certain conditions; again see the file COPYING for details.");
+       puts("");
+
+       /* we don't need stdin */
+       close(0);
+
+#if HAVE_POLL
+       {
+               int i;
+               for (i = 0; i < FD_MAX; i++) {
+                       fds[i].fd = -1;
+                       fds[i].events = 0;
+               }
+       }
+#else
+       FD_ZERO(&master_fds);
+       FD_ZERO(&master_send_fds);
+#endif
+
+       server_sock = create_server_socket();
+
+#if WANT_FULLSCREEN
+       printf("%cc", (char)27);        /* reset and clear the screen */
+#endif
+
+       /* init dummy first connection */
+       first_conn = alloc_new_conn(-1);
+       first_ftran = alloc_new_ftran(0, NULL);
+#if WANT_DCACHE
+       first_dcache = alloc_new_dcache();
+#endif
+
+#if WANT_XFERLOG
+#if WANT_NONROOT
+#warning No xferlog support for nonroot yet
+#else
+       /* open xferlog */
+       xferlog = fopen("/var/log/xferlog", "r+");
+       if (xferlog == NULL) xferlog = fopen("/usr/adm/xferlog", "r+");
+
+       if (xferlog != NULL) {
+                 fseek(xferlog, 0L, SEEK_END);
+        }
+#endif
+#endif
+
+#if WANT_FORK
+       switch (fork()) {
+       case -1:
+               perror("fork()");
+               puts("fork() failed, exiting");
+               exit(0);
+       case 0:
+               break;
+       default:
+               puts("BetaFTPD forked into the background");
+               exit(0);
+       }
+#else
+       puts("BetaFTPD active");
+#endif
+
+       /* set timeout alarm here (after the fork) */
+       alarm(60);
+       signal(SIGALRM, handle_alarm);
+
+       for ( ;; ) {
+               int i;
+#ifndef HAVE_POLL
+               struct timeval timeout;
+#endif
+
+               /*screw_clients();       //look for memory errors */
+
+#if WANT_FULLSCREEN
+               update_display(first_conn);
+#endif
+
+#if HAVE_POLL
+               i = poll(fds, highest_fds + 1, 60000);
+               DPRINT(("poll returns %d\n", i));
+#if 0
+               {
+                       int j;
+                       for (j=0; j<=highest_fds; j++) {
+                               if (fds[j].revents) printf("fds[%d].fd %d, .revents %x\n", j, fds[j].fd, fds[j].revents);
+                       }
+               }
+#endif
+#else
+               /* reset fds (gets changed by select()) */
+               fds = master_fds;
+               fds_send = master_send_fds;
+
+               /*
+                * wait up to 60 secs for any activity 
+                */
+               timeout.tv_sec = 60;
+               timeout.tv_usec = 0;
+
+               i = select(FD_SETSIZE, &fds, &fds_send, NULL, &timeout);
+#endif
+
+               if (i == -1) {
+                       if (errno == EBADF) {
+#if !HAVE_POLL
+                               /* don't like this, but we have to */
+                               clear_bad_fds(&server_sock);
+#endif
+                       } else if (errno != EINTR) {
+#if HAVE_POLL
+                               perror("poll()");
+#else
+                               perror("select()");
+#endif
+                               continue;
+                       }
+               }
+
+#if HAVE_POLL
+               /* fix an invalid server socket */
+               if (fds[server_sock].revents & POLLERR) {
+                       del_fd(server_sock);
+                       server_sock = create_server_socket();
+               }
+#endif
+
+               /* remove any timed out sockets */
+               if (time_to_check) {
+                       time_out_sockets();
+#if WANT_DCACHE
+                       time_out_dcache();
+#endif
+                       time_to_check = 0;
+               }
+
+               if (i <= 0) continue;
+
+#if HAVE_POLL
+               i -= process_all_sendfiles(i);
+               process_all_clients(i);
+#else
+               /* sends are given highest `priority' */
+               i -= process_all_sendfiles(&fds_send, i);
+
+               /* incoming PASV connections and uploads */
+               i -= process_all_sendfiles(&fds, i);
+
+               /*
+                * check the incoming PASV connections first, so
+                * process_all_clients() won't be confused.
+                */ 
+               process_all_clients(&fds, i);
+#endif
+
+#if HAVE_POLL
+               if (fds[server_sock].revents & POLLIN) {
+#else
+               if (FD_ISSET(server_sock, &fds)) {
+#endif
+                       accept_new_client(&server_sock);
+                       i--;
+               }
+       }
+}
+
+/*
+ * accept_new_client():
+ *             Open a socket for the new client, say hello and put it in
+ *             among the others.
+ */
+void accept_new_client(int * const server_sock)
+{
+       struct sockaddr_in tempaddr;
+       int tempaddr_len = sizeof(tempaddr);
+       const int tempsock = accept(*server_sock, (struct sockaddr *)&tempaddr, &tempaddr_len);
+
+       static int num_err = 0;
+
+       if (tempsock < 0) {
+#ifndef WANT_FORK
+               perror("accept()");
+#endif
+               close(tempsock);
+               if ((errno == EBADF || errno == EPIPE) && ++num_err >= 3) {
+                       del_fd(*server_sock);
+                       *server_sock = create_server_socket();
+               }
+       } else {
+               struct conn * const c = alloc_new_conn(tempsock);
+               num_err = 0;
+               if (c != NULL) {
+                       numeric(c, 220, "BetaFTPD " VERSION " ready.");
+#if WANT_STAT
+                       memcpy(&(c->addr), &tempaddr, sizeof(struct sockaddr));
+#endif
+               }
+       }
+}
+
+/*
+ * time_out_sockets():
+ *             Times out any socket that has not had any transfer
+ *             in the last 15 minutes (delay not customizable by FTP
+ *             user -- you must change it in ftpd.h).
+ *
+ *             Note that RFC959 explicitly states that there are no
+ *             `spontaneous' error replies, yet we have to do it to
+ *             get the message through at all.
+ *
+ *             If we check this list for every accept() call, it's
+ *             actually eating a lot of CPU time, so we only check
+ *             it every minute. We used to do a time() call here,
+ *             but we've changed to do use an alarm() call and set
+ *             the time_to_check_flag in the SIGALRM handler.
+ */
+RETSIGTYPE handle_alarm(int signum)
+{
+       time_to_check = 1;
+       alarm(60);
+
+       /* for libc5 */
+       signal(SIGALRM, handle_alarm);
+}
+
+void time_out_sockets()
+{
+       struct conn *c = NULL, *next = first_conn->next_conn;
+       time_t now = time(NULL);  
+
+       /* run through the linked list */
+       while (next != NULL) {
+               c = next;
+               next = c->next_conn;
+
+               if ((c->transfer == NULL || c->transfer->state != 5) &&
+                   (now - c->last_transfer > TIMEOUT_SECS)) {
+                       /* RFC violation? */
+                       numeric(c, 421, "Timeout (%u minutes): Closing control connection.", TIMEOUT_SECS/60);
+                       destroy_conn(c);
+               }
+       }
+}
+
+#if WANT_DCACHE
+/*
+ * time_out_dcache():
+ *             Time out expired directory listing cache entries.
+ *             Uses much of the same code as time_out_sockets().
+ */
+void time_out_dcache()
+{
+       struct dcache *d = NULL, *next = first_dcache->next_dcache;
+       time_t now = time(NULL);        
+
+       /* run through the linked list */
+       while (next != NULL) {
+               d = next;
+               next = d->next_dcache;
+
+               if (d->use_count == 0 && (now - d->last_used > 900)) {
+                       destroy_dcache(d);
+               }
+       }
+}
+#endif
+
+/*
+ * remove_bytes():
+ *             Remove some bytes from the incoming buffer. This gives
+ *             room for new data on the control connection, and should
+ *             be called when the code has finished using the data.
+ *             (This is done automatically for all commands, so you
+ *             normally need not worry about it.)
+ */
+void remove_bytes(struct conn * const c, const int num)
+{
+       if (c->buf_len <= num) {
+               c->buf_len = 0;
+       } else {
+               c->buf_len -= num;
+               memmove(c->recv_buf, c->recv_buf + num, c->buf_len);
+       }
+}
+
+/*
+ * numeric():  Sends a numeric FTP reply to the client. Note that
+ *             you can use this command much the same way as you
+ *             would use a printf() (with all the normal %s, %d,
+ *             etc.), since it actually uses printf() internally.
+ */
+void numeric(struct conn * const c, const int numeric, const char * const format, ...)
+{
+       char buf[256], fmt[256];
+       va_list args;
+       int i, err;
+
+       snprintf(fmt, 256, "%03u %s\r\n", numeric, format);
+
+       va_start(args, format);
+       i = vsnprintf(buf, 256, fmt, args);
+       va_end(args);
+
+       DPRINT((buf));
+       err = send(c->sock, buf, i, 0);
+       if (err == -1 && errno == EPIPE) {
+               destroy_conn(c);
+       }
+}
+
+/*
+ * init_file_transfer():
+ *             Initiate a data connection for sending. This does not open
+ *             any files etc., just does whatever is needed for the socket,
+ *             if needed. It does, however, send the 150 reply to the client,
+ *             and mmap()s if needed.
+ *
+ *             Linux systems (others?) define SOL_TCP right away, which saves us
+ *             some grief and code size. Perhaps using getprotoent() is the `right'
+ *             way, but it's bigger :-) (Optionally, we could figure it out at
+ *             configure time, of course...)
+ *
+ *             For optimal speed, we use the Linux 2.2.x-only TCP_CORK flag if
+ *             possible. Note that this is only defined in the first `arm' --
+ *             we silently assume that Linux is the only OS supporting this
+ *             flag. This might be an over-generalization, but I it looks like
+ *             we'll have to depend on it other places as well, so we might
+ *             just as well be evil here.
+ */
+void init_file_transfer(struct ftran * const f)
+{
+       struct linger ling;
+       struct conn * const c = f->owner;
+       const int mode = IPTOS_THROUGHPUT, zero = 0, one = 1;
+       struct stat buf;
+       int events;
+
+#ifdef SOL_TCP
+       /* we want max throughput */
+       setsockopt(f->sock, SOL_IP, IP_TOS, (void *)&mode, sizeof(mode));
+       setsockopt(f->sock, SOL_TCP, TCP_NODELAY, (void *)&zero, sizeof(zero));
+#ifdef TCP_CORK
+       setsockopt(f->sock, SOL_TCP, TCP_CORK, (void *)&one, sizeof(one));
+#endif
+#else
+       /* should these pointers be freed afterwards? */
+       {
+               getprotoent();  /* legal? */
+               {
+                       const struct protoent * const pe_ip  = getprotobyname("ip");
+                       const struct protoent * const pe_tcp = getprotobyname("tcp");
+                       setsockopt(f->sock, pe_ip->p_proto, IP_TOS, (void *)&mode, sizeof(mode));
+                       setsockopt(f->sock, pe_tcp->p_proto, TCP_NODELAY, (void *)&zero, sizeof(zero));
+               }
+               endprotoent();
+       }
+#endif
+
+       if (f->dir_listing) {
+               f->block_size = MAX_BLOCK_SIZE;
+       } else {
+#if WANT_ASCII
+               f->ascii_mode = f->owner->ascii_mode;
+#endif
+
+               /* find the preferred block size */
+               f->block_size = MAX_BLOCK_SIZE;
+               if (fstat(f->local_file, &buf) != -1 &&
+                   buf.st_blksize < MAX_BLOCK_SIZE) {
+                       f->block_size = buf.st_blksize;
+               }
+       }
+
+       f->state = 5;
+
+       events = POLLOUT;
+#if WANT_UPLOAD
+       if (f->upload) {
+                events = POLLIN;
+        }
+#endif /* WANT_UPLOAD */
+
+       TRAP_ERROR(add_fd(f->sock, events), 500, return);
+
+       ling.l_onoff = 0;
+       ling.l_linger = 0;
+       setsockopt(f->sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
+
+#if !HAVE_POLL && WANT_UPLOAD
+       /*
+        * if we let an upload socket stay in master_send_fds, we would
+        * get data that would fool us into closing the socket... (sigh)
+        */
+       if (f->upload) {
+               FD_CLR(f->sock, &master_send_fds);
+               FD_SET(f->sock, &master_fds);
+       }
+#endif
+
+       time(&(f->owner->last_transfer));
+       
+       if (f->dir_listing) {
+               /* include size? */
+               numeric(f->owner, 150, "Opening ASCII mode data connection for directory listing.");
+       } else {
+               /*
+                * slightly kludged -- perhaps we should kill the second arm,
+                * at the expense of code size? Or perhaps we could collapse
+                * the two possible replies into one?
+                */
+#if WANT_ASCII
+               if (f->ascii_mode
+#if WANT_UPLOAD
+                       || f->upload
+#endif /* WANT_UPLOAD */
+               ) {
+                       numeric(f->owner, 150, "Opening %s mode data connection for '%s'",
+                               (f->ascii_mode) ? "ASCII" : "BINARY", f->filename);
+               } else {
+                       numeric(f->owner, 150, "Opening %s mode data connection for '%s' (%u bytes)",
+                               (f->ascii_mode) ? "ASCII" : "BINARY", f->filename,
+                               f->size); 
+               }
+#else /* !WANT_ASCII */
+#if WANT_UPLOAD
+               if (f->upload) {
+                       numeric(f->owner, 150, "Opening BINARY mode data connection for '%s'", f->filename);
+               } else
+#endif /* WANT_UPLOAD */
+                       numeric(f->owner, 150, "Opening BINARY mode data connection for '%s' (%u bytes)", f->filename, f->size);
+#endif /* !WANT_ASCII */
+       }
+
+       /*
+        * This section _could_ in theory be more optimized, but it's
+        * much easier this way, and hopefully, the compiler will be
+        * intelligent enough to optimize most of this away. The idea
+        * is, some modes _require_ use of mmap (or not). The preferred
+        * thing is using mmap() when we don't have sendfile(), and not
+        * using mmap() when we have sendfile().
+        */
+#if HAVE_MMAP
+       if (f->dir_listing == 0) {
+#if HAVE_LINUX_SENDFILE
+               int do_mmap = 0;
+#else
+               int do_mmap = 1;
+#endif
+#if WANT_ASCII
+               if (f->ascii_mode == 1) do_mmap = 1;
+#endif
+#if WANT_UPLOAD
+                if (f->upload == 1) do_mmap = 0;
+#endif
+               if (do_mmap == 1) {
+                       f->file_data = mmap(NULL, f->size, PROT_READ, MAP_SHARED, f->local_file, 0);
+                       if (f->file_data == MAP_FAILED) f->file_data = NULL;
+               } else {
+                       f->file_data = NULL;
+               }
+               f->pos = f->owner->rest_pos;
+       }
+#else /* !HAVE_MMAP */
+       lseek(f->local_file, f->owner->rest_pos, SEEK_SET);
+#endif
+}
+
+/*
+ * create_server_socket():
+ *             Create and bind a server socket, that we can use to
+ *             listen to new clients on.
+ */
+int create_server_socket()
+{
+       int server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+       const unsigned int one = 1;
+       struct sockaddr_in addr;
+       int err;
+       
+       /*
+        * In the `perfect' world, if an address was in use, we could
+        * just wait for the kernel to clear everything up, and everybody
+        * would be happy. But when you just found out your server socket
+        * was invalid, it has to be `re-made', and 3000 users are trying
+        * to access your fileserver, I think it's nice that it comes
+        * up right away... hence this option.
+        */
+       setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+       ioctl(server_sock, FIONBIO, &one);      /* just in case */
+
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = INADDR_ANY;
+       addr.sin_port = htons(FTP_PORT);
+
+       do {
+               err = bind(server_sock, (struct sockaddr *)&addr, sizeof(struct sockaddr));
+
+               if (err == -1) {
+                       perror("bind()");
+               
+                       /* try to recover from recoverable errors... */
+                       if (errno == ENOMEM || errno == EADDRINUSE) {
+                               puts("Waiting 1 sec before trying again...");
+                               sleep(1);
+                       } else {
+                               puts("Giving up.");
+                               exit(1); 
+                       }
+               }
+       } while (err == -1);
+
+       listen(server_sock, 20);
+
+       err = add_fd(server_sock, POLLIN);
+       if (err) {
+               perror("add_fd");
+               return -1;
+       }
+
+       return server_sock;
+}
+
+#if !HAVE_POLL
+/*
+ * clear_bad_fds():
+ *             Try to find invalid socket descriptors, and clean them.
+ *             The methods used are rather UGLY, but I can't think of
+ *             any good way of checking e.g. server_sock without
+ *             doing anything to it :-(
+ *
+ *             poll() is able to do this in a much cleaner way, which 
+ *             we use if we use poll(). That checking isn't done here,
+ *             though.
+ */
+void clear_bad_fds(int * const server_sock)
+{
+       {
+               fd_set fds;
+               struct timeval tv = { 0, 0 };
+
+               FD_ZERO(&fds);
+               FD_SET(*server_sock, &fds); 
+               if (select(*server_sock, &fds, NULL, NULL, &tv) == -1) {
+                       FD_CLR(*server_sock, &master_fds);
+                       close(*server_sock);
+                       *server_sock = create_server_socket();
+               }
+       }
+
+       /* could do this (conn, ftran) in any order */
+       {
+               struct conn *c = NULL, *next = first_conn->next_conn;
+       
+               /* run through the linked list */
+               while (next != NULL) {
+                       char buf[1];
+
+                       c = next;
+                       next = c->next_conn;
+
+                       if (read(c->sock, &buf, 0) == -1 &&
+                           errno == EBADF) {
+                               destroy_conn(c);
+                       }
+               }
+       }
+
+       {
+               struct ftran *f = NULL, *next = first_ftran->next_ftran;
+       
+               while (next != NULL) {
+                       char buf[1];
+
+                       f = next;
+                       next = f->next_ftran;
+
+                       if (read(f->sock, &buf, 0) == -1 &&
+                           errno == EBADF) {
+                               destroy_ftran(f);
+                       }
+               }
+       }       
+}
+#endif
+
+#if WANT_MESSAGE
+/*
+ * dump_file(): Dumps a file on the control connection. Used for
+ *             welcome messages and the likes. Note that outbuf
+ *             is so big, to prevent any crashing from users creating
+ *             weird .message files (like 1024 LFs)...
+ */
+void dump_file(struct conn * const c, const int num, const char * const filename)
+{
+       char buf[1024], outbuf[8192];
+       char *ptr = outbuf + 4;
+       int i, j = -1;
+
+       const int dumpfile = open(filename, O_RDONLY);
+       if (dumpfile == -1) return;
+
+       i = read(dumpfile, buf, 1024);
+       if (i <= 0) {
+               close(dumpfile);
+               return;
+       }
+
+       sprintf(outbuf, "%03u-", num);
+       while (++j < i) {
+               *ptr++ = buf[j];
+               if (buf[j] == '\n') {
+                       sprintf(ptr, "%03u-", num);
+                       ptr += 4;
+               }
+       }
+       *ptr++ = '\n';
+
+       send(c->sock, outbuf, ptr - outbuf, 0);
+       close(dumpfile);
+}
+
+
+/*
+ * list_readme():
+ *             Lists all README file in the current (ie. OS current)
+ *             directory, in a 250- message.
+ */
+void list_readmes(struct conn * const c)
+{
+       glob_t pglob;
+       const time_t now = time(NULL);
+       int i;
+
+       if (glob("README*", 0, NULL, &pglob) == 0) {
+               for (i = 0; i < pglob.gl_pathc; i++) {
+                       const char * const temp = pglob.gl_pathv[i];
+                       struct stat buf;
+                       char str[2048];
+
+                       char *tm;
+
+                       if (stat(temp, &buf) == -1) continue;
+
+                       /* remove trailing LF */
+                       tm = ctime(&buf.st_mtime);
+                       tm[strlen(tm) - 1] = 0;
+
+                       sprintf(str, "250-Please read the file %s\r\n"
+                                    "250-\tIt was last modified %s - %ld days ago\r\n",
+                               temp, tm,
+                               (now - buf.st_mtime) / 86400);
+                       send(c->sock, str, strlen(str), 0);
+               }
+               globfree(&pglob);
+       }       
+}
+#endif
+
diff --git a/ftpd.h b/ftpd.h
new file mode 100644 (file)
index 0000000..c479154
--- /dev/null
+++ b/ftpd.h
@@ -0,0 +1,227 @@
+/*  ftpd.h: Prototypes for BetaFTPD
+    Copyright (C) 1999-2000 Steinar H. Gunderson
+
+    This program is is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License, version 2 if the
+    License as published by the Free Software Foundation.
+
+    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * This is the port you want BetaFTPD to listen on. The standard
+ * FTP port is 21 -- if you really want to use BetaFTPD as your
+ * primary FTP server, change FTP_PORT.
+ */
+#if WANT_NONROOT
+#define FTP_PORT 12121
+#else
+#define FTP_PORT 21
+#endif
+
+/*
+ * This is the number of seconds an idle connection is allowed to
+ * remain idle (`idle' is defined as `no activity on the data socket',
+ * more or less) without getting shut down. This is not accurate,
+ * as such delays are only checked for every 60 seconds.
+ *
+ * The default (15 minutes) should be OK for most people.
+ */
+#define TIMEOUT_SECS 900
+
+/*
+ * This is the maximum block size you think you need. (This will most
+ * likely be the block size of your filesystem, and you're not likely
+ * to need a bigger number than this, unless your TCP stack likes
+ * big send()s better than small ones, and still manages to `interleave'
+ * the framents.) If this value is too small, your performance would be
+ * slightly worse, but it would still work. Try to keep it at a power of
+ * two -- most (read: all) FS block sizes _are_ powers of two. If you
+ * set it too high, it won't affect performance much -- you would just
+ * use a bit more memory.
+ */
+#define MAX_BLOCK_SIZE 4096
+
+#if HAVE_LINUX_SENDFILE && !HAVE_MMAP
+#warning sendfile() without mmap() is not supported -- disabling sendfile()
+#undef HAVE_LINUX_SENDFILE
+#endif
+
+#if WANT_DCACHE && !HAVE_MMAP
+#warning directory cache requires use of mmap() -- disabling directory cache
+#undef WANT_DCACHE
+#endif
+
+struct list_options {
+/*     int recursive; */
+       int long_listing;
+       int classify;
+};
+
+/*
+ * General structure for the doubly linked lists (conn, ftran, dcache).
+ * This is used only by the generic linked list code (which inserts and
+ * removes elements from the lists).
+ */
+struct list_element {
+       struct list_element *prev;
+       struct list_element *next;
+
+       /* structure specific data here */
+};
+
+/* doubly linked list of active connections */
+struct conn {
+       struct conn *prev_conn;
+       struct conn *next_conn;
+
+       int sock;
+#if WANT_STAT
+       struct sockaddr addr;
+#endif
+       char recv_buf[256];
+#if WANT_FULLSCREEN
+       char last_cmd[256];
+#endif
+       char rename_from[256];
+
+       int buf_len;
+       int auth;
+
+       char username[17];
+
+       uid_t uid;
+       char root_dir[256];
+       char curr_dir[256];
+
+       struct ftran *transfer;
+
+       int rest_pos;
+#if WANT_ASCII
+       int ascii_mode;
+#endif
+
+       time_t last_transfer;
+};
+
+#if WANT_DCACHE
+/* doubly linked list of cached directory listings */
+struct dcache {
+       struct dcache *prev_dcache;
+       struct dcache *next_dcache;
+
+       int use_count;
+       time_t last_used;
+       time_t generated;
+
+       char dir_name[256];
+       char pattern[256];
+       struct list_options lo;
+
+       char *dir_data;
+       int dir_size;
+};
+#endif
+
+/* doubly linked list of file transfers */
+struct ftran {
+       struct ftran *prev_ftran;
+       struct ftran *next_ftran;
+       struct conn *owner;
+
+       int state;              /*
+                                * 0 = none, 1 = got PASV addr,
+                                * 2 = waiting on PASV socket,  
+                                * 3 = got PORT addr, 4 = waiting for
+                                *     PORT connect, 
+                                * 5 = transferring file (or waiting 
+                                *     for PORT connect)
+                                */
+       struct sockaddr_in sin;
+       int sock;
+       int dir_listing;
+#if WANT_DCACHE
+       struct dcache *dir_cache;
+#endif
+#if WANT_ASCII
+       int ascii_mode;
+#endif
+       char filename[256];
+       time_t tran_start;
+       long int size;
+
+       int local_file;
+       int block_size;
+
+#if HAVE_MMAP
+       char *file_data;        /* mmap'ed */
+#endif
+       long int pos;
+
+#if WANT_UPLOAD
+       int upload;
+       int append;
+#endif
+};
+
+void add_to_linked_list(struct list_element * const first,
+                        struct list_element * const elem);
+void remove_from_linked_list(struct list_element * const elem);
+
+struct conn *alloc_new_conn(const int sock);
+struct ftran *alloc_new_ftran(const int sock, const struct conn * const c);
+#if WANT_DCACHE
+struct dcache *alloc_new_dcache();
+#endif
+
+int add_fd(const int fd, const int events);
+void del_fd(const int fd);
+
+void destroy_conn(struct conn * const c);
+void destroy_ftran(struct ftran * const f);
+#if WANT_DCACHE
+void destroy_dcache(struct dcache * const d);
+#endif
+
+#if HAVE_POLL
+int process_all_clients(const int num_ac);
+int process_all_sendfiles(const int num_ac);
+#else
+int process_all_clients(const fd_set * const active_clients, const int num_ac);
+int process_all_sendfiles(fd_set * const active_clients, const int num_ac);
+#endif
+
+int do_upload(struct ftran *f);
+int do_download(struct ftran *f);
+void write_xferlog(struct ftran *f);
+int main(void);
+
+RETSIGTYPE handle_alarm(int signum);
+
+void accept_new_client(int * const server_sock);
+void time_out_sockets();
+#if WANT_DCACHE
+void time_out_dcache();
+#endif
+
+void remove_bytes(struct conn * const c, const int i);
+void numeric(struct conn * const c, const int numeric, const char * const format, ...);
+void init_file_transfer(struct ftran * const f);
+int create_server_socket();
+
+#if !HAVE_POLL
+void clear_bad_fds(int * const server_sock);
+#endif
+
+#if WANT_MESSAGE
+void dump_file(struct conn * const c, const int num, const char * const filename);
+void list_readmes(struct conn * const c);
+#endif
+
diff --git a/nonroot.c b/nonroot.c
new file mode 100644 (file)
index 0000000..6280557
--- /dev/null
+++ b/nonroot.c
@@ -0,0 +1,251 @@
+/*  nonroot.c: Various database functions for rights and users
+    Copyright (C) 1999-2000 Steinar H. Gunderson
+
+    This program is is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License, version 2 if the
+    License as published by the Free Software Foundation.
+
+    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/*
+ * make autoconf changeable!!!
+ */
+#define USERS_FILE "/home/betaftpd/betaftpd.users"
+#define GROUP_FILE "/home/betaftpd/betaftpd.groups"
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if WANT_NONROOT
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#if HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#include <nonroot.h>
+
+struct users {
+       char username[24];
+       char password[14];
+       char homedir[128];
+       uid_t uid;
+       gid_t gid;
+};
+
+/* we will add cacheing of both users and rights LATER :-) */
+
+int nr_userinfo(const char * const username, int * const uid,
+               char * const homedir, char * const rootdir,
+               const char * const password) 
+{
+       FILE *users_file = fopen(USERS_FILE, "r");
+       char this_username[256];
+       char real_password[256];
+
+       if (users_file == NULL) return 0; /* panic, reject all users */
+
+       /* 
+        * ignores gids atm, we may want to change that in the future
+        */
+       while (!feof(users_file)) {
+               fscanf(users_file, "%d %s %s %s %s %*[^\n]\n", 
+                       uid, this_username, real_password, homedir, rootdir);
+               if (strcmp(this_username, username) != 0) continue;
+
+               fclose(users_file);
+
+               printf("pw = %s\n", real_password);
+               printf("uid = %u\n", *uid);
+               printf("hdir = %s\n", homedir);
+               printf("rdir = %s\nEND\n", rootdir);
+
+               if (strcmp(real_password, crypt(password, real_password)) == 0) {
+                       return 3;
+               } else {
+                       return 0;
+               }
+       }
+       
+       fclose(users_file);
+       return 0;               /* no such user */
+}
+
+/*
+ * Thank goodness for the Unix inventors, who invented all those nice flags! :-)
+ * Reduces my code size a lot :-)
+ */
+int nr_check_permission(const uid_t uid, const char * const object,
+                       const int perm, const int is_dir,
+                       char * const ret_rights)
+{
+       char temp[256];
+
+       if (is_dir) {
+               snprintf(temp, 256, "%s/.rights", object);
+               return nr_intperm(uid, temp, ".", perm, ret_rights);
+       } else {
+               char *ptr;
+
+               snprintf(temp, 256, "%s", object);      /* non-overflow */
+
+               ptr = strrchr(temp, '/');
+               if (ptr == NULL) {
+                       return nr_intperm(uid, "./.rights", temp, perm, ret_rights);
+               } else {
+                       char temp2[256];
+                       ptr[0] = 0;
+
+                       snprintf(temp2, 256, "%s/.rights", temp);
+                       return nr_intperm(uid, temp2, ptr + 1, perm, ret_rights);
+               }
+       }
+}
+
+int nr_intperm(const uid_t uid, const char * const rightfile,
+               const char * const entry, const int perm,
+               char * const ret_rights)
+{
+       FILE *rights = fopen(rightfile, "r");
+       char default_rights[] = "---------";
+       char this_entry[256], these_rights[16], check_rights[16], *ptr;
+       int rights_assigned = 0;
+       uid_t this_uid, check_uid;
+       gid_t this_gid, check_gid;
+
+       printf("Checking permission %u for uid %d, file `%s', entry `%s'\n",
+               perm, uid, rightfile, entry);
+
+       {
+               char buf[256];
+               getcwd(buf, 256);
+               printf("cwd is `%s'\n", buf);
+       }
+
+       if (rights == NULL) perror(rightfile);
+
+       if (ret_rights != NULL) strcpy(ret_rights, default_rights);
+       if (rights == NULL) return -1;  /* no rights file -- no permission */
+
+       while (!feof(rights)) {
+               fscanf(rights, "%s %s %d %d\n", this_entry, these_rights,
+                       &this_uid, &this_gid);
+
+               /*
+                * a bit (a BIT?) ugly, perhaps (PERHAPS?)
+                * note: no typo, it's supposed to be a single `=' sign
+                */
+               if ((strcmp(this_entry, entry) == 0 && (rights_assigned = 1)) ||
+                   (strcmp(this_entry, ".default") == 0 &&
+                     strcmp(this_entry, ".rights")  != 0 &&
+                     rights_assigned == 0)) {
+                       if (ret_rights != NULL) strcpy(ret_rights, these_rights);
+
+                       strcpy(check_rights, these_rights);
+                       check_uid = this_uid;
+                       check_gid = this_gid;
+                       if (rights_assigned == 1) break;
+                       rights_assigned = 1;
+               }
+       }
+
+       if (rights_assigned == 0) puts("no entry! denying...\n");
+       if (rights_assigned == 0) return -1;    /* no entry, no access */
+
+       /* if we're only looking for at entry, return OK now */
+       if (perm == 0) {
+               puts("Only peeking; OK\n");
+               return 0;
+       }
+
+       /* now on to the actual checking... */
+       ptr = check_rights;
+       if (perm != 4) ptr++;           /* check the right bits */
+       if (perm == 1) ptr++;
+
+       printf("Actual rights are `%s', filtered: `%s'\n", check_rights, ptr);
+
+       if (ptr[6] != '-') return 0;                      /* all users */
+       if (uid == check_uid && ptr[0] != '-') return 0;  /* user owner */
+       if (member_of_group(uid, check_gid) && ptr[3] != '-') return 0;
+                                                         /* member of group */
+
+       return -1;              /* no access */
+}
+
+int member_of_group(const uid_t uid, const gid_t gid)
+{
+       printf("For now, `%s' is not member of `%s'\n", 
+               nr_get_uname(uid), nr_get_gname(gid));
+       return 0;
+}
+
+char username[256], groupname[256];    /* static buffer to return */
+
+char *nr_get_uname(const uid_t uid)
+{
+       FILE *users = fopen(USERS_FILE, "r");
+       uid_t uid_this;
+
+       if (users == NULL) return "error";      /* panic */
+
+       while (!feof(users)) {
+               if (fscanf(users, "%d %s %*[^\n]\n", &uid_this, username) < 2) continue;
+               if (uid_this == uid) return username;
+       }
+       snprintf(username, 256, "%d", uid);
+       return username;
+}
+
+char *nr_get_gname(const uid_t gid)
+{
+       FILE *group = fopen(GROUP_FILE, "r");
+       gid_t gid_this;
+
+       if (group == NULL) return "error";      /* panic */
+
+       while (!feof(group)) {
+               if (fscanf(group, "%d %s\n", &gid_this, groupname) < 2) continue;
+               if (gid_this == gid) return groupname;
+       }
+       snprintf(groupname, 256, "%d", gid);
+       return groupname;
+}
+
+#endif /* !WANT_NONROOT */
diff --git a/nonroot.h b/nonroot.h
new file mode 100644 (file)
index 0000000..fd23084
--- /dev/null
+++ b/nonroot.h
@@ -0,0 +1,30 @@
+/*  nonroot.h: Header file for nonroot.c
+    Copyright (C) 1999-2000 Steinar H. Gunderson
+
+    This program is is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License, version 2 if the
+    License as published by the Free Software Foundation.
+
+    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+int nr_userinfo(const char * const username, int * const uid,
+                char * const homedir, char * const rootdir,
+                const char * const password);
+int nr_check_permission(const uid_t uid, const char * const object,
+                       const int perm, const int is_dir,
+                       char * const ret_rights);
+int nr_intperm(const uid_t uid, const char * const rightfile,
+              const char * const entry, const int perm,
+              char * const ret_rights);
+int member_of_group(const uid_t uid, const gid_t gid);
+char *nr_get_uname(const uid_t uid);
+char *nr_get_gname(const gid_t gid);
+
diff --git a/strip-exec b/strip-exec
new file mode 100755 (executable)
index 0000000..fef2b5d
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# strip-exec: Try to build the smallest executable possible from the current
+# source.
+#
+
+#
+# Slack 3.4 standard gcc -- change if you need to, but gcc 2.7.2.3 typically
+# generates much smaller code than egcs/pgcc
+#
+CC="gcc -V2.7.2.3 -bi486-unknown-linux-gnulibc1"
+CFLAGS="-O2 -D__OPTIMIZE_SIZE__ -m486"
+SECTIONS=".note.ABI-tag .gnu.version .rel.got .dtors .comment .note"
+EXEC=betaftpd
+
+rm -f *.s
+
+#
+# Warning
+#
+echo "***"
+echo "*** WARNING: This script is intended for advanced users only, and"
+echo "*** is highly experimental. It will most likely fail -- please do"
+echo "*** not complain if it does."
+echo "***"
+
+#
+# Compile
+#
+make distclean
+CC="$CC" CFLAGS="$CFLAGS" ./configure --enable-dcache
+make assembly-files
+
+#
+# -malign-* doesn't remove _all_ .align occurrences :-)
+#
+for FILE in *.s; do
+       echo "Removing alignment from $FILE..."
+       grep -v .align < $FILE > `basename $FILE .s`2.s
+       mv `basename $FILE .s`2.s $FILE
+done
+
+make betaftpd-from-assembly-files
+strip --strip-all $EXEC
+
+for SECTION in $SECTIONS; do
+       echo Stripping $SECTION...
+       strip --remove-section=$SECTION $EXEC
+done
+
+upx --best betaftpd
+ls -l betaftpd
+