--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+/* 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@
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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);
--- /dev/null
+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
--- /dev/null
+/* 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';
+}
--- /dev/null
+/* 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);
+
--- /dev/null
+/* 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
+
--- /dev/null
+#! /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
+
--- /dev/null
+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)
--- /dev/null
+/* 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
--- /dev/null
+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)
--- /dev/null
+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.
--- /dev/null
+ 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.
--- /dev/null
+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.
--- /dev/null
+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 :-)
--- /dev/null
+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 */
--- /dev/null
+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 :-)
+
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+/* 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
+
--- /dev/null
+/* 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
+
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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);
+
--- /dev/null
+#
+# 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
+