Module Name:    src
Committed By:   snj
Date:           Fri Apr 15 19:38:13 UTC 2016

Modified Files:
        src/libexec/httpd [netbsd-6-0]: CHANGES Makefile auth-bozo.c
            bozohttpd.8 bozohttpd.c bozohttpd.h cgi-bozo.c content-bozo.c
            daemon-bozo.c dir-index-bozo.c lua-bozo.c main.c printenv.lua
            ssl-bozo.c tilde-luzah-bozo.c
        src/libexec/httpd/lua [netbsd-6-0]: bozo.lua glue.c
        src/libexec/httpd/testsuite [netbsd-6-0]: Makefile t10.out test-bigfile

Log Message:
Pull up following revision(s) (requested by mrg in ticket #1377):
        libexec/httpd/CHANGES: up to 1.22
        libexec/httpd/Makefile: up to 1.26 via patch
        libexec/httpd/auth-bozo.c: up to 1.18
        libexec/httpd/bozohttpd.8: up to 1.59
        libexec/httpd/bozohttpd.c: up to 1.80 via patch
        libexec/httpd/bozohttpd.h: up to 1.45
        libexec/httpd/cgi-bozo.c: up to 1.33
        libexec/httpd/content-bozo.c: up to 1.13
        libexec/httpd/daemon-bozo.c: up to 1.17
        libexec/httpd/dir-index-bozo.c: up to 1.25
        libexec/httpd/lua-bozo.c: up to 1.14
        libexec/httpd/lua/bozo.lua: up to 1.2
        libexec/httpd/lua/glue.c: up to 1.2
        libexec/httpd/main.c: up to 1.13
        libexec/httpd/printenv.lua: up to 1.3
        libexec/httpd/ssl-bozo.c: up to 1.22
        libexec/httpd/testsuite/Makefile: up to 1.5
        libexec/httpd/testsuite/t10.out: up to 1.2
        libexec/httpd/testsuite/test-bigfile: up to 1.2
        libexec/httpd/tilde-luzah-bozo.c: up to 1.14
Import bozohttpd 20151028:
o  add CGI support for ~user translation (-E switch)
o  add redirects to ~user translation
o  fix bugs around ~user translation
o  add schema detection for absolute redirects
o  fixed few memory leaks
o  bunch of minor tweaks
o  removed -r support
o  smarter redirects
--
Changes in 20150320:
o  fix redirection handling
o  support transport stream (.ts) and video object (.vob) files
o  directory listings show correct file sizes for large files
--
updates and bozohttpd 20160415:
o  add search-word support for CGI
o  fix a security issue in CGI suffix handler support which would
   allow remote code execution, from [email protected]
o  -C option supports now CGI scripts only


To generate a diff of this commit:
cvs rdiff -u -r1.11.6.1 -r1.11.6.2 src/libexec/httpd/CHANGES
cvs rdiff -u -r1.12.14.1 -r1.12.14.2 src/libexec/httpd/Makefile
cvs rdiff -u -r1.10.8.1 -r1.10.8.2 src/libexec/httpd/auth-bozo.c
cvs rdiff -u -r1.32.6.1 -r1.32.6.2 src/libexec/httpd/bozohttpd.8
cvs rdiff -u -r1.30.6.1 -r1.30.6.2 src/libexec/httpd/bozohttpd.c
cvs rdiff -u -r1.20.6.1 -r1.20.6.2 src/libexec/httpd/bozohttpd.h
cvs rdiff -u -r1.20.8.1 -r1.20.8.2 src/libexec/httpd/cgi-bozo.c
cvs rdiff -u -r1.7.8.1 -r1.7.8.2 src/libexec/httpd/content-bozo.c
cvs rdiff -u -r1.15.8.1 -r1.15.8.2 src/libexec/httpd/daemon-bozo.c
cvs rdiff -u -r1.13.2.1.4.1 -r1.13.2.1.4.2 src/libexec/httpd/dir-index-bozo.c
cvs rdiff -u -r1.9.10.2 -r1.9.10.3 src/libexec/httpd/lua-bozo.c
cvs rdiff -u -r1.5.8.1 -r1.5.8.2 src/libexec/httpd/main.c
cvs rdiff -u -r1.2.10.2 -r1.2.10.3 src/libexec/httpd/printenv.lua
cvs rdiff -u -r1.13.6.1 -r1.13.6.2 src/libexec/httpd/ssl-bozo.c
cvs rdiff -u -r1.9.8.1 -r1.9.8.2 src/libexec/httpd/tilde-luzah-bozo.c
cvs rdiff -u -r1.1.1.1 -r1.1.1.1.14.1 src/libexec/httpd/lua/bozo.lua \
    src/libexec/httpd/lua/glue.c
cvs rdiff -u -r1.4 -r1.4.14.1 src/libexec/httpd/testsuite/Makefile
cvs rdiff -u -r1.1.1.1 -r1.1.1.1.16.1 src/libexec/httpd/testsuite/t10.out
cvs rdiff -u -r1.1.1.1 -r1.1.1.1.14.1 \
    src/libexec/httpd/testsuite/test-bigfile

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/libexec/httpd/CHANGES
diff -u src/libexec/httpd/CHANGES:1.11.6.1 src/libexec/httpd/CHANGES:1.11.6.2
--- src/libexec/httpd/CHANGES:1.11.6.1	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/CHANGES	Fri Apr 15 19:38:13 2016
@@ -1,5 +1,29 @@
 $eterna: CHANGES,v 1.78 2011/11/18 01:25:11 mrg Exp $
 
+changes in bozohttpd 20160415:
+	o  add search-word support for CGI
+	o  fix a security issue in CGI suffix handler support which would
+	   allow remote code execution, from [email protected]
+	o  -C option supports now CGI scripts only
+
+changes in bozohttpd 20151028:
+	o  add CGI support for ~user translation (-E switch)
+	o  add redirects to ~user translation
+	o  fix bugs around ~user translation
+	o  add schema detection for absolute redirects
+	o  fixed few memory leaks
+	o  bunch of minor tweaks
+	o  removed -r support
+	o  smarter redirects 
+
+changes in bozohttpd 20150320:
+	o  fix redirection handling
+	o  support transport stream (.ts) and video object (.vob) files
+	o  directory listings show correct file sizes for large files
+
+changes in bozohttpd 20140717:
+	o  properly handle SSL errors
+
 changes in bozohttpd 20140708:
 	o  fixes for virtual host support, from [email protected]
 	o  avoid printing double errors, from [email protected]

Index: src/libexec/httpd/Makefile
diff -u src/libexec/httpd/Makefile:1.12.14.1 src/libexec/httpd/Makefile:1.12.14.2
--- src/libexec/httpd/Makefile:1.12.14.1	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/Makefile	Fri Apr 15 19:38:13 2016
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.12.14.1 2014/07/09 09:47:10 msaitoh Exp $
+#	$NetBSD: Makefile,v 1.12.14.2 2016/04/15 19:38:13 snj Exp $
 #
 #	$eterna: Makefile,v 1.30 2010/07/11 00:34:27 mrg Exp $
 #
@@ -15,6 +15,10 @@
 #	DO_HTPASSWD		/* support .htpasswd files */
 #	NO_LUA_SUPPORT		/* don't support Lua for dynamic content */
 #
+# other system specific defines:
+#	HAVE_NBUTIL_H		/* netbsd compat is in <nbutil.h>
+#				(don't forget to also enable -lnbutil)
+#
 # these are usually set via the "COPTS" variable, or some other method
 # for setting CFLAGS relevant to your make, eg
 #   % make COPTS="-DDO_HTPASSWD"
@@ -32,6 +36,17 @@ DPADD=	${LIBCRYPT} ${LIBLUA} ${LIBM}
 
 WARNS?=	4
 
+.if defined(.OS.MAKE)
+OPSYS=	${.OS.MAKE}
+.else
+OPSYS:=	${:!uname -s!:S/-//g:S/\///g}
+.endif
+
+.if ${OPSYS} == "QNX"
+CPPFLAGS+=	-DHAVE_NBUTIL_H
+LDADD+=		-lnbutil
+.endif
+
 .include <bsd.own.mk>
 
 .if ${MKCRYPTO} != "no"

Index: src/libexec/httpd/auth-bozo.c
diff -u src/libexec/httpd/auth-bozo.c:1.10.8.1 src/libexec/httpd/auth-bozo.c:1.10.8.2
--- src/libexec/httpd/auth-bozo.c:1.10.8.1	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/auth-bozo.c	Fri Apr 15 19:38:13 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: auth-bozo.c,v 1.10.8.1 2014/07/09 09:47:10 msaitoh Exp $	*/
+/*	$NetBSD: auth-bozo.c,v 1.10.8.2 2016/04/15 19:38:13 snj Exp $	*/
 
 /*	$eterna: auth-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -72,10 +72,10 @@ bozo_auth_check(bozo_httpreq_t *request,
 		if (bozo_check_special_files(request, basename))
 			return 1;
 	}
-	request->hr_authrealm = bozostrdup(httpd, dir);
+	request->hr_authrealm = bozostrdup(httpd, request, dir);
 
-	if ((size_t)snprintf(authfile, sizeof(authfile), "%s/%s", dir, AUTH_FILE) >= 
-	  sizeof(authfile)) {
+	if ((size_t)snprintf(authfile, sizeof(authfile), "%s/%s", dir,
+			     AUTH_FILE) >= sizeof(authfile)) {
 		return bozo_http_error(httpd, 404, request,
 			"authfile path too long");
 	}
@@ -118,6 +118,13 @@ bozo_auth_check(bozo_httpreq_t *request,
 }
 
 void
+bozo_auth_init(bozo_httpreq_t *request)
+{
+	request->hr_authuser = NULL;
+	request->hr_authpass = NULL;
+}
+
+void
 bozo_auth_cleanup(bozo_httpreq_t *request)
 {
 
@@ -129,7 +136,8 @@ bozo_auth_cleanup(bozo_httpreq_t *reques
 }
 
 int
-bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str, ssize_t len)
+bozo_auth_check_headers(bozo_httpreq_t *request, char *val, char *str,
+			ssize_t len)
 {
 	bozohttpd_t *httpd = request->hr_httpd;
 
@@ -150,8 +158,10 @@ bozo_auth_check_headers(bozo_httpreq_t *
 			return bozo_http_error(httpd, 400, request,
 			    "bad authorization field");
 		*pass++ = '\0';
-		request->hr_authuser = bozostrdup(httpd, authbuf);
-		request->hr_authpass = bozostrdup(httpd, pass);
+		free(request->hr_authuser);
+		free(request->hr_authpass);
+		request->hr_authuser = bozostrdup(httpd, request, authbuf);
+		request->hr_authpass = bozostrdup(httpd, request, pass);
 		debug((httpd, DEBUG_FAT,
 		    "decoded authorization `%s' as `%s':`%s'",
 		    str, request->hr_authuser, request->hr_authpass));
@@ -181,8 +191,8 @@ bozo_auth_check_401(bozo_httpreq_t *requ
 	if (code == 401)
 		bozo_printf(httpd,
 			"WWW-Authenticate: Basic realm=\"%s\"\r\n",
-			(request && request->hr_authrealm) ?
-				request->hr_authrealm : "default realm");
+			request->hr_authrealm ?
+			request->hr_authrealm : "default realm");
 }
 
 #ifndef NO_CGIBIN_SUPPORT
@@ -229,6 +239,12 @@ base64_decode(const unsigned char *in, s
 	unsigned char *cp;
 	size_t	 i;
 
+	if (ilen == 0) {
+		if (olen)
+			*out = '\0';
+		return 0;
+	}
+
 	cp = out;
 	for (i = 0; i < ilen; i += 4) {
 		if (cp + 3 > out + olen)
@@ -250,7 +266,7 @@ base64_decode(const unsigned char *in, s
 			| decodetable[in[i + 3]];
 #undef IN_CHECK
 	}
-	while (in[i - 1] == '=')
+	while (i > 0 && in[i - 1] == '=')
 		cp--,i--;
 	return (cp - out);
 }

Index: src/libexec/httpd/bozohttpd.8
diff -u src/libexec/httpd/bozohttpd.8:1.32.6.1 src/libexec/httpd/bozohttpd.8:1.32.6.2
--- src/libexec/httpd/bozohttpd.8:1.32.6.1	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/bozohttpd.8	Fri Apr 15 19:38:13 2016
@@ -1,8 +1,8 @@
-.\"	$NetBSD: bozohttpd.8,v 1.32.6.1 2014/07/09 09:47:10 msaitoh Exp $
+.\"	$NetBSD: bozohttpd.8,v 1.32.6.2 2016/04/15 19:38:13 snj Exp $
 .\"
 .\"	$eterna: bozohttpd.8,v 1.101 2011/11/18 01:25:11 mrg Exp $
 .\"
-.\" Copyright (c) 1997-2014 Matthew R. Green
+.\" Copyright (c) 1997-2015 Matthew R. Green
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -26,21 +26,22 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd February 9, 2014
-.Dt HTTPD 8
+.Dd December 12, 2015
+.Dt BOZOHTTPD 8
 .Os
 .Sh NAME
-.Nm httpd
+.Nm bozohttpd
 .Nd hyper text transfer protocol version 1.1 daemon
 .Sh SYNOPSIS
 .Nm
-.Op Fl CIMPSZciptvx
+.Op Fl EHVXefhnsu
 .Op Fl C Ar suffix cgihandler
 .Op Fl I Ar port
 .Op Fl L Ar prefix script
 .Op Fl M Ar suffix type encoding encoding11
 .Op Fl P Ar pidfile
 .Op Fl S Ar server_software
+.Op Fl U Ar username
 .Op Fl Z Ar cert privkey
 .Op Fl c Ar cgibin
 .Op Fl i Ar address
@@ -48,6 +49,7 @@
 .Op Fl t Ar chrootdir
 .Op Fl v Ar virtualroot
 .Op Fl x Ar index
+.Op Fl z Ar ciphers
 .Ar slashdir
 .Op Ar myname
 .Sh DESCRIPTION
@@ -111,9 +113,11 @@ is a valid CGI program in the
 directory.
 In other words, all CGI URL's must begin with
 .Em \%/cgi-bin/ .
-Note that the CGI/1.1 interface is not available with
+Note that the CGI/1.1 interface is available with
 .Em ~user
-translation.
+translation using
+.Fl E
+switch.
 .It Fl e
 Causes
 .Nm
@@ -223,15 +227,6 @@ translations from
 .Dq public_html
 to
 .Ar pubdir .
-.It Fl r
-Forces pages besides the
-.Dq index.html
-(see the
-.Fl X
-option) page to require that the Referrer: header be present and
-refer to this web server, otherwise a redirect to the
-.Dq index.html
-page will be returned instead.
 .It Fl S Ar server_software
 Sets the internal server version to
 .Ar server_software .
@@ -270,6 +265,12 @@ into the directory
 (but see the
 .Fl p
 option above).
+.It Fl E
+Enables CGI/1.1 interface for
+.Em ~user
+translation.
+Note that enabling this support implies that users can run
+commands as web server user, this may have security implications.
 .It Fl V
 Sets the default virtual host directory to
 .Ar slashdir .
@@ -303,6 +304,9 @@ Changes the default file read for direct
 .Dq index.html
 to
 .Ar index .
+.It Fl z Ar ciphers
+Sets the list of SSL ciphers (see
+.Xr SSL_CTX_set_cipher_list 3 ) .
 .It Fl Z Ar certificate_path privatekey_path
 Sets the path to the server certificate file and the private key file
 in pem format.
@@ -333,7 +337,8 @@ by default to process incoming TCP conne
 option),
 .Nm
 has little internal networking knowledge.
-(Indeed, you can run it on the command line with little change of functionality.)
+(Indeed, you can run it on the command line with little change of
+functionality.)
 A typical
 .Xr inetd.conf 5
 entry would be:
@@ -425,7 +430,7 @@ It may require linking with the crypt li
 .Dq -lcrypt .
 .Ss SSL SUPPORT
 .Nm
-has support for SSLv2, SSLv3, and TLSv1 protocols that is included by
+has support for TLSv1.1 and TLSv1.2 protocols that are included by
 default.
 It requires linking with the crypto and ssl library, using
 .Dq -lcrypto -lssl .
@@ -462,12 +467,23 @@ symbolic link is found,
 .Nm
 will perform a smart redirect to the target of this symlink.
 The target is assumed to live on the same server.
+If target starts with slash then absolute redirection is performed,
+otherwise it's handled as relative.
 If a
 .Pa .bzabsredirect
 symbolic link is found,
 .Nm
 will redirect to the absolute url pointed to by this symlink.
 This is useful to redirect to different servers.
+Two forms of redirection are supported - symbolic link without schema will use
+.Em http://
+as default i.e. link to
+.Em NetBSD.org
+will redirect to
+.Em http://NetBSD.org/
+Otherwise provided schema will be used i.e. symbolic link to
+.Em ftp://NetBSD.org/
+will redirect to provided the URL.
 .Sh EXAMPLES
 To configure set of virtual hosts, one would use an
 .Xr inetd.conf 5
@@ -491,17 +507,14 @@ with PHP, one must use the
 option to specify a CGI handler for a particular file type.
 Typically this will be like:
 .Bd -literal
-httpd -C .php /usr/pkg/bin/php /var/www
+httpd -C .php /usr/pkg/bin/php-cgi /var/www
 .Ed
 .Sh SEE ALSO
 .Xr inetd.conf 5 ,
 .Xr inetd 8
 .Sh HISTORY
-The
 .Nm
-program is actually called
-.Dq bozohttpd .
-It was first written in perl, based on another perl http server
+was first written in perl, based on another perl http server
 called
 .Dq tinyhttpd .
 It was then rewritten from scratch in perl, and then once again in C.
@@ -513,7 +526,7 @@ The focus has always been simplicity and
 and regular code audits.
 This manual documents
 .Nm
-version 20140201.
+version 20150501.
 .Sh AUTHORS
 .An -nosplit
 .Nm
@@ -546,8 +559,9 @@ provided cgi-bin support fixes, and more
 .It
 .An Alistair G. Crooks
 .Aq Mt [email protected]
-cleaned up many internal interfaces, made bozohttpd linkable as a
-library and provided the Lua binding.
+cleaned up many internal interfaces, made
+.Nm
+linkable as a library and provided the Lua binding.
 .It
 .An DEGROOTE Arnaud
 .Aq Mt [email protected]
@@ -597,6 +611,13 @@ provided fixes for HTTP basic authorisat
 .Aq Mt [email protected]
 provided many fixes and enhancements for the man page
 .It
+.An Mateusz Kocielski
+.Aq Mt [email protected]
+fixed memory leaks, various issues with userdir support,
+information disclosure issues, added support for using CGI handlers
+with directory indexing, found several security issues and provided
+various other fixes.
+.It
 .An Arnaud Lacombe
 .Aq Mt [email protected]
 provided some clean up for memory leaks
@@ -609,7 +630,7 @@ provided man page fixes
 .Aq Mt [email protected]
 Added the
 .Fl P
-option.
+option (pidfile support) and provided some man page fixes.
 .It
 .An Luke Mewburn
 .Aq Mt [email protected]
@@ -662,9 +683,6 @@ provided http authorisation fixes
 provided chroot and change-to-user support, and other various fixes
 .It
 Coyote Point provided various CGI fixes.
-.It
-.An Julio Merino
-added pidfile support and provided some man page fixes.
 .El
 .Pp
 There are probably others I have forgotten (let me know if you care)

Index: src/libexec/httpd/bozohttpd.c
diff -u src/libexec/httpd/bozohttpd.c:1.30.6.1 src/libexec/httpd/bozohttpd.c:1.30.6.2
--- src/libexec/httpd/bozohttpd.c:1.30.6.1	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/bozohttpd.c	Fri Apr 15 19:38:13 2016
@@ -1,9 +1,9 @@
-/*	$NetBSD: bozohttpd.c,v 1.30.6.1 2014/07/09 09:47:10 msaitoh Exp $	*/
+/*	$NetBSD: bozohttpd.c,v 1.30.6.2 2016/04/15 19:38:13 snj Exp $	*/
 
 /*	$eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2014 Matthew R. Green
+ * Copyright (c) 1997-2015 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -109,7 +109,7 @@
 #define INDEX_HTML		"index.html"
 #endif
 #ifndef SERVER_SOFTWARE
-#define SERVER_SOFTWARE		"bozohttpd/20140708"
+#define SERVER_SOFTWARE		"bozohttpd/20160415"
 #endif
 #ifndef DIRECT_ACCESS_FILE
 #define DIRECT_ACCESS_FILE	".bzdirect"
@@ -171,61 +171,59 @@ volatile sig_atomic_t	alarmhit;
  * check there's enough space in the prefs and names arrays.
  */
 static int
-size_arrays(bozoprefs_t *bozoprefs, unsigned needed)
+size_arrays(bozoprefs_t *bozoprefs, size_t needed)
 {
 	char	**temp;
 
 	if (bozoprefs->size == 0) {
 		/* only get here first time around */
-		bozoprefs->size = needed;
-		if ((bozoprefs->name = calloc(sizeof(char *), needed)) == NULL) {
-			(void) fprintf(stderr, "size_arrays: bad alloc\n");
+		bozoprefs->name = calloc(sizeof(char *), needed);
+		if (bozoprefs->name == NULL)
 			return 0;
-		}
-		if ((bozoprefs->value = calloc(sizeof(char *), needed)) == NULL) {
+		bozoprefs->value = calloc(sizeof(char *), needed);
+		if (bozoprefs->value == NULL) {
 			free(bozoprefs->name);
-			(void) fprintf(stderr, "size_arrays: bad alloc\n");
 			return 0;
 		}
-	} else if (bozoprefs->c == bozoprefs->size) {
+		bozoprefs->size = needed;
+	} else if (bozoprefs->count == bozoprefs->size) {
 		/* only uses 'needed' when filled array */
-		bozoprefs->size += needed;
 		temp = realloc(bozoprefs->name, sizeof(char *) * needed);
-		if (temp == NULL) {
-			(void) fprintf(stderr, "size_arrays: bad alloc\n");
+		if (temp == NULL)
 			return 0;
-		}
 		bozoprefs->name = temp;
 		temp = realloc(bozoprefs->value, sizeof(char *) * needed);
-		if (temp == NULL) {
-			(void) fprintf(stderr, "size_arrays: bad alloc\n");
+		if (temp == NULL)
 			return 0;
-		}
 		bozoprefs->value = temp;
+		bozoprefs->size += needed;
 	}
 	return 1;
 }
 
-static int
+static ssize_t
 findvar(bozoprefs_t *bozoprefs, const char *name)
 {
-	unsigned	i;
+	size_t	i;
 
-	for (i = 0 ; i < bozoprefs->c && strcmp(bozoprefs->name[i], name) != 0; i++)
-		;
-	return (i == bozoprefs->c) ? -1 : (int)i;
+	for (i = 0; i < bozoprefs->count; i++)
+		if (strcmp(bozoprefs->name[i], name) == 0)
+			return (ssize_t)i;
+	return -1;
 }
 
 int
-bozo_set_pref(bozoprefs_t *bozoprefs, const char *name, const char *value)
+bozo_set_pref(bozohttpd_t *httpd, bozoprefs_t *bozoprefs,
+	      const char *name, const char *value)
 {
-	int	i;
+	ssize_t	i;
 
 	if ((i = findvar(bozoprefs, name)) < 0) {
 		/* add the element to the array */
-		if (size_arrays(bozoprefs, bozoprefs->size + 15)) {
-			bozoprefs->name[i = bozoprefs->c++] = strdup(name);
-		}
+		if (!size_arrays(bozoprefs, bozoprefs->size + 15))
+			return 0;
+		i = bozoprefs->count++;
+		bozoprefs->name[i] = bozostrdup(httpd, NULL, name);
 	} else {
 		/* replace the element in the array */
 		if (bozoprefs->value[i]) {
@@ -233,8 +231,7 @@ bozo_set_pref(bozoprefs_t *bozoprefs, co
 			bozoprefs->value[i] = NULL;
 		}
 	}
-	/* sanity checks for range of values go here */
-	bozoprefs->value[i] = strdup(value);
+	bozoprefs->value[i] = bozostrdup(httpd, NULL, value);
 	return 1;
 }
 
@@ -244,10 +241,10 @@ bozo_set_pref(bozoprefs_t *bozoprefs, co
 char *
 bozo_get_pref(bozoprefs_t *bozoprefs, const char *name)
 {
-	int	i;
+	ssize_t	i;
 
-	return ((i = findvar(bozoprefs, name)) < 0) ? NULL :
-			bozoprefs->value[i];
+	i = findvar(bozoprefs, name);
+	return i < 0 ? NULL : bozoprefs->value[i];
 }
 
 char *
@@ -309,9 +306,9 @@ parse_request(bozohttpd_t *httpd, char *
 	}
 
 	/* allocate private copies */
-	*file = bozostrdup(httpd, *file);
+	*file = bozostrdup(httpd, NULL, *file);
 	if (*query)
-		*query = bozostrdup(httpd, *query);
+		*query = bozostrdup(httpd, NULL, *query);
 
 	debug((httpd, DEBUG_FAT,
 		"url: method: \"%s\" file: \"%s\" query: \"%s\" proto: \"%s\"",
@@ -341,6 +338,7 @@ bozo_clean_request(bozo_httpreq_t *reque
 	free(request->hr_oldfile);
 	free(request->hr_query);
 	free(request->hr_host);
+	bozo_user_free(request->hr_user);
 	bozo_auth_cleanup(request);
 	for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr;
 	    hdr = SIMPLEQ_NEXT(hdr, h_next)) {
@@ -350,6 +348,15 @@ bozo_clean_request(bozo_httpreq_t *reque
 		ohdr = hdr;
 	}
 	free(ohdr);
+	ohdr = NULL;
+	for (hdr = SIMPLEQ_FIRST(&request->hr_replheaders); hdr;
+	    hdr = SIMPLEQ_NEXT(hdr, h_next)) {
+		free(hdr->h_value);
+		free(hdr->h_header);
+		free(ohdr);
+		ohdr = hdr;
+	}
+	free(ohdr);
 
 	free(request);
 }
@@ -365,49 +372,76 @@ alarmer(int sig)
 }
 
 /*
+ * a list of header quirks: currently, a list of headers that
+ * can't be folded into a single line.
+ */
+const char *header_quirks[] = { "WWW-Authenticate", NULL };
+
+/*
  * add or merge this header (val: str) into the requests list
  */
 static bozoheaders_t *
-addmerge_header(bozo_httpreq_t *request, char *val,
-		char *str, ssize_t len)
+addmerge_header(bozo_httpreq_t *request, struct qheaders *headers,
+		const char *val, const char *str, ssize_t len)
 {
-	struct	bozoheaders *hdr;
+	struct	bozohttpd_t *httpd = request->hr_httpd;
+	struct bozoheaders	 *hdr = NULL;
+	const char		**quirk;
 
 	USE_ARG(len);
-	/* do we exist already? */
-	SIMPLEQ_FOREACH(hdr, &request->hr_headers, h_next) {
-		if (strcasecmp(val, hdr->h_header) == 0)
+	for (quirk = header_quirks; *quirk; quirk++)
+		if (strcasecmp(*quirk, val) == 0)
 			break;
+
+	if (*quirk == NULL) {
+		/* do we exist already? */
+		SIMPLEQ_FOREACH(hdr, headers, h_next) {
+			if (strcasecmp(val, hdr->h_header) == 0)
+				break;
+		}
 	}
 
 	if (hdr) {
 		/* yup, merge it in */
 		char *nval;
 
-		if (asprintf(&nval, "%s, %s", hdr->h_value, str) == -1) {
-			(void)bozo_http_error(request->hr_httpd, 500, NULL,
-			     "memory allocation failure");
-			return NULL;
-		}
+		bozoasprintf(httpd, &nval, "%s, %s", hdr->h_value, str);
 		free(hdr->h_value);
 		hdr->h_value = nval;
 	} else {
 		/* nope, create a new one */
 
-		hdr = bozomalloc(request->hr_httpd, sizeof *hdr);
-		hdr->h_header = bozostrdup(request->hr_httpd, val);
+		hdr = bozomalloc(httpd, sizeof *hdr);
+		hdr->h_header = bozostrdup(httpd, request, val);
 		if (str && *str)
-			hdr->h_value = bozostrdup(request->hr_httpd, str);
+			hdr->h_value = bozostrdup(httpd, request, str);
 		else
-			hdr->h_value = bozostrdup(request->hr_httpd, " ");
+			hdr->h_value = bozostrdup(httpd, request, " ");
 
-		SIMPLEQ_INSERT_TAIL(&request->hr_headers, hdr, h_next);
+		SIMPLEQ_INSERT_TAIL(headers, hdr, h_next);
 		request->hr_nheaders++;
 	}
 
 	return hdr;
 }
 
+bozoheaders_t *
+addmerge_reqheader(bozo_httpreq_t *request, const char *val, const char *str,
+		   ssize_t len)
+{
+
+	return addmerge_header(request, &request->hr_headers, val, str, len);
+}
+
+bozoheaders_t *
+addmerge_replheader(bozo_httpreq_t *request, const char *val, const char *str,
+		    ssize_t len)
+{
+
+	return addmerge_header(request, &request->hr_replheaders,
+	    val, str, len);
+}
+
 /*
  * as the prototype string is not constant (eg, "HTTP/1.1" is equivalent
  * to "HTTP/001.01"), we MUST parse this.
@@ -415,13 +449,14 @@ addmerge_header(bozo_httpreq_t *request,
 static int
 process_proto(bozo_httpreq_t *request, const char *proto)
 {
+	struct	bozohttpd_t *httpd = request->hr_httpd;
 	char	majorstr[16], *minorstr;
 	int	majorint, minorint;
 
 	if (proto == NULL) {
 got_proto_09:
-		request->hr_proto = request->hr_httpd->consts.http_09;
-		debug((request->hr_httpd, DEBUG_FAT, "request %s is http/0.9",
+		request->hr_proto = httpd->consts.http_09;
+		debug((httpd, DEBUG_FAT, "request %s is http/0.9",
 			request->hr_file));
 		return 0;
 	}
@@ -445,25 +480,25 @@ got_proto_09:
 		goto got_proto_09;
 	case 1:
 		if (minorint == 0)
-			request->hr_proto = request->hr_httpd->consts.http_10;
+			request->hr_proto = httpd->consts.http_10;
 		else if (minorint == 1)
-			request->hr_proto = request->hr_httpd->consts.http_11;
+			request->hr_proto = httpd->consts.http_11;
 		else
 			break;
 
-		debug((request->hr_httpd, DEBUG_FAT, "request %s is %s",
+		debug((httpd, DEBUG_FAT, "request %s is %s",
 		    request->hr_file, request->hr_proto));
 		SIMPLEQ_INIT(&request->hr_headers);
 		request->hr_nheaders = 0;
 		return 0;
 	}
 bad:
-	return bozo_http_error(request->hr_httpd, 404, NULL, "unknown prototype");
+	return bozo_http_error(httpd, 404, NULL, "unknown prototype");
 }
 
 /*
  * process each type of HTTP method, setting this HTTP requests
- # method type.
+ * method type.
  */
 static struct method_map {
 	const char *name;
@@ -485,9 +520,10 @@ static struct method_map {
 static int
 process_method(bozo_httpreq_t *request, const char *method)
 {
+	struct	bozohttpd_t *httpd = request->hr_httpd;
 	struct	method_map *mmp;
 
-	if (request->hr_proto == request->hr_httpd->consts.http_11)
+	if (request->hr_proto == httpd->consts.http_11)
 		request->hr_allow = "GET, HEAD, POST";
 
 	for (mmp = method_map; mmp->name; mmp++)
@@ -497,7 +533,7 @@ process_method(bozo_httpreq_t *request, 
 			return 0;
 		}
 
-	return bozo_http_error(request->hr_httpd, 404, request, "unknown method");
+	return bozo_http_error(httpd, 404, request, "unknown method");
 }
 
 /*
@@ -527,7 +563,8 @@ bozo_read_request(bozohttpd_t *httpd)
 	 */
 	if (bozo_daemon_fork(httpd))
 		return NULL;
-	bozo_ssl_accept(httpd);
+	if (bozo_ssl_accept(httpd))
+		return NULL;
 
 	request = bozomalloc(httpd, sizeof(*request));
 	memset(request, 0, sizeof(*request));
@@ -540,6 +577,8 @@ bozo_read_request(bozohttpd_t *httpd)
 	request->hr_virthostname = NULL;
 	request->hr_file = NULL;
 	request->hr_oldfile = NULL;
+	SIMPLEQ_INIT(&request->hr_replheaders);
+	bozo_auth_init(request);
 
 	slen = sizeof(ss);
 	if (getpeername(0, (struct sockaddr *)(void *)&ss, &slen) < 0)
@@ -558,9 +597,9 @@ bozo_read_request(bozohttpd_t *httpd)
 			host = NULL;
 	}
 	if (host != NULL)
-		request->hr_remotehost = bozostrdup(request->hr_httpd, host);
+		request->hr_remotehost = bozostrdup(httpd, request, host);
 	if (addr != NULL)
-		request->hr_remoteaddr = bozostrdup(request->hr_httpd, addr);
+		request->hr_remoteaddr = bozostrdup(httpd, request, addr);
 	slen = sizeof(ss);
 
 	/*
@@ -576,15 +615,16 @@ bozo_read_request(bozohttpd_t *httpd)
 		if (getsockname(0, (struct sockaddr *)(void *)&ss, &slen) < 0)
 			port = NULL;
 		else {
-			if (getnameinfo((struct sockaddr *)(void *)&ss, slen, NULL, 0,
-					bufport, sizeof bufport, NI_NUMERICSERV) == 0)
+			if (getnameinfo((struct sockaddr *)(void *)&ss, slen,
+					NULL, 0, bufport, sizeof bufport,
+					NI_NUMERICSERV) == 0)
 				port = bufport;
 			else
 				port = NULL;
 		}
 	}
 	if (port != NULL)
-		request->hr_serverport = bozostrdup(request->hr_httpd, port);
+		request->hr_serverport = bozostrdup(httpd, request, port);
 
 	/*
 	 * setup a timer to make sure the request is not hung
@@ -593,7 +633,7 @@ bozo_read_request(bozohttpd_t *httpd)
 	sigemptyset(&sa.sa_mask);
 	sigaddset(&sa.sa_mask, SIGALRM);
 	sa.sa_flags = 0;
-	sigaction(SIGALRM, &sa, NULL);	/* XXX */
+	sigaction(SIGALRM, &sa, NULL);
 
 	alarm(MAX_WAIT_TIME);
 	while ((str = bozodgetln(httpd, STDIN_FILENO, &len, bozo_read)) != NULL) {
@@ -612,11 +652,11 @@ bozo_read_request(bozohttpd_t *httpd)
 						"null method");
 				goto cleanup;
 			}
-
-			bozo_warn(httpd, "got request ``%s'' from host %s to port %s",
-				str,
-				host ? host : addr ? addr : "<local>",
-				port ? port : "<stdin>");
+			bozowarn(httpd,
+				  "got request ``%s'' from host %s to port %s",
+				  str,
+				  host ? host : addr ? addr : "<local>",
+				  port ? port : "<stdin>");
 
 			/* we allocate return space in file and query only */
 			parse_request(httpd, str, &method, &file, &query, &proto);
@@ -673,14 +713,15 @@ bozo_read_request(bozohttpd_t *httpd)
 			if (bozo_auth_check_headers(request, val, str, len))
 				goto next_header;
 
-			hdr = addmerge_header(request, val, str, len);
+			hdr = addmerge_reqheader(request, val, str, len);
 
 			if (strcasecmp(hdr->h_header, "content-type") == 0)
 				request->hr_content_type = hdr->h_value;
 			else if (strcasecmp(hdr->h_header, "content-length") == 0)
 				request->hr_content_length = hdr->h_value;
 			else if (strcasecmp(hdr->h_header, "host") == 0)
-				request->hr_host = bozostrdup(httpd, hdr->h_value);
+				request->hr_host = bozostrdup(httpd, request,
+							      hdr->h_value);
 			/* RFC 2616 (HTTP/1.1): 14.20 */
 			else if (strcasecmp(hdr->h_header, "expect") == 0) {
 				(void)bozo_http_error(httpd, 417, request,
@@ -795,7 +836,7 @@ mmap_and_write_part(bozohttpd_t *httpd, 
 
 	addr = mmap(0, mappedsz, PROT_READ, MAP_SHARED, fd, mappedoffset);
 	if (addr == (char *)-1) {
-		bozo_warn(httpd, "mmap failed: %s", strerror(errno));
+		bozowarn(httpd, "mmap failed: %s", strerror(errno));
 		return -1;
 	}
 	mappedaddr = addr;
@@ -806,7 +847,7 @@ mmap_and_write_part(bozohttpd_t *httpd, 
 	while (sz > BOZO_WRSZ) {
 		if (bozo_write(httpd, STDOUT_FILENO, addr + wroffset,
 				BOZO_WRSZ) != BOZO_WRSZ) {
-			bozo_warn(httpd, "write failed: %s", strerror(errno));
+			bozowarn(httpd, "write failed: %s", strerror(errno));
 			goto out;
 		}
 		debug((httpd, DEBUG_OBESE, "wrote %d bytes", BOZO_WRSZ));
@@ -815,13 +856,13 @@ mmap_and_write_part(bozohttpd_t *httpd, 
 	}
 	if (sz && (size_t)bozo_write(httpd, STDOUT_FILENO, addr + wroffset,
 				sz) != sz) {
-		bozo_warn(httpd, "final write failed: %s", strerror(errno));
+		bozowarn(httpd, "final write failed: %s", strerror(errno));
 		goto out;
 	}
 	debug((httpd, DEBUG_OBESE, "wrote %d bytes", (int)sz));
  out:
 	if (munmap(mappedaddr, mappedsz) < 0) {
-		bozo_warn(httpd, "munmap failed");
+		bozowarn(httpd, "munmap failed");
 		return -1;
 	}
 
@@ -849,10 +890,11 @@ parse_http_date(const char *val, time_t 
 /*
  * given an url, encode it ala rfc 3986.  ie, escape ? and friends.
  * note that this function returns a static buffer, and thus needs
- * to be updated for any sort of parallel processing.
+ * to be updated for any sort of parallel processing. escape only
+ * chosen characters for absolute redirects
  */
 char *
-bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url)
+bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url, int absolute)
 {
 	static char *buf;
 	static size_t buflen = 0;
@@ -866,17 +908,11 @@ bozo_escape_rfc3986(bozohttpd_t *httpd, 
 		buf = bozorealloc(httpd, buf, buflen);
 	}
 
-	if (url == NULL) {
-		buf[0] = 0;
-		return buf;
-	}
-
 	for (len = 0, s = url, d = buf; *s;) {
 		if (*s & 0x80)
 			goto encode_it;
 		switch (*s) {
 		case ':':
-		case '/':
 		case '?':
 		case '#':
 		case '[':
@@ -894,11 +930,18 @@ bozo_escape_rfc3986(bozohttpd_t *httpd, 
 		case ';':
 		case '=':
 		case '%':
+		case '"':
+			if (absolute)
+				goto leave_it;
+		case '\n':
+		case '\r':
+		case ' ':
 		encode_it:
-			snprintf(d, 4, "%%%2X", *s++);
+			snprintf(d, 4, "%%%02X", *s++);
 			d += 3;
 			len += 3;
 			break;
+		leave_it:
 		default:
 			*d++ = *s++;
 			len++;
@@ -911,115 +954,118 @@ bozo_escape_rfc3986(bozohttpd_t *httpd, 
 }
 
 /*
- * checks to see if this request has a valid .bzdirect file.  returns
- * 0 on failure and 1 on success.
- */
-static int
-check_direct_access(bozo_httpreq_t *request)
-{
-	FILE *fp;
-	struct stat sb;
-	char dir[MAXPATHLEN], dirfile[MAXPATHLEN], *basename;
-
-	snprintf(dir, sizeof(dir), "%s", request->hr_file + 1);
-	debug((request->hr_httpd, DEBUG_FAT, "check_direct_access: dir %s", dir));
-	basename = strrchr(dir, '/');
-
-	if ((!basename || basename[1] != '\0') &&
-	    lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode))
-		/* nothing */;
-	else if (basename == NULL)
-		strcpy(dir, ".");
-	else {
-		*basename++ = '\0';
-		bozo_check_special_files(request, basename);
-	}
-
-	if ((size_t)snprintf(dirfile, sizeof(dirfile), "%s/%s", dir,
-	  DIRECT_ACCESS_FILE) >= sizeof(dirfile)) {
-		bozo_http_error(request->hr_httpd, 404, request,
-		  "directfile path too long");
-		return 0;
-	}
-	if (stat(dirfile, &sb) < 0 ||
-	    (fp = fopen(dirfile, "r")) == NULL)
-		return 0;
-	fclose(fp);
-	return 1;
-}
-
-/*
- * do automatic redirection -- if there are query parameters for the URL
- * we will tack these on to the new (redirected) URL.
+ * do automatic redirection -- if there are query parameters or userdir for
+ * the URL we will tack these on to the new (redirected) URL.
  */
 static void
-handle_redirect(bozo_httpreq_t *request,
-		const char *url, int absolute)
+handle_redirect(bozo_httpreq_t *request, const char *url, int absolute)
 {
 	bozohttpd_t *httpd = request->hr_httpd;
-	char *urlbuf;
+	char *finalurl, *urlbuf;
+#ifndef NO_USER_SUPPORT
+	char *userbuf;
+#endif /* !NO_USER_SUPPORT */
 	char portbuf[20];
+	const char *scheme, *query, *quest;
 	const char *hostname = BOZOHOST(httpd, request);
-	int query = 0;
+	int absproto = 0; /* absolute redirect provides own schema */
 
 	if (url == NULL) {
-		if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0)
-			bozo_err(httpd, 1, "asprintf");
+		bozoasprintf(httpd, &urlbuf, "/%s/", request->hr_file);
 		url = urlbuf;
 	} else
 		urlbuf = NULL;
-	url = bozo_escape_rfc3986(request->hr_httpd, url);
 
-	if (request->hr_query && strlen(request->hr_query))
-		query = 1;
+#ifndef NO_USER_SUPPORT
+	if (request->hr_user && !absolute) {
+		bozoasprintf(httpd, &userbuf, "/~%s%s", request->hr_user, url);
+		url = userbuf;
+	} else
+		userbuf = NULL;
+#endif /* !NO_USER_SUPPORT */
 
-	if (request->hr_serverport && strcmp(request->hr_serverport, "80") != 0)
-		snprintf(portbuf, sizeof(portbuf), ":%s",
-		    request->hr_serverport);
-	else
+	if (absolute) {
+		char *sep = NULL;
+		const char *s;
+
+		/*
+		 * absolute redirect may specify own protocol i.e. to redirect
+		 * to another schema like https:// or ftp://.
+		 * Details: RFC 3986, section 3.
+		 */
+
+		/* 1. check if url contains :// */
+		sep = strstr(url, "://");
+
+		/*
+		 * RFC 3986, section 3.1:
+		 * scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+		 */
+		if (sep) {
+			for (s = url; s != sep;) {
+				if (!isalnum((int)*s) &&
+				    *s != '+' && *s != '-' && *s != '.')
+					break;
+				if (++s == sep) {
+					absproto = 1;
+				}
+			}
+		}
+	}
+
+	/* construct final redirection url */
+
+	scheme = absproto ? "" : httpd->sslinfo ? "https://"; : "http://";;
+
+	if (absolute) {
+		hostname = "";
 		portbuf[0] = '\0';
-	if (absolute)
-		bozo_warn(httpd, "redirecting %s", url);
-	else
-		bozo_warn(httpd, "redirecting %s%s%s", hostname, portbuf, url);
-	debug((httpd, DEBUG_FAT, "redirecting %s", url));
+	} else {
+		const char *defport = httpd->sslinfo ? "443" : "80";
+
+		if (request->hr_serverport &&
+		    strcmp(request->hr_serverport, defport) != 0)
+			snprintf(portbuf, sizeof(portbuf), ":%s",
+			    request->hr_serverport);
+		else
+			portbuf[0] = '\0';
+	}
+
+	url = bozo_escape_rfc3986(httpd, url, absolute);
+
+	if (request->hr_query && strlen(request->hr_query)) {
+		query = request->hr_query;
+		quest = "?";
+	} else {
+		query = quest = "";
+	}
+
+	bozoasprintf(httpd, &finalurl, "%s%s%s%s%s%s",
+		     scheme, hostname, portbuf, url, quest, query);
+
+	bozowarn(httpd, "redirecting %s", finalurl);
+	debug((httpd, DEBUG_FAT, "redirecting %s", finalurl));
+
 	bozo_printf(httpd, "%s 301 Document Moved\r\n", request->hr_proto);
 	if (request->hr_proto != httpd->consts.http_09)
 		bozo_print_header(request, NULL, "text/html", NULL);
-	if (request->hr_proto != httpd->consts.http_09) {
-		bozo_printf(httpd, "Location: http://";);
-		if (absolute == 0)
-			bozo_printf(httpd, "%s%s", hostname, portbuf);
-		if (query) {
-			bozo_printf(httpd, "%s?%s\r\n", url, request->hr_query);
-		} else {
-			bozo_printf(httpd, "%s\r\n", url);
-		}
-	}
+	if (request->hr_proto != httpd->consts.http_09)
+		bozo_printf(httpd, "Location: %s\r\n", finalurl);
 	bozo_printf(httpd, "\r\n");
 	if (request->hr_method == HTTP_HEAD)
 		goto head;
 	bozo_printf(httpd, "<html><head><title>Document Moved</title></head>\n");
 	bozo_printf(httpd, "<body><h1>Document Moved</h1>\n");
-	bozo_printf(httpd, "This document had moved <a href=\"http://";);
-	if (query) {
-		if (absolute)
-			bozo_printf(httpd, "%s?%s", url, request->hr_query);
-		else
-			bozo_printf(httpd, "%s%s%s?%s", hostname,
-				    portbuf, url, request->hr_query);
-	} else {
-		if (absolute)
-			bozo_printf(httpd, "%s", url);
-		else
-			bozo_printf(httpd, "%s%s%s", hostname,
-				    portbuf, url);
-	}
-	bozo_printf(httpd, "\">here</a>\n");
+	bozo_printf(httpd, "This document had moved <a href=\"%s\">here</a>\n",
+	  finalurl);
 	bozo_printf(httpd, "</body></html>\n");
 head:
 	bozo_flush(httpd, stdout);
 	free(urlbuf);
+	free(finalurl);
+#ifndef NO_USER_SUPPORT
+	free(userbuf);
+#endif /* !NO_USER_SUPPORT */
 }
 
 /*
@@ -1036,9 +1082,6 @@ check_virtual(bozo_httpreq_t *request)
 	char *file = request->hr_file, *s;
 	size_t len;
 
-	if (!httpd->virtbase)
-		goto use_slashdir;
-
 	/*
 	 * convert http://virtual.host/ to request->hr_host
 	 */
@@ -1049,12 +1092,12 @@ check_virtual(bozo_httpreq_t *request)
 		file += 7;
 		/* RFC 2616 (HTTP/1.1), 5.2: URI takes precedence over Host: */
 		free(request->hr_host);
-		request->hr_host = bozostrdup(request->hr_httpd, file);
+		request->hr_host = bozostrdup(httpd, request, file);
 		if ((s = strchr(request->hr_host, '/')) != NULL)
 			*s = '\0';
 		s = strchr(file, '/');
 		free(request->hr_file);
-		request->hr_file = bozostrdup(request->hr_httpd, s ? s : "/");
+		request->hr_file = bozostrdup(httpd, request, s ? s : "/");
 		debug((httpd, DEBUG_OBESE, "got host ``%s'' file is now ``%s''",
 		    request->hr_host, request->hr_file));
 	} else if (!request->hr_host)
@@ -1068,6 +1111,29 @@ check_virtual(bozo_httpreq_t *request)
 		request->hr_host[len - 3] = '\0';
 		len = strlen(request->hr_host);
 	}
+
+	if (!httpd->virtbase) {
+
+		/*
+		 * if we don't use vhost support, then set virthostname if
+		 * user supplied Host header. It will be used for possible
+		 * redirections
+		 */
+
+		if (request->hr_host) {
+			s = strrchr(request->hr_host, ':');
+			if (s != NULL)
+				/* truncate Host: as we want to copy it without port part */
+				*s = '\0';
+			request->hr_virthostname = bozostrdup(httpd, request,
+			  request->hr_host);
+			if (s != NULL)
+				/* fix Host: again, if we truncated it */
+				*s = ':';
+		}
+
+		goto use_slashdir;
+	}
 	
 	/*
 	 * ok, we have a virtual host, use opendir(3) to find a case
@@ -1092,15 +1158,14 @@ check_virtual(bozo_httpreq_t *request)
 				}
 				debug((httpd, DEBUG_OBESE, "looking at dir``%s''",
 			 	   d->d_name));
-				if (strncasecmp(d->d_name, request->hr_host,
-				    len) == 0) {
+				if (strcmp(d->d_name, request->hr_host) == 0) {
 					/* found it, punch it */
 					debug((httpd, DEBUG_OBESE, "found it punch it"));
 					request->hr_virthostname =
-					    bozostrdup(httpd, d->d_name);
-					if (asprintf(&s, "%s/%s", httpd->virtbase,
-					    request->hr_virthostname) < 0)
-						bozo_err(httpd, 1, "asprintf");
+					    bozostrdup(httpd, request, d->d_name);
+					bozoasprintf(httpd, &s, "%s/%s",
+					    httpd->virtbase,
+					    request->hr_virthostname);
 					break;
 				}
 			}
@@ -1137,6 +1202,7 @@ use_slashdir:
 static int
 check_bzredirect(bozo_httpreq_t *request)
 {
+	bozohttpd_t *httpd = request->hr_httpd;
 	struct stat sb;
 	char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1],
 	    path[MAXPATHLEN];
@@ -1149,27 +1215,31 @@ check_bzredirect(bozo_httpreq_t *request
 	 */
 	if((size_t)snprintf(dir, sizeof(dir), "%s", request->hr_file + 1) >=
 	  sizeof(dir)) {
-		bozo_http_error(request->hr_httpd, 404, request,
+		bozo_http_error(httpd, 404, request,
 		  "file path too long");
 		return -1;
 	}
-	debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir));
+	debug((httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir));
 	basename = strrchr(dir, '/');
 
 	if ((!basename || basename[1] != '\0') &&
-	    lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode))
-		/* nothing */;
-	else if (basename == NULL)
-		strcpy(dir, ".");
-	else {
+	    lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+		strcpy(path, dir);
+	} else if (basename == NULL) {
+		strcpy(path, ".");
+		strcpy(dir, "");
+	} else {
 		*basename++ = '\0';
 		bozo_check_special_files(request, basename);
+		strcpy(path, dir);
 	}
 
-	if ((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir,
+	debug((httpd, DEBUG_FAT, "check_bzredirect: path %s", path));
+
+	if ((size_t)snprintf(redir, sizeof(redir), "%s/%s", path,
 	  REDIRECT_FILE) >= sizeof(redir)) {
-		bozo_http_error(request->hr_httpd, 404, request,
-		  "redirectfile path too long");
+		bozo_http_error(httpd, 404, request,
+		    "redirectfile path too long");
 		return -1;
 	}
 	if (lstat(redir, &sb) == 0) {
@@ -1177,9 +1247,9 @@ check_bzredirect(bozo_httpreq_t *request
 			return 0;
 		absolute = 0;
 	} else {
-		if((size_t)snprintf(redir, sizeof(redir), "%s/%s", dir,
+		if((size_t)snprintf(redir, sizeof(redir), "%s/%s", path,
 		  ABSREDIRECT_FILE) >= sizeof(redir)) {
-			bozo_http_error(request->hr_httpd, 404, request,
+			bozo_http_error(httpd, 404, request,
 			  "redirectfile path too long");
 			return -1;
 		}
@@ -1187,16 +1257,14 @@ check_bzredirect(bozo_httpreq_t *request
 			return 0;
 		absolute = 1;
 	}
-	debug((request->hr_httpd, DEBUG_FAT,
-	       "check_bzredirect: calling readlink"));
+	debug((httpd, DEBUG_FAT, "check_bzredirect: calling readlink"));
 	rv = readlink(redir, redirpath, sizeof redirpath - 1);
 	if (rv == -1 || rv == 0) {
-		debug((request->hr_httpd, DEBUG_FAT, "readlink failed"));
+		debug((httpd, DEBUG_FAT, "readlink failed"));
 		return 0;
 	}
 	redirpath[rv] = '\0';
-	debug((request->hr_httpd, DEBUG_FAT,
-	       "readlink returned \"%s\"", redirpath));
+	debug((httpd, DEBUG_FAT, "readlink returned \"%s\"", redirpath));
 
 	/* check if we need authentication */
 	snprintf(path, sizeof(path), "%s/", dir);
@@ -1204,37 +1272,33 @@ check_bzredirect(bozo_httpreq_t *request
 		return 1;
 
 	/* now we have the link pointer, redirect to the real place */
-	if (absolute)
-		finalredir = redirpath;
-	else {
-		if ((size_t)snprintf(finalredir = redir, sizeof(redir), "/%s/%s",
-		  dir, redirpath) >= sizeof(redir)) {
-			bozo_http_error(request->hr_httpd, 404, request,
+	if (!absolute && redirpath[0] != '/') {
+		if ((size_t)snprintf(finalredir = redir, sizeof(redir), "%s%s/%s",
+		  (strlen(dir) > 0 ? "/" : ""), dir, redirpath) >= sizeof(redir)) {
+			bozo_http_error(httpd, 404, request,
 			  "redirect path too long");
 			return -1;
 		}
-	}
+	} else
+		finalredir = redirpath;
 
-	debug((request->hr_httpd, DEBUG_FAT,
-	       "check_bzredirect: new redir %s", finalredir));
+	debug((httpd, DEBUG_FAT, "check_bzredirect: new redir %s", finalredir));
 	handle_redirect(request, finalredir, absolute);
 	return 1;
 }
 
 /* this fixes the %HH hack that RFC2396 requires.  */
-static int
-fix_url_percent(bozo_httpreq_t *request)
+int
+bozo_decode_url_percent(bozo_httpreq_t *request, char *str)
 {
 	bozohttpd_t *httpd = request->hr_httpd;
-	char	*s, *t, buf[3], *url;
+	char	*s, *t, buf[3];
 	char	*end;	/* if end is not-zero, we don't translate beyond that */
 
-	url = request->hr_file;
-
-	end = url + strlen(url);
+	end = str + strlen(str);
 
 	/* fast forward to the first % */
-	if ((s = strchr(url, '%')) == NULL)
+	if ((s = strchr(str, '%')) == NULL)
 		return 0;
 
 	t = s;
@@ -1286,7 +1350,7 @@ fix_url_percent(bozo_httpreq_t *request)
 	} while (*s);
 	*t = '\0';
 
-	debug((httpd, DEBUG_FAT, "fix_url_percent returns %s in url",
+	debug((httpd, DEBUG_FAT, "bozo_decode_url_percent returns `%s'",
 			request->hr_file));
 
 	return 0;
@@ -1296,7 +1360,6 @@ fix_url_percent(bozo_httpreq_t *request)
  * transform_request does this:
  *	- ``expand'' %20 crapola
  *	- punt if it doesn't start with /
- *	- check httpd->untrustedref / referrer
  *	- look for "http://myname/"; and deal with it.
  *	- maybe call bozo_process_cgi()
  *	- check for ~user and call bozo_user_transform() if so
@@ -1314,12 +1377,11 @@ transform_request(bozo_httpreq_t *reques
 	bozohttpd_t *httpd = request->hr_httpd;
 	char	*file, *newfile = NULL;
 	size_t	len;
-	const char *hostname = BOZOHOST(httpd, request);
 
 	file = NULL;
 	*isindex = 0;
 	debug((httpd, DEBUG_FAT, "tf_req: file %s", request->hr_file));
-	if (fix_url_percent(request)) {
+	if (bozo_decode_url_percent(request, request->hr_file)) {
 		goto bad_done;
 	}
 	if (check_virtual(request)) {
@@ -1332,84 +1394,49 @@ transform_request(bozo_httpreq_t *reques
 		goto bad_done;
 	}
 
-	switch(check_bzredirect(request)) {
-	case -1:
-		goto bad_done;
-	case 1:
-		return 0;
-	}
-
-	if (httpd->untrustedref) {
-		int to_indexhtml = 0;
+	/* omit additional slashes at the beginning */
+	while (file[1] == '/')
+		file++;
 
-#define TOP_PAGE(x)	(strcmp((x), "/") == 0 || \
-			 strcmp((x) + 1, httpd->index_html) == 0 || \
-			 strcmp((x) + 1, "favicon.ico") == 0)
-
-		debug((httpd, DEBUG_EXPLODING, "checking httpd->untrustedref"));
-		/*
-		 * first check that this path isn't allowed via .bzdirect file,
-		 * and then check referrer; make sure that people come via the
-		 * real name... otherwise if we aren't looking at / or
-		 * /index.html, redirect...  we also special case favicon.ico.
-		 */
-		if (check_direct_access(request))
-			/* nothing */;
-		else if (request->hr_referrer) {
-			const char *r = request->hr_referrer;
-
-			debug((httpd, DEBUG_FAT,
-				"checking referrer \"%s\" vs virthostname %s",
-				r, hostname));
-			if (strncmp(r, "http://";, 7) != 0 ||
-			    (strncasecmp(r + 7, hostname,
-			    		 strlen(hostname)) != 0 &&
-			     !TOP_PAGE(file)))
-				to_indexhtml = 1;
-		} else {
-			const char *h = request->hr_host;
-
-			debug((httpd, DEBUG_FAT, "url has no referrer at all"));
-			/* if there's no referrer, let / or /index.html past */
-			if (!TOP_PAGE(file) ||
-			    (h && strncasecmp(h, hostname,
-			    		strlen(hostname)) != 0))
-				to_indexhtml = 1;
-		}
-
-		if (to_indexhtml) {
-			char *slashindexhtml;
-
-			if (asprintf(&slashindexhtml, "/%s",
-					httpd->index_html) < 0)
-				bozo_err(httpd, 1, "asprintf");
-			debug((httpd, DEBUG_FAT,
-				"httpd->untrustedref: redirecting %s to %s",
-				file, slashindexhtml));
-			handle_redirect(request, slashindexhtml, 0);
-			free(slashindexhtml);
-			return 0;
-		}
-	}
+	/* fix file provided by user as it's used in other handlers */
+	request->hr_file = file;
 
 	len = strlen(file);
-	if (/*CONSTCOND*/0) {
+
 #ifndef NO_USER_SUPPORT
-	} else if (len > 1 && httpd->enable_users && file[1] == '~') {
+	/* first of all expand user path */
+	if (len > 1 && httpd->enable_users && file[1] == '~') {
 		if (file[2] == '\0') {
 			(void)bozo_http_error(httpd, 404, request,
 						"missing username");
 			goto bad_done;
 		}
 		if (strchr(file + 2, '/') == NULL) {
-			handle_redirect(request, NULL, 0);
+			char *userredirecturl;
+			bozoasprintf(httpd, &userredirecturl, "%s/", file);
+			handle_redirect(request, userredirecturl, 0);
+			free(userredirecturl);
 			return 0;
 		}
 		debug((httpd, DEBUG_FAT, "calling bozo_user_transform"));
 
-		return bozo_user_transform(request, isindex);
+		if (!bozo_user_transform(request))
+			return 0;
+		
+		file = request->hr_file;
+		len = strlen(file);
+	}
 #endif /* NO_USER_SUPPORT */
-	} else if (len > 1) {
+
+
+	switch (check_bzredirect(request)) {
+	case -1:
+		goto bad_done;
+	case 1:
+		return 0;
+	}
+
+	if (len > 1) {
 		debug((httpd, DEBUG_FAT, "file[len-1] == %c", file[len-1]));
 		if (file[len-1] == '/') {	/* append index.html */
 			*isindex = 1;
@@ -1419,10 +1446,10 @@ transform_request(bozo_httpreq_t *reques
 			strcpy(newfile, file + 1);
 			strcat(newfile, httpd->index_html);
 		} else
-			newfile = bozostrdup(request->hr_httpd, file + 1);
+			newfile = bozostrdup(httpd, request, file + 1);
 	} else if (len == 1) {
 		debug((httpd, DEBUG_EXPLODING, "tf_req: len == 1"));
-		newfile = bozostrdup(request->hr_httpd, httpd->index_html);
+		newfile = bozostrdup(httpd, request, httpd->index_html);
 		*isindex = 1;
 	} else {	/* len == 0 ? */
 		(void)bozo_http_error(httpd, 500, request,
@@ -1436,15 +1463,14 @@ transform_request(bozo_httpreq_t *reques
 	}
 
 	/*
-	 * look for "http://myname/"; and deal with it as necessary.
-	 */
-
-	/*
 	 * stop traversing outside our domain
 	 *
 	 * XXX true security only comes from our parent using chroot(2)
 	 * before execve(2)'ing us.  or our own built in chroot(2) support.
 	 */
+	
+	debug((httpd, DEBUG_FAT, "newfile: %s", newfile));
+	
 	if (*newfile == '/' || strcmp(newfile, "..") == 0 ||
 	    strstr(newfile, "/..") || strstr(newfile, "../")) {
 		(void)bozo_http_error(httpd, 403, request, "illegal request");
@@ -1548,7 +1574,7 @@ bozo_process_request(bozo_httpreq_t *req
 	fd = -1;
 	encoding = NULL;
 	if (can_gzip(request)) {
-		asprintf(&file, "%s.gz", request->hr_file);
+		bozoasprintf(httpd, &file, "%s.gz", request->hr_file);
 		fd = open(file, O_RDONLY);
 		if (fd >= 0)
 			encoding = "gzip";
@@ -1562,7 +1588,7 @@ bozo_process_request(bozo_httpreq_t *req
 
 	if (fd < 0) {
 		debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno)));
-		switch(errno) {
+		switch (errno) {
 		case EPERM:
 			(void)bozo_http_error(httpd, 403, request,
 						"no permission to open file");
@@ -1640,9 +1666,6 @@ bozo_process_request(bozo_httpreq_t *req
 		while (szleft) {
 			size_t sz;
 
-			/* This should take care of the first unaligned chunk */
-			if ((cur_byte_pos & (httpd->page_size - 1)) != 0)
-				sz = (size_t)(cur_byte_pos & ~httpd->page_size);
 			if ((off_t)httpd->mmapsz < szleft)
 				sz = httpd->mmapsz;
 			else
@@ -1694,6 +1717,12 @@ bozo_print_header(bozo_httpreq_t *reques
 	bozohttpd_t *httpd = request->hr_httpd;
 	off_t len;
 	char	date[40];
+	bozoheaders_t *hdr;
+
+	SIMPLEQ_FOREACH(hdr, &request->hr_replheaders, h_next) {
+		bozo_printf(httpd, "%s: %s\r\n", hdr->h_header,
+				hdr->h_value);
+	}
 
 	bozo_printf(httpd, "Date: %s\r\n", bozo_http_date(date, sizeof(date)));
 	bozo_printf(httpd, "Server: %s\r\n", httpd->server_software);
@@ -1724,7 +1753,7 @@ bozo_print_header(bozo_httpreq_t *reques
 			len = sbp->st_size;
 		bozo_printf(httpd, "Content-Length: %qd\r\n", (long long)len);
 	}
-	if (request && request->hr_proto == httpd->consts.http_11)
+	if (request->hr_proto == httpd->consts.http_11)
 		bozo_printf(httpd, "Connection: close\r\n");
 	bozo_flush(httpd, stdout);
 }
@@ -1754,7 +1783,7 @@ debug__(bozohttpd_t *httpd, int level, c
 
 /* these are like warn() and err(), except for syslog not stderr */
 void
-bozo_warn(bozohttpd_t *httpd, const char *fmt, ...)
+bozowarn(bozohttpd_t *httpd, const char *fmt, ...)
 {
 	va_list ap;
 
@@ -1769,7 +1798,7 @@ bozo_warn(bozohttpd_t *httpd, const char
 }
 
 void
-bozo_err(bozohttpd_t *httpd, int code, const char *fmt, ...)
+bozoerr(bozohttpd_t *httpd, int code, const char *fmt, ...)
 {
 	va_list ap;
 
@@ -1784,6 +1813,20 @@ bozo_err(bozohttpd_t *httpd, int code, c
 	exit(code);
 }
 
+void
+bozoasprintf(bozohttpd_t *httpd, char **str, const char *fmt, ...)
+{
+	va_list ap;
+	int e;
+
+	va_start(ap, fmt);
+	e = vasprintf(str, fmt, ap);
+	va_end(ap);
+
+	if (e < 0)
+		bozoerr(httpd, EXIT_FAILURE, "asprintf");
+}
+
 /*
  * this escapes HTML tags.  returns allocated escaped
  * string if needed, or NULL on allocation failure or
@@ -1859,6 +1902,7 @@ static struct errors_map {
 	{ 404, 	"404 Not Found",	"This item has not been found", },
 	{ 408, 	"408 Request Timeout",	"This request took too long", },
 	{ 417,	"417 Expectation Failed","Expectations not available", },
+	{ 420,	"420 Enhance Your Calm","Chill, Winston", },
 	{ 500,	"500 Internal Error",	"An error occured on the server", },
 	{ 501,	"501 Not Implemented",	"This request is not available", },
 	{ 0,	NULL,			NULL, },
@@ -1900,10 +1944,11 @@ bozo_http_error(bozohttpd_t *httpd, int 
 	const char *proto = (request && request->hr_proto) ?
 				request->hr_proto : httpd->consts.http_11;
 	int	size;
+	bozoheaders_t *hdr;
 
 	debug((httpd, DEBUG_FAT, "bozo_http_error %d: %s", code, msg));
 	if (header == NULL || reason == NULL) {
-		bozo_err(httpd, 1,
+		bozoerr(httpd, 1,
 			"bozo_http_error() failed (short = %p, long = %p)",
 			header, reason);
 		return code;
@@ -1917,32 +1962,59 @@ bozo_http_error(bozohttpd_t *httpd, int 
 		portbuf[0] = '\0';
 
 	if (request && request->hr_file) {
-		char *file = NULL;
+		char *file = NULL, *user = NULL, *user_escaped = NULL;
+		int file_alloc = 0;
 		const char *hostname = BOZOHOST(httpd, request);
 
 		/* bozo_escape_html() failure here is just too bad. */
 		file = bozo_escape_html(NULL, request->hr_file);
-		if (file == NULL)
+		if (file == NULL) 
 			file = request->hr_file;
+		else
+			file_alloc = 1;
+
+#ifndef NO_USER_SUPPORT
+		if (request->hr_user != NULL) {
+			user_escaped = bozo_escape_html(NULL, request->hr_user);
+			if (user_escaped == NULL)
+				user_escaped = request->hr_user;
+			/* expand username to ~user/ */
+			bozoasprintf(httpd, &user, "~%s/", user_escaped);
+			if (user_escaped != request->hr_user)
+				free(user_escaped);
+		}
+#endif /* !NO_USER_SUPPORT */
+
 		size = snprintf(httpd->errorbuf, BUFSIZ,
 		    "<html><head><title>%s</title></head>\n"
 		    "<body><h1>%s</h1>\n"
-		    "%s: <pre>%s</pre>\n"
+		    "%s%s: <pre>%s</pre>\n"
  		    "<hr><address><a href=\"http://%s%s/\";>%s%s</a></address>\n"
 		    "</body></html>\n",
-		    header, header, file, reason,
-		    hostname, portbuf, hostname, portbuf);
+		    header, header,
+		    user ? user : "", file,
+		    reason, hostname, portbuf, hostname, portbuf);
+		free(user);
 		if (size >= (int)BUFSIZ) {
-			bozo_warn(httpd,
+			bozowarn(httpd,
 				"bozo_http_error buffer too small, truncated");
 			size = (int)BUFSIZ;
 		}
+
+		if (file_alloc)
+			free(file);
 	} else
 		size = 0;
 
 	bozo_printf(httpd, "%s %s\r\n", proto, header);
-	if (request)
+
+	if (request) {
 		bozo_auth_check_401(request, code);
+		SIMPLEQ_FOREACH(hdr, &request->hr_replheaders, h_next) {
+			bozo_printf(httpd, "%s: %s\r\n", hdr->h_header,
+					hdr->h_value);
+		}
+	}
 
 	bozo_printf(httpd, "Content-Type: text/html\r\n");
 	bozo_printf(httpd, "Content-Length: %d\r\n", size);
@@ -2075,12 +2147,11 @@ bozorealloc(bozohttpd_t *httpd, void *pt
 	void	*p;
 
 	p = realloc(ptr, size);
-	if (p == NULL) {
-		(void)bozo_http_error(httpd, 500, NULL,
-				"memory allocation failure");
-		exit(1);
-	}
-	return (p);
+	if (p)
+		return p;
+	
+	(void)bozo_http_error(httpd, 500, NULL, "memory allocation failure");
+	exit(EXIT_FAILURE);
 }
 
 void *
@@ -2089,26 +2160,27 @@ bozomalloc(bozohttpd_t *httpd, size_t si
 	void	*p;
 
 	p = malloc(size);
-	if (p == NULL) {
-		(void)bozo_http_error(httpd, 500, NULL,
-				"memory allocation failure");
-		exit(1);
-	}
-	return (p);
+	if (p)
+		return p;
+
+	(void)bozo_http_error(httpd, 500, NULL, "memory allocation failure");
+	exit(EXIT_FAILURE);
 }
 
 char *
-bozostrdup(bozohttpd_t *httpd, const char *str)
+bozostrdup(bozohttpd_t *httpd, bozo_httpreq_t *request, const char *str)
 {
 	char	*p;
 
 	p = strdup(str);
-	if (p == NULL) {
-		(void)bozo_http_error(httpd, 500, NULL,
-					"memory allocation failure");
-		exit(1);
-	}
-	return (p);
+	if (p)
+		return p;
+
+	if (!request)
+		bozoerr(httpd, EXIT_FAILURE, "strdup");
+
+	(void)bozo_http_error(httpd, 500, request, "memory allocation failure");
+	exit(EXIT_FAILURE);
 }
 
 /* set default values in bozohttpd_t struct */
@@ -2141,15 +2213,16 @@ bozo_init_httpd(bozohttpd_t *httpd)
 
 /* set default values in bozoprefs_t struct */
 int
-bozo_init_prefs(bozoprefs_t *prefs)
+bozo_init_prefs(bozohttpd_t *httpd, bozoprefs_t *prefs)
 {
 	/* make sure everything is clean */
 	(void) memset(prefs, 0x0, sizeof(*prefs));
 
 	/* set up default values */
-	bozo_set_pref(prefs, "server software", SERVER_SOFTWARE);
-	bozo_set_pref(prefs, "index.html", INDEX_HTML);
-	bozo_set_pref(prefs, "public_html", PUBLIC_HTML);
+	if (!bozo_set_pref(httpd, prefs, "server software", SERVER_SOFTWARE) ||
+	    !bozo_set_pref(httpd, prefs, "index.html", INDEX_HTML) ||
+	    !bozo_set_pref(httpd, prefs, "public_html", PUBLIC_HTML))
+		return 0;
 
 	return 1;
 }
@@ -2158,7 +2231,7 @@ bozo_init_prefs(bozoprefs_t *prefs)
 int
 bozo_set_defaults(bozohttpd_t *httpd, bozoprefs_t *prefs)
 {
-	return bozo_init_httpd(httpd) && bozo_init_prefs(prefs);
+	return bozo_init_httpd(httpd) && bozo_init_prefs(httpd, prefs);
 }
 
 /* set the virtual host name, port and root */
@@ -2169,7 +2242,7 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs
 	struct passwd	 *pw;
 	extern char	**environ;
 	static char	 *cleanenv[1] = { NULL };
-	uid_t		  uid;
+	uid_t		  uid = 0;
 	char		 *chrootdir;
 	char		 *username;
 	char		 *portnum;
@@ -2180,16 +2253,15 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs
 
 	if (vhost == NULL) {
 		httpd->virthostname = bozomalloc(httpd, MAXHOSTNAMELEN+1);
-		/* XXX we do not check for FQDN here */
 		if (gethostname(httpd->virthostname, MAXHOSTNAMELEN+1) < 0)
-			bozo_err(httpd, 1, "gethostname");
+			bozoerr(httpd, 1, "gethostname");
 		httpd->virthostname[MAXHOSTNAMELEN] = '\0';
 	} else {
-		httpd->virthostname = strdup(vhost);
+		httpd->virthostname = bozostrdup(httpd, NULL, vhost);
 	}
-	httpd->slashdir = strdup(root);
+	httpd->slashdir = bozostrdup(httpd, NULL, root);
 	if ((portnum = bozo_get_pref(prefs, "port number")) != NULL) {
-		httpd->bindport = strdup(portnum);
+		httpd->bindport = bozostrdup(httpd, NULL, portnum);
 	}
 
 	/* go over preferences now */
@@ -2197,16 +2269,12 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs
 	    strcmp(cp, "true") == 0) {
 		httpd->numeric = 1;
 	}
-	if ((cp = bozo_get_pref(prefs, "trusted referal")) != NULL &&
-	    strcmp(cp, "true") == 0) {
-		httpd->untrustedref = 1;
-	}
 	if ((cp = bozo_get_pref(prefs, "log to stderr")) != NULL &&
 	    strcmp(cp, "true") == 0) {
 		httpd->logstderr = 1;
 	}
 	if ((cp = bozo_get_pref(prefs, "bind address")) != NULL) {
-		httpd->bindaddress = strdup(cp);
+		httpd->bindaddress = bozostrdup(httpd, NULL, cp);
 	}
 	if ((cp = bozo_get_pref(prefs, "background")) != NULL) {
 		httpd->background = atoi(cp);
@@ -2216,19 +2284,23 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs
 		httpd->foreground = 1;
 	}
 	if ((cp = bozo_get_pref(prefs, "pid file")) != NULL) {
-		httpd->pidfile = strdup(cp);
+		httpd->pidfile = bozostrdup(httpd, NULL, cp);
 	}
 	if ((cp = bozo_get_pref(prefs, "unknown slash")) != NULL &&
 	    strcmp(cp, "true") == 0) {
 		httpd->unknown_slash = 1;
 	}
 	if ((cp = bozo_get_pref(prefs, "virtual base")) != NULL) {
-		httpd->virtbase = strdup(cp);
+		httpd->virtbase = bozostrdup(httpd, NULL, cp);
 	}
 	if ((cp = bozo_get_pref(prefs, "enable users")) != NULL &&
 	    strcmp(cp, "true") == 0) {
 		httpd->enable_users = 1;
 	}
+	if ((cp = bozo_get_pref(prefs, "enable user cgibin")) != NULL &&
+	    strcmp(cp, "true") == 0) {
+		httpd->enable_cgi_users = 1;
+	}
 	if ((cp = bozo_get_pref(prefs, "dirty environment")) != NULL &&
 	    strcmp(cp, "true") == 0) {
 		dirtyenv = 1;
@@ -2242,11 +2314,12 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs
 		httpd->dir_indexing = 1;
 	}
 	if ((cp = bozo_get_pref(prefs, "public_html")) != NULL) {
-		httpd->public_html = strdup(cp);
+		httpd->public_html = bozostrdup(httpd, NULL, cp);
 	}
 	httpd->server_software =
-			strdup(bozo_get_pref(prefs, "server software"));
-	httpd->index_html = strdup(bozo_get_pref(prefs, "index.html"));
+	    bozostrdup(httpd, NULL, bozo_get_pref(prefs, "server software"));
+	httpd->index_html =
+	    bozostrdup(httpd, NULL, bozo_get_pref(prefs, "index.html"));
 
 	/*
 	 * initialise ssl and daemon mode if necessary.
@@ -2254,39 +2327,33 @@ bozo_setup(bozohttpd_t *httpd, bozoprefs
 	bozo_ssl_init(httpd);
 	bozo_daemon_init(httpd);
 
-	if ((username = bozo_get_pref(prefs, "username")) == NULL) {
-		if ((pw = getpwuid(uid = 0)) == NULL)
-			bozo_err(httpd, 1, "getpwuid(0): %s", strerror(errno));
-		httpd->username = strdup(pw->pw_name);
-	} else {
-		httpd->username = strdup(username);
-		if ((pw = getpwnam(httpd->username)) == NULL)
-			bozo_err(httpd, 1, "getpwnam(%s): %s", httpd->username,
-					strerror(errno));
+	username = bozo_get_pref(prefs, "username");
+	if (username != NULL) {
+		if ((pw = getpwnam(username)) == NULL)
+			bozoerr(httpd, 1, "getpwnam(%s): %s", username,
+				strerror(errno));
 		if (initgroups(pw->pw_name, pw->pw_gid) == -1)
-			bozo_err(httpd, 1, "initgroups: %s", strerror(errno));
+			bozoerr(httpd, 1, "initgroups: %s", strerror(errno));
 		if (setgid(pw->pw_gid) == -1)
-			bozo_err(httpd, 1, "setgid(%u): %s", pw->pw_gid,
-					strerror(errno));
+			bozoerr(httpd, 1, "setgid(%u): %s", pw->pw_gid,
+				strerror(errno));
 		uid = pw->pw_uid;
 	}
 	/*
 	 * handle chroot.
 	 */
 	if ((chrootdir = bozo_get_pref(prefs, "chroot dir")) != NULL) {
-		httpd->rootdir = strdup(chrootdir);
+		httpd->rootdir = bozostrdup(httpd, NULL, chrootdir);
 		if (chdir(httpd->rootdir) == -1)
-			bozo_err(httpd, 1, "chdir(%s): %s", httpd->rootdir,
+			bozoerr(httpd, 1, "chdir(%s): %s", httpd->rootdir,
 				strerror(errno));
 		if (chroot(httpd->rootdir) == -1)
-			bozo_err(httpd, 1, "chroot(%s): %s", httpd->rootdir,
+			bozoerr(httpd, 1, "chroot(%s): %s", httpd->rootdir,
 				strerror(errno));
 	}
 
-	if (username != NULL)
-		if (setuid(uid) == -1)
-			bozo_err(httpd, 1, "setuid(%d): %s", uid,
-					strerror(errno));
+	if (username != NULL && setuid(uid) == -1)
+		bozoerr(httpd, 1, "setuid(%d): %s", uid, strerror(errno));
 
 	/*
 	 * prevent info leakage between different compartments.

Index: src/libexec/httpd/bozohttpd.h
diff -u src/libexec/httpd/bozohttpd.h:1.20.6.1 src/libexec/httpd/bozohttpd.h:1.20.6.2
--- src/libexec/httpd/bozohttpd.h:1.20.6.1	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/bozohttpd.h	Fri Apr 15 19:38:13 2016
@@ -1,9 +1,9 @@
-/*	$NetBSD: bozohttpd.h,v 1.20.6.1 2014/07/09 09:47:10 msaitoh Exp $	*/
+/*	$NetBSD: bozohttpd.h,v 1.20.6.2 2016/04/15 19:38:13 snj Exp $	*/
 
 /*	$eterna: bozohttpd.h,v 1.39 2011/11/18 09:21:15 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2014 Matthew R. Green
+ * Copyright (c) 1997-2015 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -41,6 +41,11 @@
 #endif
 #include <stdio.h>
 
+/* QNX provides a lot of NetBSD things in nbutil.h */
+#ifdef HAVE_NBUTIL_H
+#include <nbutil.h>
+#endif
+
 /* lots of "const" but gets free()'ed etc at times, sigh */
 
 /* headers */
@@ -49,6 +54,7 @@ typedef struct bozoheaders {
 	/*const*/ char *h_value;	/* this gets free()'ed etc at times */
 	SIMPLEQ_ENTRY(bozoheaders)	h_next;
 } bozoheaders_t;
+SIMPLEQ_HEAD(qheaders, bozoheaders);
 
 #ifndef NO_LUA_SUPPORT
 typedef struct lua_handler {
@@ -68,7 +74,6 @@ typedef struct lua_state_map {
 
 typedef struct bozo_content_map_t {
 	const char	*name;		/* postfix of file */
-	size_t	 	 namelen;	/* length of postfix */
 	const char	*type;		/* matching content-type */
 	const char	*encoding;	/* matching content-encoding */
 	const char	*encoding11;	/* matching content-encoding (HTTP/1.1) */
@@ -86,11 +91,9 @@ typedef struct bozo_consts_t {
 /* this structure encapsulates all the bozo flags and control vars */
 typedef struct bozohttpd_t {
 	char		*rootdir;	/* root directory */
-	char		*username;	/* username to switch to */
 	int		 numeric;	/* avoid gethostby*() */
 	char		*virtbase;	/* virtual directory base */
 	int		 unknown_slash;	/* unknown vhosts go to normal slashdir */
-	int		 untrustedref;	/* make sure referrer = me unless url = / */
 	int		 logstderr;	/* log to stderr (even if not tty) */
 	int		 background;	/* drop into daemon mode */
 	int		 foreground;	/* keep daemon mode in foreground */
@@ -105,6 +108,7 @@ typedef struct bozohttpd_t {
 	const char	*index_html;	/* our home page */
 	const char	*public_html;	/* ~user/public_html page */
 	int		 enable_users;	/* enable public_html */
+	int		 enable_cgi_users;	/* use the cgi handler */
 	int		*sock;		/* bound sockets */
 	int		 nsock;		/* number of above */
 	struct pollfd	*fds;		/* current poll fd set */
@@ -147,6 +151,9 @@ typedef struct bozo_httpreq_t {
 	char	*hr_query;
 	char	*hr_host;	/* HTTP/1.1 Host: or virtual hostname,
 				   possibly including a port number */
+#ifndef NO_USER_SUPPORT
+	char	*hr_user;	/* username if we hit userdir request */
+#endif /* !NO_USER_SUPPORT */
 	const char *hr_proto;
 	const char *hr_content_type;
 	const char *hr_content_length;
@@ -166,8 +173,9 @@ typedef struct bozo_httpreq_t {
 	/*const*/ char *hr_authuser;
 	/*const*/ char *hr_authpass;
 #endif
-	SIMPLEQ_HEAD(, bozoheaders)	hr_headers;
-	int	hr_nheaders;
+	struct qheaders		hr_headers;
+	struct qheaders		hr_replheaders;
+	int			hr_nheaders;
 } bozo_httpreq_t;
 
 /* helper to access the "active" host name from a httpd/request pair */
@@ -177,8 +185,8 @@ typedef struct bozo_httpreq_t {
 
 /* structure to hold string based (name, value) pairs with preferences */
 typedef struct bozoprefs_t {
-	unsigned	  size;		/* size of the two arrays */
-	unsigned	  c;		/* # of entries in arrays */
+	size_t		  size;		/* size of the two arrays */
+	size_t		  count;	/* # of entries in arrays */
 	char		**name;		/* names of each entry */
 	char		**value;	/* values for the name entries */
 } bozoprefs_t;
@@ -211,42 +219,52 @@ void	debug__(bozohttpd_t *, int, const c
 #define	debug(x)
 #endif /* NO_DEBUG */
 
-void	bozo_warn(bozohttpd_t *, const char *, ...)
-		BOZO_PRINTFLIKE(2, 3);
-void	bozo_err(bozohttpd_t *, int, const char *, ...)
-		BOZO_PRINTFLIKE(3, 4)
-		BOZO_DEAD;
 int	bozo_http_error(bozohttpd_t *, int, bozo_httpreq_t *, const char *);
 
 int	bozo_check_special_files(bozo_httpreq_t *, const char *);
 char	*bozo_http_date(char *, size_t);
-void	bozo_print_header(bozo_httpreq_t *, struct stat *, const char *, const char *);
-char	*bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url);
+void	bozo_print_header(bozo_httpreq_t *, struct stat *, const char *,
+			  const char *);
+char	*bozo_escape_rfc3986(bozohttpd_t *httpd, const char *url, int absolute);
 char	*bozo_escape_html(bozohttpd_t *httpd, const char *url);
+int	bozo_decode_url_percent(bozo_httpreq_t *, char *);
 
-char	*bozodgetln(bozohttpd_t *, int, ssize_t *, ssize_t (*)(bozohttpd_t *, int, void *, size_t));
+/* these are similar to libc functions, no underscore here */
+void	bozowarn(bozohttpd_t *, const char *, ...)
+		BOZO_PRINTFLIKE(2, 3);
+void	bozoerr(bozohttpd_t *, int, const char *, ...)
+		BOZO_PRINTFLIKE(3, 4)
+		BOZO_DEAD;
+void	bozoasprintf(bozohttpd_t *, char **, const char *, ...)
+		BOZO_PRINTFLIKE(3, 4);
+char	*bozodgetln(bozohttpd_t *, int, ssize_t *, ssize_t (*)(bozohttpd_t *,
+		    int, void *, size_t));
 char	*bozostrnsep(char **, const char *, ssize_t *);
-
 void	*bozomalloc(bozohttpd_t *, size_t);
 void	*bozorealloc(bozohttpd_t *, void *, size_t);
-char	*bozostrdup(bozohttpd_t *, const char *);
+char	*bozostrdup(bozohttpd_t *, bozo_httpreq_t *, const char *);
+
+#define bozo_noop	do { /* nothing */ } while (/*CONSTCOND*/0)
 
 /* ssl-bozo.c */
 #ifdef NO_SSL_SUPPORT
-#define bozo_ssl_set_opts(w, x, y)	do { /* nothing */ } while (0)
-#define bozo_ssl_init(x)		do { /* nothing */ } while (0)
-#define bozo_ssl_accept(x)		do { /* nothing */ } while (0)
-#define bozo_ssl_destroy(x)		do { /* nothing */ } while (0)
+#define bozo_ssl_set_opts(w, x, y)	bozo_noop
+#define bozo_ssl_set_ciphers(w, x, y)	bozo_noop
+#define bozo_ssl_init(x)		bozo_noop
+#define bozo_ssl_accept(x)		(0)
+#define bozo_ssl_destroy(x)		bozo_noop
 #else
 void	bozo_ssl_set_opts(bozohttpd_t *, const char *, const char *);
+void	bozo_ssl_set_ciphers(bozohttpd_t *, const char *);
 void	bozo_ssl_init(bozohttpd_t *);
-void	bozo_ssl_accept(bozohttpd_t *);
+int	bozo_ssl_accept(bozohttpd_t *);
 void	bozo_ssl_destroy(bozohttpd_t *);
 #endif
 
 
 /* auth-bozo.c */
 #ifdef DO_HTPASSWD
+void	bozo_auth_init(bozo_httpreq_t *);
 int	bozo_auth_check(bozo_httpreq_t *, const char *);
 void	bozo_auth_cleanup(bozo_httpreq_t *);
 int	bozo_auth_check_headers(bozo_httpreq_t *, char *, char *, ssize_t);
@@ -255,12 +273,13 @@ void	bozo_auth_check_401(bozo_httpreq_t 
 void	bozo_auth_cgi_setenv(bozo_httpreq_t *, char ***);
 int	bozo_auth_cgi_count(bozo_httpreq_t *);
 #else
+#define	bozo_auth_init(x)			bozo_noop
 #define	bozo_auth_check(x, y)			0
-#define	bozo_auth_cleanup(x)			do { /* nothing */ } while (0)
+#define	bozo_auth_cleanup(x)			bozo_noop
 #define	bozo_auth_check_headers(y, z, a, b)	0
 #define	bozo_auth_check_special_files(x, y)	0
-#define	bozo_auth_check_401(x, y)		do { /* nothing */ } while (0)
-#define	bozo_auth_cgi_setenv(x, y)		do { /* nothing */ } while (0)
+#define	bozo_auth_check_401(x, y)		bozo_noop
+#define	bozo_auth_cgi_setenv(x, y)		bozo_noop
 #define	bozo_auth_cgi_count(x)			0
 #endif /* DO_HTPASSWD */
 
@@ -287,9 +306,9 @@ int	bozo_process_lua(bozo_httpreq_t *);
 
 /* daemon-bozo.c */
 #ifdef NO_DAEMON_MODE
-#define bozo_daemon_init(x)				do { /* nothing */ } while (0)
+#define bozo_daemon_init(x)				bozo_noop
 #define bozo_daemon_fork(x)				0
-#define bozo_daemon_closefds(x)				do { /* nothing */ } while (0)
+#define bozo_daemon_closefds(x)				bozo_noop
 #else
 void	bozo_daemon_init(bozohttpd_t *);
 int	bozo_daemon_fork(bozohttpd_t *);
@@ -299,9 +318,11 @@ void	bozo_daemon_closefds(bozohttpd_t *)
 
 /* tilde-luzah-bozo.c */
 #ifdef NO_USER_SUPPORT
-#define bozo_user_transform(a, c)			0
+#define bozo_user_transform(x)				0
+#define bozo_user_free(x)					0
 #else
-int	bozo_user_transform(bozo_httpreq_t *, int *);
+int	bozo_user_transform(bozo_httpreq_t *);
+#define bozo_user_free(x)					free(x)
 #endif /* NO_USER_SUPPORT */
 
 
@@ -319,7 +340,8 @@ const char *bozo_content_encoding(bozo_h
 bozo_content_map_t *bozo_match_content_map(bozohttpd_t *, const char *, int);
 bozo_content_map_t *bozo_get_content_map(bozohttpd_t *, const char *);
 #ifndef NO_DYNAMIC_CONTENT
-void	bozo_add_content_map_mime(bozohttpd_t *, const char *, const char *, const char *, const char *);
+void	bozo_add_content_map_mime(bozohttpd_t *, const char *, const char *,
+				  const char *, const char *);
 #endif
 
 /* I/O */
@@ -330,15 +352,19 @@ int bozo_flush(bozohttpd_t *, FILE *);
 
 /* misc */
 int bozo_init_httpd(bozohttpd_t *);
-int bozo_init_prefs(bozoprefs_t *);
+int bozo_init_prefs(bozohttpd_t *, bozoprefs_t *);
 int bozo_set_defaults(bozohttpd_t *, bozoprefs_t *);
 int bozo_setup(bozohttpd_t *, bozoprefs_t *, const char *, const char *);
 bozo_httpreq_t *bozo_read_request(bozohttpd_t *);
 void bozo_process_request(bozo_httpreq_t *);
 void bozo_clean_request(bozo_httpreq_t *);
+bozoheaders_t *addmerge_reqheader(bozo_httpreq_t *, const char *,
+				  const char *, ssize_t);
+bozoheaders_t *addmerge_replheader(bozo_httpreq_t *, const char *,
+				   const char *, ssize_t);
 
 /* variables */
-int bozo_set_pref(bozoprefs_t *, const char *, const char *);
+int bozo_set_pref(bozohttpd_t *, bozoprefs_t *, const char *, const char *);
 char *bozo_get_pref(bozoprefs_t *, const char *);
 
 #endif	/* BOZOHTTOPD_H_ */

Index: src/libexec/httpd/cgi-bozo.c
diff -u src/libexec/httpd/cgi-bozo.c:1.20.8.1 src/libexec/httpd/cgi-bozo.c:1.20.8.2
--- src/libexec/httpd/cgi-bozo.c:1.20.8.1	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/cgi-bozo.c	Fri Apr 15 19:38:13 2016
@@ -1,9 +1,9 @@
-/*	$NetBSD: cgi-bozo.c,v 1.20.8.1 2014/07/09 09:47:10 msaitoh Exp $	*/
+/*	$NetBSD: cgi-bozo.c,v 1.20.8.2 2016/04/15 19:38:13 snj Exp $	*/
 
 /*	$eterna: cgi-bozo.c,v 1.40 2011/11/18 09:21:15 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2014 Matthew R. Green
+ * Copyright (c) 1997-2015 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -75,22 +75,23 @@ content_cgihandler(bozohttpd_t *httpd, b
 }
 
 static int
-parse_header(bozohttpd_t *httpd, const char *str, ssize_t len, char **hdr_str,
-		char **hdr_val)
+parse_header(bozo_httpreq_t *request, const char *str, ssize_t len,
+	     char **hdr_str, char **hdr_val)
 {
+	struct	bozohttpd_t *httpd = request->hr_httpd;
 	char	*name, *value;
 
 	/* if the string passed is zero-length bail out */
 	if (*str == '\0')
 		return -1;
 
-	value = bozostrdup(httpd, str);
+	value = bozostrdup(httpd, request, str);
 
 	/* locate the ':' separator in the header/value */
 	name = bozostrnsep(&value, ":", &len);
 
 	if (NULL == name || -1 == len) {
-		free(name);
+		free(value);
 		return -1;
 	}
 
@@ -127,7 +128,7 @@ finish_cgi_output(bozohttpd_t *httpd, bo
 		(str = bozodgetln(httpd, in, &len, bozo_read)) != NULL) {
 		char	*hdr_name, *hdr_value;
 
-		if (parse_header(httpd, str, len, &hdr_name, &hdr_value))
+		if (parse_header(request, str, len, &hdr_name, &hdr_value))
 			break;
 
 		/*
@@ -194,7 +195,7 @@ finish_cgi_output(bozohttpd_t *httpd, bo
 				rbytes -= wbytes;
 				bp += wbytes;
 			} else
-				bozo_err(httpd, 1,
+				bozoerr(httpd, 1,
 					"cgi output write failed: %s",
 					strerror(errno));
 		}		
@@ -211,10 +212,140 @@ append_index_html(bozohttpd_t *httpd, ch
 		"append_index_html: url adjusted to `%s'", *url));
 }
 
+/* This function parse search-string according to section 4.4 of RFC3875 */
+static char **
+parse_search_string(bozo_httpreq_t *request, const char *query, size_t *args_len)
+{
+	struct	bozohttpd_t *httpd = request->hr_httpd;
+	size_t i;
+	char *s, *str, **args;
+	
+	*args_len = 0;
+
+	/* URI MUST not contain any unencoded '=' - RFC3875, section 4.4 */
+	if (strchr(query, '=')) {
+		return NULL;
+	}
+
+	str = bozostrdup(httpd, request, query);
+
+	/*
+	 * there's no more arguments than '+' chars in the query string as it's
+	 * the separator
+	 */
+	*args_len = 1;
+	/* count '+' in str */
+	for (s = str; (s = strchr(s, '+')); (*args_len)++);
+	
+	args = bozomalloc(httpd, sizeof(*args) * (*args_len + 1));
+ 
+	args[0] = str;
+	args[*args_len] = NULL;
+	for (s = str, i = 0; (s = strchr(s, '+'));) {
+		*s = '\0';
+		s++;
+		args[i++] = s;
+	}
+
+	/*
+	 * check if search-strings are valid:
+	 *
+	 * RFC3875, section 4.4:
+	 *
+	 * search-string = search-word *( "+" search-word )
+	 * search-word   = 1*schar
+	 * schar		 = unreserved | escaped | xreserved
+	 * xreserved	 = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "," |
+	 *		   "$"
+	 * 
+	 * section 2.3:
+	 *
+	 * hex	      = digit | "A" | "B" | "C" | "D" | "E" | "F" | "a" |
+	 *		"b" | "c" | "d" | "e" | "f"
+	 * escaped    = "%" hex hex
+	 * unreserved = alpha | digit | mark
+	 * mark	      = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+	 *
+	 * section 2.2:
+	 *
+	 * alpha	= lowalpha | hialpha
+	 * lowalpha	= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
+	 *		  "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
+	 *		  "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
+	 *		  "y" | "z"
+	 * hialpha	= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" |
+	 *		  "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" |
+	 *		  "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" |
+	 *	          "Y" | "Z"  
+	 * digit        = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+	 *		  "8" | "9"
+	 */
+#define	UNRESERVED_CHAR	"-_.!~*'()"
+#define	XRESERVED_CHAR	";/?:@&=,$"
+
+	for (i = 0; i < *args_len; i++) {
+		s = args[i];
+		/* search-word MUST have at least one schar */
+		if (*s == '\0')
+			goto parse_err;
+		while(*s) {
+			/* check if it's unreserved */
+			if (isalpha((int)*s) || isdigit((int)*s) ||
+			    strchr(UNRESERVED_CHAR, *s)) {
+				s++;
+				continue;
+			}
+
+			/* check if it's escaped */
+			if (*s == '%') {
+				if (s[1] == '\0' || s[2] == '\0')
+					goto parse_err;
+				if (!isxdigit((int)s[1]) ||
+				    !isxdigit((int)s[2]))
+					goto parse_err;
+				s += 3;
+				continue;
+			}
+
+			/* check if it's xreserved */
+
+			if (strchr(XRESERVED_CHAR, *s)) {
+				s++;
+				continue;
+			}
+
+			goto parse_err;
+		}
+	}
+
+	/* decode percent encoding */
+	for (i = 0; i < *args_len; i++) {
+		if (bozo_decode_url_percent(request, args[i]))
+			goto parse_err;
+	}
+
+	/* allocate each arg separately */
+	for (i = 0; i < *args_len; i++)
+		args[i] = bozostrdup(httpd, request, args[i]);
+	free(str);
+
+	return args;
+
+parse_err:
+
+	free (*args);
+	free (str);
+	*args = NULL;
+	*args_len = 0;
+
+	return 0;
+
+}
+
 void
 bozo_cgi_setbin(bozohttpd_t *httpd, const char *path)
 {
-	httpd->cgibin = strdup(path);
+	httpd->cgibin = bozostrdup(httpd, NULL, path);
 	debug((httpd, DEBUG_OBESE, "cgibin (cgi-bin directory) is %s",
 		httpd->cgibin));
 }
@@ -248,9 +379,9 @@ bozo_process_cgi(bozo_httpreq_t *request
 	bozoheaders_t *headp;
 	const char *type, *clen, *info, *cgihandler;
 	char	*query, *s, *t, *path, *env, *command, *file, *url;
-	char	**envp, **curenvp, *argv[4];
+	char	**envp, **curenvp, **argv, **search_string_argv = NULL;
 	char	*uri;
-	size_t	len;
+	size_t	i, len, search_string_argc = 0;
 	ssize_t rbytes;
 	pid_t	pid;
 	int	envpsize, ix, nph;
@@ -259,26 +390,30 @@ bozo_process_cgi(bozo_httpreq_t *request
 	if (!httpd->cgibin && !httpd->process_cgi)
 		return 0;
 
+#ifndef NO_USER_SUPPORT
+	if (request->hr_user && !httpd->enable_cgi_users)
+		return 0;
+#endif /* !NO_USER_SUPPORT */
+
 	if (request->hr_oldfile && strcmp(request->hr_oldfile, "/") != 0)
 		uri = request->hr_oldfile;
 	else
 		uri = request->hr_file;
 
 	if (uri[0] == '/')
-		file = bozostrdup(httpd, uri);
+		file = bozostrdup(httpd, request, uri);
 	else
-		asprintf(&file, "/%s", uri);
-	if (file == NULL)
-		return 0;
+		bozoasprintf(httpd, &file, "/%s", uri);
 
 	if (request->hr_query && strlen(request->hr_query))
-		query = bozostrdup(httpd, request->hr_query);
+		query = bozostrdup(httpd, request, request->hr_query);
 	else
 		query = NULL;
 
-	asprintf(&url, "%s%s%s", file, query ? "?" : "", query ? query : "");
-	if (url == NULL)
-		goto out;
+	bozoasprintf(httpd, &url, "%s%s%s",
+		     file,
+		     query ? "?" : "",
+		     query ? query : "");
 	debug((httpd, DEBUG_NORMAL, "bozo_process_cgi: url `%s'", url));
 
 	path = NULL;
@@ -307,16 +442,29 @@ bozo_process_cgi(bozo_httpreq_t *request
 	} else if (len - 1 == CGIBIN_PREFIX_LEN)	/* url is "/cgi-bin/" */
 		append_index_html(httpd, &file);
 
+	/* RFC3875  sect. 4.4. - search-string support */
+	if (query != NULL) {
+		search_string_argv = parse_search_string(request, query,
+		    &search_string_argc);
+	}
+
+	debug((httpd, DEBUG_NORMAL, "parse_search_string args no: %lu",
+	    search_string_argc));
+	for (i = 0; i < search_string_argc; i++) {
+		debug((httpd, DEBUG_FAT,
+		    "search_string[%lu]: `%s'", i, search_string_argv[i]));
+	}
+
+	argv = bozomalloc(httpd, sizeof(*argv) * (3 + search_string_argc));
+
 	ix = 0;
 	if (cgihandler) {
 		command = file + 1;
-		path = bozostrdup(httpd, cgihandler);
-		argv[ix++] = path;
-			/* argv[] = [ path, command, query, NULL ] */
+		path = bozostrdup(httpd, request, cgihandler);
 	} else {
 		command = file + CGIBIN_PREFIX_LEN + 1;
 		if ((s = strchr(command, '/')) != NULL) {
-			info = bozostrdup(httpd, s);
+			info = bozostrdup(httpd, request, s);
 			*s = '\0';
 		}
 		path = bozomalloc(httpd,
@@ -324,12 +472,15 @@ bozo_process_cgi(bozo_httpreq_t *request
 		strcpy(path, httpd->cgibin);
 		strcat(path, "/");
 		strcat(path, command);
-			/* argv[] = [ command, query, NULL ] */
 	}
-	argv[ix++] = command;
-	argv[ix++] = query;
-	argv[ix++] = NULL;
 
+	argv[ix++] = path;
+
+	/* copy search-string args */
+	for (i = 0; i < search_string_argc; i++)
+		argv[ix++] = search_string_argv[i];
+
+	argv[ix++] = NULL;
 	nph = strncmp(command, "nph-", 4) == 0;
 
 	type = request->hr_content_type;
@@ -395,8 +546,11 @@ bozo_process_cgi(bozo_httpreq_t *request
 	bozo_setenv(httpd, "REQUEST_URI", uri, curenvp++);
 	bozo_setenv(httpd, "DATE_GMT", bozo_http_date(date, sizeof(date)),
 			curenvp++);
+	/* RFC3875 section 4.1.7 says that QUERY_STRING MUST be defined. */ 
 	if (query && *query)
 		bozo_setenv(httpd, "QUERY_STRING", query, curenvp++);
+	else
+		bozo_setenv(httpd, "QUERY_STRING", "", curenvp++);
 	if (info && *info)
 		bozo_setenv(httpd, "PATH_INFO", info, curenvp++);
 	if (type && *type)
@@ -413,21 +567,23 @@ bozo_process_cgi(bozo_httpreq_t *request
 		bozo_setenv(httpd, "REMOTE_ADDR", request->hr_remoteaddr,
 				curenvp++);
 	/*
-	 * XXX Apache does this when invoking content handlers, and PHP
-	 * XXX 5.3 requires it as a "security" measure.
+	 * Apache does this when invoking content handlers, and PHP
+	 * 5.3 requires it as a "security" measure.
 	 */
 	if (cgihandler)
 		bozo_setenv(httpd, "REDIRECT_STATUS", "200", curenvp++);
 	bozo_auth_cgi_setenv(request, &curenvp);
 
-	free(file);
-	free(url);
+	debug((httpd, DEBUG_FAT, "bozo_process_cgi: going exec %s with args:",
+	    path));
 
-	debug((httpd, DEBUG_FAT, "bozo_process_cgi: going exec %s, %s %s %s",
-	    path, argv[0], strornull(argv[1]), strornull(argv[2])));
+	for (i = 0; argv[i] != NULL; i++) {
+		debug((httpd, DEBUG_FAT, "bozo_process_cgi: argv[%lu] = `%s'",
+		    i, argv[i]));
+	}
 
 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sv) == -1)
-		bozo_err(httpd, 1, "child socketpair failed: %s",
+		bozoerr(httpd, 1, "child socketpair failed: %s",
 				strerror(errno));
 
 	/*
@@ -438,7 +594,7 @@ bozo_process_cgi(bozo_httpreq_t *request
 	 */
 	switch (fork()) {
 	case -1: /* eep, failure */
-		bozo_err(httpd, 1, "child fork failed: %s", strerror(errno));
+		bozoerr(httpd, 1, "child fork failed: %s", strerror(errno));
 		/*NOTREACHED*/
 	case 0:
 		close(sv[0]);
@@ -450,19 +606,26 @@ bozo_process_cgi(bozo_httpreq_t *request
 		bozo_daemon_closefds(httpd);
 
 		if (-1 == execve(path, argv, envp))
-			bozo_err(httpd, 1, "child exec failed: %s: %s",
+			bozoerr(httpd, 1, "child exec failed: %s: %s",
 			      path, strerror(errno));
 		/* NOT REACHED */
-		bozo_err(httpd, 1, "child execve returned?!");
+		bozoerr(httpd, 1, "child execve returned?!");
 	}
 
+	free(query);
+	free(file);
+	free(url);
+	for (i = 0; i < search_string_argc; i++)
+		free(search_string_argv[i]);
+	free(search_string_argv);
+
 	close(sv[1]);
 
 	/* parent: read from stdin (bozo_read()) write to sv[0] */
 	/* child: read from sv[0] (bozo_write()) write to stdout */
 	pid = fork();
 	if (pid == -1)
-		bozo_err(httpd, 1, "io child fork failed: %s", strerror(errno));
+		bozoerr(httpd, 1, "io child fork failed: %s", strerror(errno));
 	else if (pid == 0) {
 		/* child reader/writer */
 		close(STDIN_FILENO);
@@ -486,7 +649,7 @@ bozo_process_cgi(bozo_httpreq_t *request
 				rbytes -= wbytes;
 				bp += wbytes;
 			} else
-				bozo_err(httpd, 1, "write failed: %s",
+				bozoerr(httpd, 1, "write failed: %s",
 					strerror(errno));
 		}		
 	}
@@ -494,6 +657,10 @@ bozo_process_cgi(bozo_httpreq_t *request
 	exit(0);
 
  out:
+
+	for (i = 0; i < search_string_argc; i++)
+		free(search_string_argv[i]);
+	free(search_string_argv);
 	free(query);
 	free(file);
 	free(url);
@@ -503,7 +670,8 @@ bozo_process_cgi(bozo_httpreq_t *request
 #ifndef NO_DYNAMIC_CONTENT
 /* cgi maps are simple ".postfix /path/to/prog" */
 void
-bozo_add_content_map_cgi(bozohttpd_t *httpd, const char *arg, const char *cgihandler)
+bozo_add_content_map_cgi(bozohttpd_t *httpd, const char *arg,
+                         const char *cgihandler)
 {
 	bozo_content_map_t *map;
 
@@ -514,7 +682,6 @@ bozo_add_content_map_cgi(bozohttpd_t *ht
 
 	map = bozo_get_content_map(httpd, arg);
 	map->name = arg;
-	map->namelen = strlen(map->name);
 	map->type = map->encoding = map->encoding11 = NULL;
 	map->cgihandler = cgihandler;
 }

Index: src/libexec/httpd/content-bozo.c
diff -u src/libexec/httpd/content-bozo.c:1.7.8.1 src/libexec/httpd/content-bozo.c:1.7.8.2
--- src/libexec/httpd/content-bozo.c:1.7.8.1	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/content-bozo.c	Fri Apr 15 19:38:13 2016
@@ -1,9 +1,9 @@
-/*	$NetBSD: content-bozo.c,v 1.7.8.1 2014/07/09 09:47:10 msaitoh Exp $	*/
+/*	$NetBSD: content-bozo.c,v 1.7.8.2 2016/04/15 19:38:13 snj Exp $	*/
 
 /*	$eterna: content-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $	*/
 
 /*
- * Copyright (c) 1997-2014 Matthew R. Green
+ * Copyright (c) 1997-2015 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -47,141 +47,145 @@
  */
 
 static bozo_content_map_t static_content_map[] = {
-	{ ".html",	5, "text/html",			"",		"", NULL },
-	{ ".htm",	4, "text/html",			"",		"", NULL },
-	{ ".gif",	4, "image/gif",			"",		"", NULL },
-	{ ".jpeg",	5, "image/jpeg",		"",		"", NULL },
-	{ ".jpg",	4, "image/jpeg",		"",		"", NULL },
-	{ ".jpe",	4, "image/jpeg",		"",		"", NULL },
-	{ ".png",	4, "image/png",			"",		"", NULL },
-	{ ".mp3",	4, "audio/mpeg",		"",		"", NULL },
-	{ ".css",	4, "text/css",			"",		"", NULL },
-	{ ".txt",	4, "text/plain",		"",		"", NULL },
-	{ ".swf",	4, "application/x-shockwave-flash","",		"", NULL },
-	{ ".dcr",	4, "application/x-director",	"",		"", NULL },
-	{ ".pac",	4, "application/x-ns-proxy-autoconfig", "",	"", NULL },
-	{ ".pa",	3, "application/x-ns-proxy-autoconfig", "",	"", NULL },
-	{ ".tar",	4, "multipart/x-tar",		"",		"", NULL },
-	{ ".gtar",	5, "multipart/x-gtar",		"",		"", NULL },
-	{ ".tar.Z",	6, "multipart/x-tar",		"x-compress",	"compress", NULL },
-	{ ".tar.gz",	7, "multipart/x-tar",		"x-gzip",	"gzip", NULL },
-	{ ".taz",	4, "multipart/x-tar",		"x-gzip",	"gzip", NULL },
-	{ ".tgz",	4, "multipart/x-tar",		"x-gzip",	"gzip", NULL },
-	{ ".tar.z",	6, "multipart/x-tar",		"x-pack",	"x-pack", NULL },
-	{ ".Z",		2, "application/x-compress",	"x-compress",	"compress", NULL },
-	{ ".gz",	3, "application/x-gzip",	"x-gzip",	"gzip", NULL },
-	{ ".z",		2, "unknown",			"x-pack",	"x-pack", NULL },
-	{ ".bz2",	4, "application/x-bzip2",	"x-bzip2",	"x-bzip2", NULL },
-	{ ".ogg",	4, "application/x-ogg",		"",		"", NULL },
-	{ ".mkv",	4, "video/x-matroska",		"",		"", NULL },
-	{ ".xbel",	5, "text/xml",			"",		"", NULL },
-	{ ".xml",	4, "text/xml",			"",		"", NULL },
-	{ ".xsl",	4, "text/xml",			"",		"", NULL },
-	{ ".hqx",	4, "application/mac-binhex40",	"",		"", NULL },
-	{ ".cpt",	4, "application/mac-compactpro","",		"", NULL },
-	{ ".doc",	4, "application/msword",	"",		"", NULL },
-	{ ".bin",	4, "application/octet-stream",	"",		"", NULL },
-	{ ".dms",	4, "application/octet-stream",	"",		"", NULL },
-	{ ".lha",	4, "application/octet-stream",	"",		"", NULL },
-	{ ".lzh",	4, "application/octet-stream",	"",		"", NULL },
-	{ ".exe",	4, "application/octet-stream",	"",		"", NULL },
-	{ ".class",	6, "application/octet-stream",	"",		"", NULL },
-	{ ".oda",	4, "application/oda",		"",		"", NULL },
-	{ ".pdf",	4, "application/pdf",		"",		"", NULL },
-	{ ".ai",	3, "application/postscript",	"",		"", NULL },
-	{ ".eps",	4, "application/postscript",	"",		"", NULL },
-	{ ".ps",	3, "application/postscript",	"",		"", NULL },
-	{ ".ppt",	4, "application/powerpoint",	"",		"", NULL },
-	{ ".rtf",	4, "application/rtf",		"",		"", NULL },
-	{ ".bcpio",	6, "application/x-bcpio",	"",		"", NULL },
-	{ ".torrent",	8, "application/x-bittorrent",	"",		"", NULL },
-	{ ".vcd",	4, "application/x-cdlink",	"",		"", NULL },
-	{ ".cpio",	5, "application/x-cpio",	"",		"", NULL },
-	{ ".csh",	4, "application/x-csh",		"",		"", NULL },
-	{ ".dir",	4, "application/x-director",	"",		"", NULL },
-	{ ".dxr",	4, "application/x-director",	"",		"", NULL },
-	{ ".dvi",	4, "application/x-dvi",		"",		"", NULL },
-	{ ".hdf",	4, "application/x-hdf",		"",		"", NULL },
-	{ ".cgi",	4, "application/x-httpd-cgi",	"",		"", NULL },
-	{ ".skp",	4, "application/x-koan",	"",		"", NULL },
-	{ ".skd",	4, "application/x-koan",	"",		"", NULL },
-	{ ".skt",	4, "application/x-koan",	"",		"", NULL },
-	{ ".skm",	4, "application/x-koan",	"",		"", NULL },
-	{ ".latex",	6, "application/x-latex",	"",		"", NULL },
-	{ ".mif",	4, "application/x-mif",		"",		"", NULL },
-	{ ".nc",	3, "application/x-netcdf",	"",		"", NULL },
-	{ ".cdf",	4, "application/x-netcdf",	"",		"", NULL },
-	{ ".patch",	6, "application/x-patch",	"",		"", NULL },
-	{ ".sh",	3, "application/x-sh",		"",		"", NULL },
-	{ ".shar",	5, "application/x-shar",	"",		"", NULL },
-	{ ".sit",	4, "application/x-stuffit",	"",		"", NULL },
-	{ ".sv4cpio",	8, "application/x-sv4cpio",	"",		"", NULL },
-	{ ".sv4crc",	7, "application/x-sv4crc",	"",		"", NULL },
-	{ ".tar",	4, "application/x-tar",		"",		"", NULL },
-	{ ".tcl",	4, "application/x-tcl",		"",		"", NULL },
-	{ ".tex",	4, "application/x-tex",		"",		"", NULL },
-	{ ".texinfo",	8, "application/x-texinfo",	"",		"", NULL },
-	{ ".texi",	5, "application/x-texinfo",	"",		"", NULL },
-	{ ".t",		2, "application/x-troff",	"",		"", NULL },
-	{ ".tr",	3, "application/x-troff",	"",		"", NULL },
-	{ ".roff",	5, "application/x-troff",	"",		"", NULL },
-	{ ".man",	4, "application/x-troff-man",	"",		"", NULL },
-	{ ".me",	3, "application/x-troff-me",	"",		"", NULL },
-	{ ".ms",	3, "application/x-troff-ms",	"",		"", NULL },
-	{ ".ustar",	6, "application/x-ustar",	"",		"", NULL },
-	{ ".src",	4, "application/x-wais-source",	"",		"", NULL },
-	{ ".zip",	4, "application/zip",		"",		"", NULL },
-	{ ".au",	3, "audio/basic",		"",		"", NULL },
-	{ ".snd",	4, "audio/basic",		"",		"", NULL },
-	{ ".mpga",	5, "audio/mpeg",		"",		"", NULL },
-	{ ".mp2",	4, "audio/mpeg",		"",		"", NULL },
-	{ ".aif",	4, "audio/x-aiff",		"",		"", NULL },
-	{ ".aiff",	5, "audio/x-aiff",		"",		"", NULL },
-	{ ".aifc",	5, "audio/x-aiff",		"",		"", NULL },
-	{ ".ram",	4, "audio/x-pn-realaudio",	"",		"", NULL },
-	{ ".rpm",	4, "audio/x-pn-realaudio-plugin","",		"", NULL },
-	{ ".ra",	3, "audio/x-realaudio",		"",		"", NULL },
-	{ ".wav",	4, "audio/x-wav",		"",		"", NULL },
-	{ ".pdb",	4, "chemical/x-pdb",		"",		"", NULL },
-	{ ".xyz",	4, "chemical/x-pdb",		"",		"", NULL },
-	{ ".ief",	4, "image/ief",			"",		"", NULL },
-	{ ".tiff",	5, "image/tiff",		"",		"", NULL },
-	{ ".tif",	4, "image/tiff",		"",		"", NULL },
-	{ ".ras",	4, "image/x-cmu-raster",	"",		"", NULL },
-	{ ".pnm",	4, "image/x-portable-anymap",	"",		"", NULL },
-	{ ".pbm",	4, "image/x-portable-bitmap",	"",		"", NULL },
-	{ ".pgm",	4, "image/x-portable-graymap",	"",		"", NULL },
-	{ ".ppm",	4, "image/x-portable-pixmap",	"",		"", NULL },
-	{ ".rgb",	4, "image/x-rgb",		"",		"", NULL },
-	{ ".xbm",	4, "image/x-xbitmap",		"",		"", NULL },
-	{ ".xpm",	4, "image/x-xpixmap",		"",		"", NULL },
-	{ ".xwd",	4, "image/x-xwindowdump",	"",		"", NULL },
-	{ ".rtx",	4, "text/richtext",		"",		"", NULL },
-	{ ".tsv",	4, "text/tab-separated-values",	"",		"", NULL },
-	{ ".etx",	4, "text/x-setext",		"",		"", NULL },
-	{ ".sgml",	5, "text/x-sgml",		"",		"", NULL },
-	{ ".sgm",	4, "text/x-sgml",		"",		"", NULL },
-	{ ".mpeg",	5, "video/mpeg",		"",		"", NULL },
-	{ ".mpg",	4, "video/mpeg",		"",		"", NULL },
-	{ ".mpe",	4, "video/mpeg",		"",		"", NULL },
-	{ ".mp4",	4, "video/mp4",			"",		"", NULL },
-	{ ".qt",	3, "video/quicktime",		"",		"", NULL },
-	{ ".mov",	4, "video/quicktime",		"",		"", NULL },
-	{ ".avi",	4, "video/x-msvideo",		"",		"", NULL },
-	{ ".movie",	6, "video/x-sgi-movie",		"",		"", NULL },
-	{ ".ice",	4, "x-conference/x-cooltalk",	"",		"", NULL },
-	{ ".wrl",	4, "x-world/x-vrml",		"",		"", NULL },
-	{ ".vrml",	5, "x-world/x-vrml",		"",		"", NULL },
-	{ ".svg",	5, "image/svg+xml",		"",		"", NULL },
-	{ NULL,		0, NULL,		NULL,		NULL, NULL }
+	{ ".html",	"text/html",			"",		"", NULL },
+	{ ".htm",	"text/html",			"",		"", NULL },
+	{ ".gif",	"image/gif",			"",		"", NULL },
+	{ ".jpeg",	"image/jpeg",			"",		"", NULL },
+	{ ".jpg",	"image/jpeg",			"",		"", NULL },
+	{ ".jpe",	"image/jpeg",			"",		"", NULL },
+	{ ".png",	"image/png",			"",		"", NULL },
+	{ ".mp3",	"audio/mpeg",			"",		"", NULL },
+	{ ".css",	"text/css",			"",		"", NULL },
+	{ ".txt",	"text/plain",			"",		"", NULL },
+	{ ".swf",	"application/x-shockwave-flash","",		"", NULL },
+	{ ".dcr",	"application/x-director",	"",		"", NULL },
+	{ ".pac",	"application/x-ns-proxy-autoconfig", "",	"", NULL },
+	{ ".pa",	"application/x-ns-proxy-autoconfig", "",	"", NULL },
+	{ ".tar",	"multipart/x-tar",		"",		"", NULL },
+	{ ".gtar",	"multipart/x-gtar",		"",		"", NULL },
+	{ ".tar.Z",	"multipart/x-tar",		"x-compress",	"compress", NULL },
+	{ ".tar.gz",	"multipart/x-tar",		"x-gzip",	"gzip", NULL },
+	{ ".taz",	"multipart/x-tar",		"x-gzip",	"gzip", NULL },
+	{ ".tgz",	"multipart/x-tar",		"x-gzip",	"gzip", NULL },
+	{ ".tar.z",	"multipart/x-tar",		"x-pack",	"x-pack", NULL },
+	{ ".Z",		"application/x-compress",	"x-compress",	"compress", NULL },
+	{ ".gz",	"application/x-gzip",		"x-gzip",	"gzip", NULL },
+	{ ".z",		"unknown",			"x-pack",	"x-pack", NULL },
+	{ ".bz2",	"application/x-bzip2",		"x-bzip2",	"x-bzip2", NULL },
+	{ ".ogg",	"application/x-ogg",		"",		"", NULL },
+	{ ".mkv",	"video/x-matroska",		"",		"", NULL },
+	{ ".xbel",	"text/xml",			"",		"", NULL },
+	{ ".xml",	"text/xml",			"",		"", NULL },
+	{ ".xsl",	"text/xml",			"",		"", NULL },
+	{ ".hqx",	"application/mac-binhex40",	"",		"", NULL },
+	{ ".cpt",	"application/mac-compactpro",	"",		"", NULL },
+	{ ".doc",	"application/msword",		"",		"", NULL },
+	{ ".bin",	"application/octet-stream",	"",		"", NULL },
+	{ ".dms",	"application/octet-stream",	"",		"", NULL },
+	{ ".lha",	"application/octet-stream",	"",		"", NULL },
+	{ ".lzh",	"application/octet-stream",	"",		"", NULL },
+	{ ".exe",	"application/octet-stream",	"",		"", NULL },
+	{ ".class",	"application/octet-stream",	"",		"", NULL },
+	{ ".oda",	"application/oda",		"",		"", NULL },
+	{ ".pdf",	"application/pdf",		"",		"", NULL },
+	{ ".ai",	"application/postscript",	"",		"", NULL },
+	{ ".eps",	"application/postscript",	"",		"", NULL },
+	{ ".ps",	"application/postscript",	"",		"", NULL },
+	{ ".ppt",	"application/powerpoint",	"",		"", NULL },
+	{ ".rtf",	"application/rtf",		"",		"", NULL },
+	{ ".bcpio",	"application/x-bcpio",		"",		"", NULL },
+	{ ".torrent",	"application/x-bittorrent",	"",		"", NULL },
+	{ ".vcd",	"application/x-cdlink",		"",		"", NULL },
+	{ ".cpio",	"application/x-cpio",		"",		"", NULL },
+	{ ".csh",	"application/x-csh",		"",		"", NULL },
+	{ ".dir",	"application/x-director",	"",		"", NULL },
+	{ ".dxr",	"application/x-director",	"",		"", NULL },
+	{ ".dvi",	"application/x-dvi",		"",		"", NULL },
+	{ ".hdf",	"application/x-hdf",		"",		"", NULL },
+	{ ".cgi",	"application/x-httpd-cgi",	"",		"", NULL },
+	{ ".skp",	"application/x-koan",		"",		"", NULL },
+	{ ".skd",	"application/x-koan",		"",		"", NULL },
+	{ ".skt",	"application/x-koan",		"",		"", NULL },
+	{ ".skm",	"application/x-koan",		"",		"", NULL },
+	{ ".latex",	"application/x-latex",		"",		"", NULL },
+	{ ".mif",	"application/x-mif",		"",		"", NULL },
+	{ ".nc",	"application/x-netcdf",		"",		"", NULL },
+	{ ".cdf",	"application/x-netcdf",		"",		"", NULL },
+	{ ".patch",	"application/x-patch",		"",		"", NULL },
+	{ ".sh",	"application/x-sh",		"",		"", NULL },
+	{ ".shar",	"application/x-shar",		"",		"", NULL },
+	{ ".sit",	"application/x-stuffit",	"",		"", NULL },
+	{ ".sv4cpio",	"application/x-sv4cpio",	"",		"", NULL },
+	{ ".sv4crc",	"application/x-sv4crc",		"",		"", NULL },
+	{ ".tar",	"application/x-tar",		"",		"", NULL },
+	{ ".tcl",	"application/x-tcl",		"",		"", NULL },
+	{ ".tex",	"application/x-tex",		"",		"", NULL },
+	{ ".texinfo",	"application/x-texinfo",	"",		"", NULL },
+	{ ".texi",	"application/x-texinfo",	"",		"", NULL },
+	{ ".t",		"application/x-troff",		"",		"", NULL },
+	{ ".tr",	"application/x-troff",		"",		"", NULL },
+	{ ".roff",	"application/x-troff",		"",		"", NULL },
+	{ ".man",	"application/x-troff-man",	"",		"", NULL },
+	{ ".me",	"application/x-troff-me",	"",		"", NULL },
+	{ ".ms",	"application/x-troff-ms",	"",		"", NULL },
+	{ ".ustar",	"application/x-ustar",		"",		"", NULL },
+	{ ".src",	"application/x-wais-source",	"",		"", NULL },
+	{ ".zip",	"application/zip",		"",		"", NULL },
+	{ ".au",	"audio/basic",			"",		"", NULL },
+	{ ".snd",	"audio/basic",			"",		"", NULL },
+	{ ".mpga",	"audio/mpeg",			"",		"", NULL },
+	{ ".mp2",	"audio/mpeg",			"",		"", NULL },
+	{ ".aif",	"audio/x-aiff",			"",		"", NULL },
+	{ ".aiff",	"audio/x-aiff",			"",		"", NULL },
+	{ ".aifc",	"audio/x-aiff",			"",		"", NULL },
+	{ ".ram",	"audio/x-pn-realaudio",		"",		"", NULL },
+	{ ".rpm",	"audio/x-pn-realaudio-plugin",	"",		"", NULL },
+	{ ".ra",	"audio/x-realaudio",		"",		"", NULL },
+	{ ".wav",	"audio/x-wav",			"",		"", NULL },
+	{ ".pdb",	"chemical/x-pdb",		"",		"", NULL },
+	{ ".xyz",	"chemical/x-pdb",		"",		"", NULL },
+	{ ".ief",	"image/ief",			"",		"", NULL },
+	{ ".tiff",	"image/tiff",			"",		"", NULL },
+	{ ".tif",	"image/tiff",			"",		"", NULL },
+	{ ".ras",	"image/x-cmu-raster",		"",		"", NULL },
+	{ ".pnm",	"image/x-portable-anymap",	"",		"", NULL },
+	{ ".pbm",	"image/x-portable-bitmap",	"",		"", NULL },
+	{ ".pgm",	"image/x-portable-graymap",	"",		"", NULL },
+	{ ".ppm",	"image/x-portable-pixmap",	"",		"", NULL },
+	{ ".rgb",	"image/x-rgb",			"",		"", NULL },
+	{ ".xbm",	"image/x-xbitmap",		"",		"", NULL },
+	{ ".xpm",	"image/x-xpixmap",		"",		"", NULL },
+	{ ".xwd",	"image/x-xwindowdump",		"",		"", NULL },
+	{ ".rtx",	"text/richtext",		"",		"", NULL },
+	{ ".tsv",	"text/tab-separated-values",	"",		"", NULL },
+	{ ".etx",	"text/x-setext",		"",		"", NULL },
+	{ ".sgml",	"text/x-sgml",			"",		"", NULL },
+	{ ".sgm",	"text/x-sgml",			"",		"", NULL },
+	{ ".mpeg",	"video/mpeg",			"",		"", NULL },
+	{ ".mpg",	"video/mpeg",			"",		"", NULL },
+	{ ".mpe",	"video/mpeg",			"",		"", NULL },
+	{ ".ts",	"video/mpeg",			"",		"", NULL },
+	{ ".vob",	"video/mpeg",			"",		"", NULL },
+	{ ".mp4",	"video/mp4",			"",		"", NULL },
+	{ ".qt",	"video/quicktime",		"",		"", NULL },
+	{ ".mov",	"video/quicktime",		"",		"", NULL },
+	{ ".avi",	"video/x-msvideo",		"",		"", NULL },
+	{ ".movie",	"video/x-sgi-movie",		"",		"", NULL },
+	{ ".ice",	"x-conference/x-cooltalk",	"",		"", NULL },
+	{ ".wrl",	"x-world/x-vrml",		"",		"", NULL },
+	{ ".vrml",	"x-world/x-vrml",		"",		"", NULL },
+	{ ".svg",	"image/svg+xml",		"",		"", NULL },
+	{ NULL,		NULL,		NULL,		NULL, NULL }
 };
 
 static bozo_content_map_t *
 search_map(bozo_content_map_t *map, const char *name, size_t len)
 {
 	for ( ; map && map->name; map++) {
-		if (map->namelen < len &&
-		    strcasecmp(map->name, name + (len - map->namelen)) == 0)
+		const size_t namelen = strlen(map->name);
+
+		if (namelen < len &&
+		    strcasecmp(map->name, name + (len - namelen)) == 0)
 			return map;
 	}
 	return NULL;
@@ -254,11 +258,10 @@ bozo_get_content_map(bozohttpd_t *httpd,
 		httpd->dynamic_content_map,
 		(httpd->dynamic_content_map_size + 1) * sizeof *map);
 	if (httpd->dynamic_content_map == NULL)
-		bozo_err(httpd, 1, "out of memory allocating content map");
+		bozoerr(httpd, 1, "out of memory allocating content map");
 	map = &httpd->dynamic_content_map[httpd->dynamic_content_map_size];
 	map->name = map->type = map->encoding = map->encoding11 =
 		map->cgihandler = NULL;
-	map->namelen = 0;
 	map--;
 
 	return map;
@@ -289,7 +292,6 @@ bozo_add_content_map_mime(bozohttpd_t *h
 	map = bozo_get_content_map(httpd, cmap0);
 #define CHECKMAP(s)	(!s || ((s)[0] == '-' && (s)[1] == '\0') ? "" : (s))
 	map->name = CHECKMAP(cmap0);
-	map->namelen = strlen(map->name);
 	map->type = CHECKMAP(cmap1);
 	map->encoding = CHECKMAP(cmap2);
 	map->encoding11 = CHECKMAP(cmap3);

Index: src/libexec/httpd/daemon-bozo.c
diff -u src/libexec/httpd/daemon-bozo.c:1.15.8.1 src/libexec/httpd/daemon-bozo.c:1.15.8.2
--- src/libexec/httpd/daemon-bozo.c:1.15.8.1	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/daemon-bozo.c	Fri Apr 15 19:38:13 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: daemon-bozo.c,v 1.15.8.1 2014/07/09 09:47:10 msaitoh Exp $	*/
+/*	$NetBSD: daemon-bozo.c,v 1.15.8.2 2016/04/15 19:38:13 snj Exp $	*/
 
 /*	$eterna: daemon-bozo.c,v 1.24 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -105,10 +105,10 @@ create_pidfile(bozohttpd_t *httpd)
 		return;
 
 	if (atexit(remove_pidfile) == -1)
-		bozo_err(httpd, 1, "Failed to install pidfile handler");
+		bozoerr(httpd, 1, "Failed to install pidfile handler");
 
 	if ((file = fopen(httpd->pidfile, "w")) == NULL)
-		bozo_err(httpd, 1, "Failed to create pidfile '%s'",
+		bozoerr(httpd, 1, "Failed to create pidfile '%s'",
 		    httpd->pidfile);
 	(void)fprintf(file, "%d\n", getpid());
 	(void)fclose(file);
@@ -138,7 +138,7 @@ bozo_daemon_init(bozohttpd_t *httpd)
 	h.ai_flags = AI_PASSIVE;
 	e = getaddrinfo(httpd->bindaddress, portnum, &h, &r0);
 	if (e)
-		bozo_err(httpd, 1, "getaddrinfo([%s]:%s): %s",
+		bozoerr(httpd, 1, "getaddrinfo([%s]:%s): %s",
 		    httpd->bindaddress ? httpd->bindaddress : "*",
 		    portnum, gai_strerror(e));
 	for (r = r0; r != NULL; r = r->ai_next)
@@ -151,7 +151,7 @@ bozo_daemon_init(bozohttpd_t *httpd)
 			continue;
 		if (setsockopt(httpd->sock[i], SOL_SOCKET, SO_REUSEADDR, &on,
 		    sizeof(on)) == -1)
-			bozo_warn(httpd, "setsockopt SO_REUSEADDR: %s",
+			bozowarn(httpd, "setsockopt SO_REUSEADDR: %s",
 			    strerror(errno));
 		if (bind(httpd->sock[i], r->ai_addr, r->ai_addrlen) == -1)
 			continue;
@@ -163,7 +163,7 @@ bozo_daemon_init(bozohttpd_t *httpd)
 		i++;
 	}
 	if (i == 0)
-		bozo_err(httpd, 1, "could not find any addresses to bind");
+		bozoerr(httpd, 1, "could not find any addresses to bind");
 	httpd->nsock = i;
 	freeaddrinfo(r0);
 
@@ -172,7 +172,7 @@ bozo_daemon_init(bozohttpd_t *httpd)
 
 	create_pidfile(httpd);
 
-	bozo_warn(httpd, "started in daemon mode as `%s' port `%s' root `%s'",
+	bozowarn(httpd, "started in daemon mode as `%s' port `%s' root `%s'",
 	    httpd->virthostname, portnum, httpd->slashdir);
 
 	signal(SIGHUP, controlled_exit);
@@ -209,13 +209,13 @@ daemon_poll_err(bozohttpd_t *httpd, int 
 	if ((httpd->fds[idx].revents & (POLLNVAL|POLLERR|POLLHUP)) == 0)
 		return 0;
 
-	bozo_warn(httpd, "poll on fd %d pid %d revents %d: %s",
+	bozowarn(httpd, "poll on fd %d pid %d revents %d: %s",
 	    httpd->fds[idx].fd, getpid(), httpd->fds[idx].revents,
 	    strerror(errno));
-	bozo_warn(httpd, "nsock = %d", httpd->nsock);
+	bozowarn(httpd, "nsock = %d", httpd->nsock);
 	close(httpd->sock[idx]);
 	httpd->nsock--;
-	bozo_warn(httpd, "nsock now = %d", httpd->nsock);
+	bozowarn(httpd, "nsock now = %d", httpd->nsock);
 	/* no sockets left */
 	if (httpd->nsock == 0)
 		exit(0);
@@ -271,7 +271,7 @@ again:
 			/* fail on programmer errors */
 			if (errno == EFAULT ||
 			    errno == EINVAL)
-				bozo_err(httpd, 1, "poll: %s",
+				bozoerr(httpd, 1, "poll: %s",
 					strerror(errno));
 
 			/* sleep on some temporary kernel failures */
@@ -294,7 +294,7 @@ again:
 			if (fd == -1) {
 				if (errno == EFAULT ||
 				    errno == EINVAL)
-					bozo_err(httpd, 1, "accept: %s",
+					bozoerr(httpd, 1, "accept: %s",
 						strerror(errno));
 
 				if (errno == ENOMEM ||
@@ -317,7 +317,7 @@ again:
 
 			switch (fork()) {
 			case -1: /* eep, failure */
-				bozo_warn(httpd, "fork() failed, sleeping for "
+				bozowarn(httpd, "fork() failed, sleeping for "
 					"10 seconds: %s", strerror(errno));
 				close(fd);
 				sleep(10);

Index: src/libexec/httpd/dir-index-bozo.c
diff -u src/libexec/httpd/dir-index-bozo.c:1.13.2.1.4.1 src/libexec/httpd/dir-index-bozo.c:1.13.2.1.4.2
--- src/libexec/httpd/dir-index-bozo.c:1.13.2.1.4.1	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/dir-index-bozo.c	Fri Apr 15 19:38:13 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: dir-index-bozo.c,v 1.13.2.1.4.1 2014/07/09 09:47:10 msaitoh Exp $	*/
+/*	$NetBSD: dir-index-bozo.c,v 1.13.2.1.4.2 2016/04/15 19:38:13 snj Exp $	*/
 
 /*	$eterna: dir-index-bozo.c,v 1.20 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -57,7 +57,7 @@ directory_hr(bozohttpd_t *httpd)
  * output a directory index.  return 1 if it actually did something..
  */
 int
-bozo_dir_index(bozo_httpreq_t *request, const char *dirname, int isindex)
+bozo_dir_index(bozo_httpreq_t *request, const char *dirpath, int isindex)
 {
 	bozohttpd_t *httpd = request->hr_httpd;
 	struct stat sb;
@@ -66,23 +66,23 @@ bozo_dir_index(bozo_httpreq_t *request, 
 	DIR *dp;
 	char buf[MAXPATHLEN];
 	char spacebuf[48];
-	char *file = NULL;
+	char *file = NULL, *printname = NULL;
 	int l, k, j, i;
 
 	if (!isindex || !httpd->dir_indexing)
 		return 0;
 
-	if (strlen(dirname) <= strlen(httpd->index_html))
-		dirname = ".";
+	if (strlen(dirpath) <= strlen(httpd->index_html))
+		dirpath = ".";
 	else {
-		file = bozostrdup(httpd, dirname);
+		file = bozostrdup(httpd, request, dirpath);
 
 		file[strlen(file) - strlen(httpd->index_html)] = '\0';
-		dirname = file;
+		dirpath = file;
 	}
-	debug((httpd, DEBUG_FAT, "bozo_dir_index: dirname ``%s''", dirname));
-	if (stat(dirname, &sb) < 0 ||
-	    (dp = opendir(dirname)) == NULL) {
+	debug((httpd, DEBUG_FAT, "bozo_dir_index: dirpath ``%s''", dirpath));
+	if (stat(dirpath, &sb) < 0 ||
+	    (dp = opendir(dirpath)) == NULL) {
 		if (errno == EPERM)
 			(void)bozo_http_error(httpd, 403, request,
 			    "no permission to open directory");
@@ -108,11 +108,21 @@ bozo_dir_index(bozo_httpreq_t *request, 
 		goto done;
 	}
 
+#ifndef NO_USER_SUPPORT
+	if (request->hr_user) {
+		bozoasprintf(httpd, &printname, "~%s/%s",
+			     request->hr_user, request->hr_file);
+	} else
+		printname = bozostrdup(httpd, request, request->hr_file);
+#else
+	printname = bozostrdup(httpd, request, request->hr_file);
+#endif /* !NO_USER_SUPPORT */
+
 	bozo_printf(httpd,
 		"<html><head><title>Index of %s</title></head>\r\n",
-		request->hr_file);
+		printname);
 	bozo_printf(httpd, "<body><h1>Index of %s</h1>\r\n",
-		request->hr_file);
+		printname);
 	bozo_printf(httpd, "<pre>\r\n");
 #define NAMELEN 40
 #define LMODLEN 19
@@ -123,7 +133,7 @@ bozo_dir_index(bozo_httpreq_t *request, 
 	directory_hr(httpd);
 	bozo_printf(httpd, "<pre>");
 
-	for (j = k = scandir(dirname, &de, NULL, alphasort), deo = de;
+	for (j = k = scandir(dirpath, &de, NULL, alphasort), deo = de;
 	    j--; de++) {
 		int nostat = 0;
 		char *name = (*de)->d_name;
@@ -134,13 +144,13 @@ bozo_dir_index(bozo_httpreq_t *request, 
 		     httpd->hide_dots && name[0] == '.'))
 			continue;
 
-		snprintf(buf, sizeof buf, "%s/%s", dirname, name);
+		snprintf(buf, sizeof buf, "%s/%s", dirpath, name);
 		if (stat(buf, &sb))
 			nostat = 1;
 
 		l = 0;
 
-		urlname = bozo_escape_rfc3986(httpd, name);
+		urlname = bozo_escape_rfc3986(httpd, name, 0);
 		htmlname = bozo_escape_html(httpd, name);
 		if (htmlname == NULL)
 			htmlname = name;
@@ -189,8 +199,8 @@ bozo_dir_index(bozo_httpreq_t *request, 
 			spacebuf[i] = '\0';
 			bozo_printf(httpd, "%s", spacebuf);
 
-			bozo_printf(httpd, "%7ukB",
-			    ((unsigned)((unsigned)(sb.st_size) >> 10)));
+			bozo_printf(httpd, "%12llukB",
+				    (unsigned long long)sb.st_size >> 10);
 		}
 		bozo_printf(httpd, "\r\n");
 	}
@@ -206,6 +216,7 @@ bozo_dir_index(bozo_httpreq_t *request, 
 
 done:
 	free(file);
+	free(printname);
 	return 1;
 }
 #endif /* NO_DIRINDEX_SUPPORT */

Index: src/libexec/httpd/lua-bozo.c
diff -u src/libexec/httpd/lua-bozo.c:1.9.10.2 src/libexec/httpd/lua-bozo.c:1.9.10.3
--- src/libexec/httpd/lua-bozo.c:1.9.10.2	Wed Jul  9 09:47:10 2014
+++ src/libexec/httpd/lua-bozo.c	Fri Apr 15 19:38:13 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: lua-bozo.c,v 1.9.10.2 2014/07/09 09:47:10 msaitoh Exp $	*/
+/*	$NetBSD: lua-bozo.c,v 1.9.10.3 2016/04/15 19:38:13 snj Exp $	*/
 
 /*
  * Copyright (c) 2013 Marc Balmer <[email protected]>
@@ -123,7 +123,7 @@ lua_register_handler(lua_State *L)
 
 	handler = bozomalloc(httpd, sizeof(lua_handler_t));
 
-	handler->name = bozostrdup(httpd, lua_tostring(L, 1));
+	handler->name = bozostrdup(httpd, NULL, lua_tostring(L, 1));
 	handler->ref = luaL_ref(L, LUA_REGISTRYINDEX);
 	SIMPLEQ_INSERT_TAIL(&map->handlers, handler, h_next);
 	httpd->process_lua = 1;
@@ -184,28 +184,28 @@ bozo_add_lua_map(bozohttpd_t *httpd, con
 	lua_state_map_t *map;
 
 	map = bozomalloc(httpd, sizeof(lua_state_map_t));
-	map->prefix = bozostrdup(httpd, prefix);
+	map->prefix = bozostrdup(httpd, NULL, prefix);
 	if (*script == '/')
-		map->script = bozostrdup(httpd, script);
+		map->script = bozostrdup(httpd, NULL, script);
 	else {
 		char cwd[MAXPATHLEN], *path;
 
 		getcwd(cwd, sizeof(cwd) - 1);
-		asprintf(&path, "%s/%s", cwd, script);
+		bozoasprintf(httpd, &path, "%s/%s", cwd, script);
 		map->script = path;
 	}
 	map->L = luaL_newstate();
 	if (map->L == NULL)
-		bozo_err(httpd, 1, "can't create Lua state");
+		bozoerr(httpd, 1, "can't create Lua state");
 	SIMPLEQ_INIT(&map->handlers);
 
 #if LUA_VERSION_NUM >= 502
 	luaL_openlibs(map->L);
-	lua_getglobal(L, "package");
-	lua_getfield(L, -1, "preload");
-	lua_pushcfunction(L, luaopen_httpd);
-	lua_setfield(L, -2, "httpd");
-	lua_pop(L, 2);
+	lua_getglobal(map->L, "package");
+	lua_getfield(map->L, -1, "preload");
+	lua_pushcfunction(map->L, luaopen_httpd);
+	lua_setfield(map->L, -2, "httpd");
+	lua_pop(map->L, 2);
 #else
 	lua_openlib(map->L, "", luaopen_base);
 	lua_openlib(map->L, LUA_LOADLIBNAME, luaopen_package);
@@ -225,10 +225,10 @@ bozo_add_lua_map(bozohttpd_t *httpd, con
 	lua_settable(map->L, LUA_REGISTRYINDEX);
 
 	if (luaL_loadfile(map->L, script))
-		bozo_err(httpd, 1, "failed to load script %s: %s", script,
+		bozoerr(httpd, 1, "failed to load script %s: %s", script,
 		    lua_tostring(map->L, -1));
 	if (lua_pcall(map->L, 0, 0, 0))
-		bozo_err(httpd, 1, "failed to execute script %s: %s", script,
+		bozoerr(httpd, 1, "failed to execute script %s: %s", script,
 		    lua_tostring(map->L, -1));
 	SIMPLEQ_INSERT_TAIL(&httpd->lua_states, map, s_next);
 }
@@ -276,6 +276,7 @@ lua_url_decode(lua_State *L, char *s)
 			*q++ = *p;
 		}
 	}
+	*q = '\0';
 	lua_pushstring(L, val);
 	lua_setfield(L, -2, s);
 	free(val);
@@ -310,44 +311,41 @@ bozo_process_lua(bozo_httpreq_t *request
 	if (!httpd->process_lua)
 		return 0;
 
+	info = NULL;
+	query = NULL;
+	prefix = NULL;
 	uri = request->hr_oldfile ? request->hr_oldfile : request->hr_file;
 
 	if (*uri == '/') {
-		file = bozostrdup(httpd, uri);
-		prefix = bozostrdup(httpd, &uri[1]);
+		file = bozostrdup(httpd, request, uri);
+		if (file == NULL)
+			goto out;
+		prefix = bozostrdup(httpd, request, &uri[1]);
 	} else {
-		prefix = bozostrdup(httpd, uri);
-		asprintf(&file, "/%s", uri);
-	}
-	if (file == NULL) {
-		free(prefix);
-		return 0;
+		if (asprintf(&file, "/%s", uri) < 0)
+			goto out;
+		prefix = bozostrdup(httpd, request, uri);
 	}
+	if (prefix == NULL)
+		goto out;
 
-	if (request->hr_query && strlen(request->hr_query))
-		query = bozostrdup(httpd, request->hr_query);
-	else
-		query = NULL;
+	if (request->hr_query && request->hr_query[0])
+		query = bozostrdup(httpd, request, request->hr_query);
 
 	p = strchr(prefix, '/');
-	if (p == NULL){
-		free(prefix);
-		return 0;
-	}
+	if (p == NULL)
+		goto out;
 	*p++ = '\0';
 	handler = p;
-	if (!*handler) {
-		free(prefix);
-		return 0;
-	}
+	if (!*handler)
+		goto out;
 	p = strchr(handler, '/');
 	if (p != NULL)
 		*p++ = '\0';
 
-	info = NULL;
 	command = file + 1;
 	if ((s = strchr(command, '/')) != NULL) {
-		info = bozostrdup(httpd, s);
+		info = bozostrdup(httpd, request, s);
 		*s = '\0';
 	}
 

Index: src/libexec/httpd/main.c
diff -u src/libexec/httpd/main.c:1.5.8.1 src/libexec/httpd/main.c:1.5.8.2
--- src/libexec/httpd/main.c:1.5.8.1	Wed Jul  9 09:47:11 2014
+++ src/libexec/httpd/main.c	Fri Apr 15 19:38:13 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: main.c,v 1.5.8.1 2014/07/09 09:47:11 msaitoh Exp $	*/
+/*	$NetBSD: main.c,v 1.5.8.2 2016/04/15 19:38:13 snj Exp $	*/
 
 /*	$eterna: main.c,v 1.6 2011/11/18 09:21:15 mrg Exp $	*/
 /* from: eterna: bozohttpd.c,v 1.159 2009/05/23 02:14:30 mrg Exp 	*/
@@ -58,65 +58,67 @@
 BOZO_DEAD static void
 usage(bozohttpd_t *httpd, char *progname)
 {
-	bozo_warn(httpd, "usage: %s [options] slashdir [virtualhostname]",
+	bozowarn(httpd, "usage: %s [options] slashdir [virtualhostname]",
 			progname);
-	bozo_warn(httpd, "options:");
+	bozowarn(httpd, "options:");
 #ifndef NO_DEBUG
-	bozo_warn(httpd, "   -d\t\t\tenable debug support");
+	bozowarn(httpd, "   -d\t\t\tenable debug support");
+#endif
+	bozowarn(httpd, "   -s\t\t\talways log to stderr");
+#ifndef NO_DYNAMIC_CONTENT
+	bozowarn(httpd, "   -M arg t c c11\tadd this mime extenstion");
 #endif
-	bozo_warn(httpd, "   -s\t\t\talways log to stderr");
 #ifndef NO_USER_SUPPORT
-	bozo_warn(httpd, "   -u\t\t\tenable ~user/public_html support");
-	bozo_warn(httpd, "   -p dir\t\tchange `public_html' directory name]");
+	bozowarn(httpd, "   -u\t\t\tenable ~user/public_html support");
+	bozowarn(httpd, "   -p dir\t\tchange `public_html' directory name");
+#ifndef NO_CGIBIN_SUPPORT
+	bozowarn(httpd, "   -E\t\t\tenable CGI support for user dirs");
 #endif
-#ifndef NO_DYNAMIC_CONTENT
-	bozo_warn(httpd, "   -M arg t c c11\tadd this mime extenstion");
 #endif
 #ifndef NO_CGIBIN_SUPPORT
 #ifndef NO_DYNAMIC_CONTENT
-	bozo_warn(httpd, "   -C arg prog\t\tadd this CGI handler");
+	bozowarn(httpd, "   -C arg prog\t\tadd this CGI handler");
 #endif
-	bozo_warn(httpd,
+	bozowarn(httpd,
 		"   -c cgibin\t\tenable cgi-bin support in this directory");
 #endif
 #ifndef NO_LUA_SUPPORT
-	bozo_warn(httpd, "   -L arg script\tadd this Lua script");
+	bozowarn(httpd, "   -L arg script\tadd this Lua script");
 #endif
-	bozo_warn(httpd, "   -I port\t\tbind or use on this port");
+	bozowarn(httpd, "   -I port\t\tbind or use on this port");
 #ifndef NO_DAEMON_MODE
-	bozo_warn(httpd, "   -b\t\t\tbackground and go into daemon mode");
-	bozo_warn(httpd, "   -f\t\t\tkeep daemon mode in the foreground");
-	bozo_warn(httpd,
+	bozowarn(httpd, "   -b\t\t\tbackground and go into daemon mode");
+	bozowarn(httpd, "   -f\t\t\tkeep daemon mode in the foreground");
+	bozowarn(httpd,
 		"   -i address\t\tbind on this address (daemon mode only)");
-	bozo_warn(httpd, "   -P pidfile\t\tpath to the pid file to create");
+	bozowarn(httpd, "   -P pidfile\t\tpath to the pid file to create");
 #endif
-	bozo_warn(httpd, "   -S version\t\tset server version string");
-	bozo_warn(httpd, "   -t dir\t\tchroot to `dir'");
-	bozo_warn(httpd, "   -U username\t\tchange user to `user'");
-	bozo_warn(httpd,
+	bozowarn(httpd, "   -S version\t\tset server version string");
+	bozowarn(httpd, "   -t dir\t\tchroot to `dir'");
+	bozowarn(httpd, "   -U username\t\tchange user to `user'");
+	bozowarn(httpd,
 		"   -e\t\t\tdon't clean the environment (-t and -U only)");
-	bozo_warn(httpd,
+	bozowarn(httpd,
 		"   -v virtualroot\tenable virtual host support "
 		"in this directory");
-	bozo_warn(httpd,
-		"   -r\t\t\tmake sure sub-pages come from "
-		"this host via referrer");
 #ifndef NO_DIRINDEX_SUPPORT
-	bozo_warn(httpd,
+	bozowarn(httpd,
 		"   -X\t\t\tenable automatic directory index support");
-	bozo_warn(httpd,
+	bozowarn(httpd,
 		"   -H\t\t\thide files starting with a period (.)"
 		" in index mode");
 #endif
-	bozo_warn(httpd,
+	bozowarn(httpd,
 		"   -x index\t\tchange default `index.html' file name");
 #ifndef NO_SSL_SUPPORT
-	bozo_warn(httpd,
+	bozowarn(httpd,
+		"   -z ciphers\t\tspecify SSL ciphers");
+	bozowarn(httpd,
 		"   -Z cert privkey\tspecify path to server certificate"
 			" and private key file\n"
 		"\t\t\tin pem format and enable bozohttpd in SSL mode");
 #endif /* NO_SSL_SUPPORT */
-	bozo_err(httpd, 1, "%s failed to start", progname);
+	bozoerr(httpd, 1, "%s failed to start", progname);
 }
 
 int
@@ -126,6 +128,7 @@ main(int argc, char **argv)
 	bozohttpd_t	 httpd;
 	bozoprefs_t	 prefs;
 	char		*progname;
+	const char	*val;
 	int		 c;
 
 	(void) memset(&httpd, 0x0, sizeof(httpd));
@@ -140,13 +143,17 @@ main(int argc, char **argv)
 
 	bozo_set_defaults(&httpd, &prefs);
 
+	/*
+	 * -r option was removed, do not reuse it for a while
+	 */
+
 	while ((c = getopt(argc, argv,
-	    "C:HI:L:M:P:S:U:VXZ:bc:defhi:np:rst:uv:x:z:")) != -1) {
-		switch(c) {
+	    "C:EHI:L:M:P:S:U:VXZ:bc:defhi:np:st:uv:x:z:")) != -1) {
+		switch (c) {
 
 		case 'L':
 #ifdef NO_LUA_SUPPORT
-			bozo_err(&httpd, 1,
+			bozoerr(&httpd, 1,
 				"Lua support is not enabled");
 			/* NOTREACHED */
 #else
@@ -159,7 +166,7 @@ main(int argc, char **argv)
 #endif /* NO_LUA_SUPPORT */
 		case 'M':
 #ifdef NO_DYNAMIC_CONTENT
-			bozo_err(&httpd, 1,
+			bozoerr(&httpd, 1,
 				"dynamic mime content support is not enabled");
 			/* NOTREACHED */
 #else
@@ -173,23 +180,20 @@ main(int argc, char **argv)
 #endif /* NO_DYNAMIC_CONTENT */
 
 		case 'n':
-			bozo_set_pref(&prefs, "numeric", "true");
-			break;
-
-		case 'r':
-			bozo_set_pref(&prefs, "trusted referal", "true");
+			bozo_set_pref(&httpd, &prefs, "numeric", "true");
 			break;
 
 		case 's':
-			bozo_set_pref(&prefs, "log to stderr", "true");
+			bozo_set_pref(&httpd, &prefs, "log to stderr", "true");
 			break;
 
 		case 'S':
-			bozo_set_pref(&prefs, "server software", optarg);
+			bozo_set_pref(&httpd, &prefs, "server software",
+				      optarg);
 			break;
 		case 'Z':
 #ifdef NO_SSL_SUPPORT
-			bozo_err(&httpd, 1, "ssl support is not enabled");
+			bozoerr(&httpd, 1, "ssl support is not enabled");
 			/* NOT REACHED */
 #else
 			/* make sure there's two arguments */
@@ -198,24 +202,34 @@ main(int argc, char **argv)
 			bozo_ssl_set_opts(&httpd, optarg, argv[optind++]);
 			break;
 #endif /* NO_SSL_SUPPORT */
+
+		case 'z':
+#ifdef NO_SSL_SUPPORT
+			bozoerr(&httpd, 1, "ssl support is not enabled");
+			/* NOT REACHED */
+#else
+			bozo_ssl_set_ciphers(&httpd, optarg);
+			break;
+#endif /* NO_SSL_SUPPORT */
+
 		case 'U':
-			bozo_set_pref(&prefs, "username", optarg);
+			bozo_set_pref(&httpd, &prefs, "username", optarg);
 			break;
 
 		case 'V':
-			bozo_set_pref(&prefs, "unknown slash", "true");
+			bozo_set_pref(&httpd, &prefs, "unknown slash", "true");
 			break;
 
 		case 'v':
-			bozo_set_pref(&prefs, "virtual base", optarg);
+			bozo_set_pref(&httpd, &prefs, "virtual base", optarg);
 			break;
 
 		case 'x':
-			bozo_set_pref(&prefs, "index.html", optarg);
+			bozo_set_pref(&httpd, &prefs, "index.html", optarg);
 			break;
 
 		case 'I':
-			bozo_set_pref(&prefs, "port number", optarg);
+			bozo_set_pref(&httpd, &prefs, "port number", optarg);
 			break;
 
 #ifdef NO_DAEMON_MODE
@@ -224,7 +238,7 @@ main(int argc, char **argv)
 		case 'f':
 		case 'i':
 		case 'P':
-			bozo_err(&httpd, 1, "Daemon mode is not enabled");
+			bozoerr(&httpd, 1, "Daemon mode is not enabled");
 			/* NOTREACHED */
 #else
 		case 'b':
@@ -233,34 +247,33 @@ main(int argc, char **argv)
 			 * background == 2 (aka, -b -b) means to
 			 * only process 1 per kid
 			 */
-			if (bozo_get_pref(&prefs, "background") == NULL) {
-				bozo_set_pref(&prefs, "background", "1");
-			} else {
-				bozo_set_pref(&prefs, "background", "2");
-			}
+			val = bozo_get_pref(&prefs, "background") == NULL ?
+			    "1" : "2";
+			bozo_set_pref(&httpd, &prefs, "background", val);
 			break;
 
 		case 'e':
-			bozo_set_pref(&prefs, "dirty environment", "true");
+			bozo_set_pref(&httpd, &prefs, "dirty environment",
+				      "true");
 			break;
 
 		case 'f':
-			bozo_set_pref(&prefs, "foreground", "true");
+			bozo_set_pref(&httpd, &prefs, "foreground", "true");
 			break;
 
 		case 'i':
-			bozo_set_pref(&prefs, "bind address", optarg);
+			bozo_set_pref(&httpd, &prefs, "bind address", optarg);
 			break;
 
 		case 'P':
-			bozo_set_pref(&prefs, "pid file", optarg);
+			bozo_set_pref(&httpd, &prefs, "pid file", optarg);
 			break;
 #endif /* NO_DAEMON_MODE */
 
 #ifdef NO_CGIBIN_SUPPORT
 		case 'c':
 		case 'C':
-			bozo_err(&httpd, 1, "CGI is not enabled");
+			bozoerr(&httpd, 1, "CGI is not enabled");
 			/* NOTREACHED */
 #else
 		case 'c':
@@ -269,7 +282,7 @@ main(int argc, char **argv)
 
 		case 'C':
 #  ifdef NO_DYNAMIC_CONTENT
-			bozo_err(&httpd, 1,
+			bozoerr(&httpd, 1,
 				"dynamic CGI handler support is not enabled");
 			/* NOTREACHED */
 #  else
@@ -286,43 +299,54 @@ main(int argc, char **argv)
 			httpd.debug++;
 #ifdef NO_DEBUG
 			if (httpd.debug == 1)
-				bozo_warn(&httpd, "Debugging is not enabled");
+				bozowarn(&httpd, "Debugging is not enabled");
 #endif /* NO_DEBUG */
 			break;
 
+		case 't':
+			bozo_set_pref(&httpd, &prefs, "chroot dir", optarg);
+			break;
+
 #ifdef NO_USER_SUPPORT
 		case 'p':
-		case 't':
 		case 'u':
-			bozo_err(&httpd, 1, "User support is not enabled");
+		case 'E':
+			bozoerr(&httpd, 1, "User support is not enabled");
 			/* NOTREACHED */
 #else
 		case 'p':
-			bozo_set_pref(&prefs, "public_html", optarg);
-			break;
-
-		case 't':
-			bozo_set_pref(&prefs, "chroot dir", optarg);
+			bozo_set_pref(&httpd, &prefs, "public_html", optarg);
 			break;
 
 		case 'u':
-			bozo_set_pref(&prefs, "enable users", "true");
+			bozo_set_pref(&httpd, &prefs, "enable users", "true");
+			break;
+#ifndef NO_CGIBIN_SUPPORT
+		case 'E':
+			bozo_set_pref(&httpd, &prefs, "enable user cgibin",
+				      "true");
 			break;
+#else
+		case 'E':
+			bozoerr(&httpd, 1, "CGI is not enabled");
+			/* NOTREACHED */
+#endif /* NO_CGIBIN_SPPORT */
 #endif /* NO_USER_SUPPORT */
 
 #ifdef NO_DIRINDEX_SUPPORT
 		case 'H':
 		case 'X':
-			bozo_err(&httpd, 1,
+			bozoerr(&httpd, 1,
 				"directory indexing is not enabled");
 			/* NOTREACHED */
 #else
 		case 'H':
-			bozo_set_pref(&prefs, "hide dots", "true");
+			bozo_set_pref(&httpd, &prefs, "hide dots", "true");
 			break;
 
 		case 'X':
-			bozo_set_pref(&prefs, "directory indexing", "true");
+			bozo_set_pref(&httpd, &prefs, "directory indexing",
+				      "true");
 			break;
 
 #endif /* NO_DIRINDEX_SUPPORT */

Index: src/libexec/httpd/printenv.lua
diff -u src/libexec/httpd/printenv.lua:1.2.10.2 src/libexec/httpd/printenv.lua:1.2.10.3
--- src/libexec/httpd/printenv.lua:1.2.10.2	Wed Jul  9 09:47:11 2014
+++ src/libexec/httpd/printenv.lua	Fri Apr 15 19:38:13 2016
@@ -1,4 +1,4 @@
--- $NetBSD: printenv.lua,v 1.2.10.2 2014/07/09 09:47:11 msaitoh Exp $
+-- $NetBSD: printenv.lua,v 1.2.10.3 2016/04/15 19:38:13 snj Exp $
 
 -- this small Lua script demonstrates the use of Lua in (bozo)httpd
 -- it will simply output the "environment"
@@ -8,6 +8,10 @@
 -- the same value on each invocation.  You can not keep state between
 -- two calls.
 
+-- You can test this example by running the following command:
+-- /usr/libexec/httpd -b -f -I 8080 -L test printenv.lua .
+-- and then navigate to: http://127.0.0.1:8080/test/printenv
+
 local httpd = require 'httpd'
 
 function printenv(env, headers, query)
@@ -15,12 +19,14 @@ function printenv(env, headers, query)
 	-- we get the "environment" in the env table, the values are more
 	-- or less the same as the variable for a CGI program
 
-	if count == nil then
-		count = 1
-	end
-
-	-- output a header
-	print([[
+	-- output headers using httpd.write()
+	-- httpd.write() will not append newlines
+	httpd.write("HTTP/1.1 200 Ok\r\n")
+	httpd.write("Content-Type: text/html\r\n\r\n")
+
+	-- output html using httpd.print()
+	-- you can also use print() and io.write() but they will not work with SSL
+	httpd.print([[
 		<html>
 			<head>
 				<title>Bozotic Lua Environment</title>
@@ -29,54 +35,58 @@ function printenv(env, headers, query)
 				<h1>Bozotic Lua Environment</h1>
 	]])
 
-	print('module version: ' .. httpd._VERSION .. '<br>')
+	httpd.print('module version: ' .. httpd._VERSION .. '<br>')
 
-	print('<h2>Server Environment</h2>')
+	httpd.print('<h2>Server Environment</h2>')
 	-- print the list of "environment" variables
 	for k, v in pairs(env) do
-		print(k .. '=' .. v .. '<br/>')
+		httpd.print(k .. '=' .. v .. '<br/>')
 	end
 
-	print('<h2>Request Headers</h2>')
+	httpd.print('<h2>Request Headers</h2>')
 	for k, v in pairs(headers) do
-		print(k .. '=' .. v .. '<br/>')
+		httpd.print(k .. '=' .. v .. '<br/>')
 	end
 
 	if query ~= nil then
-		print('<h2>Query Variables</h2>')
+		httpd.print('<h2>Query Variables</h2>')
 		for k, v in pairs(query) do
-			print(k .. '=' .. v .. '<br/>')
+			httpd.print(k .. '=' .. v .. '<br/>')
 		end
 	end
 
-	print('<h2>Form Test</h2>')
+	httpd.print('<h2>Form Test</h2>')
 
-	print([[
-	<form method="POST" action="/rest/form?sender=me">
+	httpd.print([[
+	<form method="POST" action="form?sender=me">
 	<input type="text" name="a_value">
 	<input type="submit">
 	</form>
 	]])
 	-- output a footer
-	print([[
+	httpd.print([[
 		</body>
 	</html>
 	]])
 end
 
 function form(env, header, query)
+
+	httpd.write("HTTP/1.1 200 Ok\r\n")
+	httpd.write("Content-Type: text/html\r\n\r\n")
+
 	if query ~= nil then
-		print('<h2>Form Variables</h2>')
+		httpd.print('<h2>Form Variables</h2>')
 
 		if env.CONTENT_TYPE ~= nil then
-			print('Content-type: ' .. env.CONTENT_TYPE .. '<br>')
+			httpd.print('Content-type: ' .. env.CONTENT_TYPE .. '<br>')
 		end
 
 		for k, v in pairs(query) do
-			print(k .. '=' .. v .. '<br/>')
+			httpd.print(k .. '=' .. v .. '<br/>')
 		end
 	else
-		print('No values')
+		httpd.print('No values')
 	end
 end
 

Index: src/libexec/httpd/ssl-bozo.c
diff -u src/libexec/httpd/ssl-bozo.c:1.13.6.1 src/libexec/httpd/ssl-bozo.c:1.13.6.2
--- src/libexec/httpd/ssl-bozo.c:1.13.6.1	Wed Jul  9 09:47:11 2014
+++ src/libexec/httpd/ssl-bozo.c	Fri Apr 15 19:38:13 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ssl-bozo.c,v 1.13.6.1 2014/07/09 09:47:11 msaitoh Exp $	*/
+/*	$NetBSD: ssl-bozo.c,v 1.13.6.2 2016/04/15 19:38:13 snj Exp $	*/
 
 /*	$eterna: ssl-bozo.c,v 1.15 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -30,7 +30,7 @@
  *
  */
 
-/* this code implements SSL for bozohttpd */
+/* this code implements SSL and backend IO for bozohttpd */
 
 #include <stdarg.h>
 #include <stdio.h>
@@ -48,6 +48,25 @@
 #define USE_ARG(x)	/*LINTED*/(void)&(x)
 #endif
 
+#ifndef BOZO_SSL_CIPHERS
+#define BOZO_SSL_CIPHERS 					\
+	"AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:"		\
+	"AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:"		\
+	"AES:"							\
+	"-SHA:"							\
+	"!aNULL:!eNULL:"					\
+	"!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:"			\
+	"!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:"		\
+	"!KRB5-DES-CBC3-SHA"
+#endif
+
+#ifndef BOZO_SSL_OPTIONS
+#define BOZO_SSL_OPTIONS					\
+	(SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1)
+#endif
+
+  /* this structure encapsulates the ssl info */
+
 /* this structure encapsulates the ssl info */
 typedef struct sslinfo_t {
 	SSL_CTX			*ssl_context;
@@ -55,29 +74,17 @@ typedef struct sslinfo_t {
 	SSL			*bozossl;
 	char			*certificate_file;
 	char			*privatekey_file;
+	char			*ciphers;
 } sslinfo_t;
 
 /*
- * bozo_ssl_err
- *
- * bozo_ssl_err works just like bozo_err except in addition to printing
- * the error provided by the caller at the point of error it pops and
- * prints all errors from the SSL error queue.
+ * bozo_clear_ssl_queue:  print the contents of the SSL error queue
  */
-BOZO_PRINTFLIKE(3, 4) BOZO_DEAD static void
-bozo_ssl_err(bozohttpd_t *httpd, int code, const char *fmt, ...)
+static void
+bozo_clear_ssl_queue(bozohttpd_t *httpd)
 {
-        va_list ap;
-
-        va_start(ap, fmt);
-        if (httpd->logstderr || isatty(STDERR_FILENO)) {
-                vfprintf(stderr, fmt, ap);
-                fputs("\n", stderr);
-        } else
-                vsyslog(LOG_ERR, fmt, ap);
-        va_end(ap);
+	unsigned long sslcode = ERR_get_error();
 
-	unsigned int sslcode = ERR_get_error();
 	do {
 		static const char sslfmt[] = "SSL Error: %s:%s:%s";
 
@@ -93,20 +100,77 @@ bozo_ssl_err(bozohttpd_t *httpd, int cod
 			    ERR_reason_error_string(sslcode));
 		}
 	} while (0 != (sslcode = ERR_get_error()));
+}
+
+/*
+ * bozo_ssl_warn works just like bozowarn, plus the SSL error queue
+ */
+BOZO_PRINTFLIKE(2, 3) static void
+bozo_ssl_warn(bozohttpd_t *httpd, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	if (httpd->logstderr || isatty(STDERR_FILENO)) {
+		vfprintf(stderr, fmt, ap);
+		fputs("\n", stderr);
+	} else
+		vsyslog(LOG_ERR, fmt, ap);
+	va_end(ap);
+
+	bozo_clear_ssl_queue(httpd);
+}
+
+
+/*
+ * bozo_ssl_err works just like bozoerr, plus the SSL error queue
+ */
+BOZO_PRINTFLIKE(3, 4) BOZO_DEAD static void
+bozo_ssl_err(bozohttpd_t *httpd, int code, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	if (httpd->logstderr || isatty(STDERR_FILENO)) {
+		vfprintf(stderr, fmt, ap);
+		fputs("\n", stderr);
+	} else
+		vsyslog(LOG_ERR, fmt, ap);
+	va_end(ap);
+
+	bozo_clear_ssl_queue(httpd);
 	exit(code);
 }
 
+/*
+ * bozo_check_error_queue:  print warnings if the error isn't expected
+ */
+static void
+bozo_check_error_queue(bozohttpd_t *httpd, const char *tag, int ret)
+{
+	if (ret > 0)
+		return;
+
+	const sslinfo_t *sslinfo = httpd->sslinfo;
+	const int sslerr = SSL_get_error(sslinfo->bozossl, ret);
+
+	if (sslerr != SSL_ERROR_ZERO_RETURN &&
+	    sslerr != SSL_ERROR_SYSCALL &&
+	    sslerr != SSL_ERROR_NONE)
+		bozo_ssl_warn(httpd, "%s: SSL_ERROR %d", tag, sslerr);
+}
+
 static BOZO_PRINTFLIKE(2, 0) int
 bozo_ssl_printf(bozohttpd_t *httpd, const char * fmt, va_list ap)
 {
-	sslinfo_t	*sslinfo;
-	char		*buf;
-	int		 nbytes;
-
-	sslinfo = httpd->sslinfo;
-	/* XXX we need more elegant/proper handling of SSL_write return */
-	if ((nbytes = vasprintf(&buf, fmt, ap)) != -1) 
-		SSL_write(sslinfo->bozossl, buf, nbytes);
+	char	*buf;
+	int	 nbytes;
+
+	if ((nbytes = vasprintf(&buf, fmt, ap)) != -1)  {
+		const sslinfo_t *sslinfo = httpd->sslinfo;
+		int ret = SSL_write(sslinfo->bozossl, buf, nbytes);
+		bozo_check_error_queue(httpd, "write", ret);
+	}
 
 	free(buf);
 
@@ -116,53 +180,35 @@ bozo_ssl_printf(bozohttpd_t *httpd, cons
 static ssize_t
 bozo_ssl_read(bozohttpd_t *httpd, int fd, void *buf, size_t nbytes)
 {
-	sslinfo_t	*sslinfo;
-	ssize_t		 rbytes;
+	const sslinfo_t *sslinfo = httpd->sslinfo;
+	int	ret;
 
 	USE_ARG(fd);
-	sslinfo = httpd->sslinfo;
-	/* XXX we need elegant/proper handling of SSL_read return */
-	rbytes = (ssize_t)SSL_read(sslinfo->bozossl, buf, (int)nbytes);
-	if (rbytes < 1) {
-		if (SSL_get_error(sslinfo->bozossl, rbytes) ==
-				SSL_ERROR_WANT_READ)
-			bozo_warn(httpd, "SSL_ERROR_WANT_READ");
-		else
-			bozo_warn(httpd, "SSL_ERROR OTHER");
-	}
+	ret = SSL_read(sslinfo->bozossl, buf, (int)nbytes);
+	bozo_check_error_queue(httpd, "read", ret);
 
-	return rbytes;
+	return (ssize_t)ret;
 }
 
 static ssize_t
 bozo_ssl_write(bozohttpd_t *httpd, int fd, const void *buf, size_t nbytes)
 {
-	sslinfo_t	*sslinfo;
-	ssize_t		 wbytes;
+	const sslinfo_t *sslinfo = httpd->sslinfo;
+	int	ret;
 
 	USE_ARG(fd);
-	sslinfo = httpd->sslinfo;
-	/* XXX we need elegant/proper handling of SSL_write return */
-	wbytes = (ssize_t)SSL_write(sslinfo->bozossl, buf, (int)nbytes);
+	ret = SSL_write(sslinfo->bozossl, buf, (int)nbytes);
+	bozo_check_error_queue(httpd, "write", ret);
 
-	return wbytes;
-}
-
-static int
-bozo_ssl_flush(bozohttpd_t *httpd, FILE *fp)
-{
-	USE_ARG(httpd);
-	USE_ARG(fp);
-	/* nothing to see here, move right along */
-	return 0;
+	return (ssize_t)ret;
 }
 
 void
 bozo_ssl_init(bozohttpd_t *httpd)
 {
-	sslinfo_t	*sslinfo;
+	sslinfo_t *sslinfo = httpd->sslinfo;
+	long options;
 
-	sslinfo = httpd->sslinfo;
 	if (sslinfo == NULL || !sslinfo->certificate_file)
 		return;
 	SSL_library_init();
@@ -171,11 +217,22 @@ bozo_ssl_init(bozohttpd_t *httpd)
 	sslinfo->ssl_method = SSLv23_server_method();
 	sslinfo->ssl_context = SSL_CTX_new(sslinfo->ssl_method);
 
-	/* XXX we need to learn how to check the SSL stack for more info */
 	if (NULL == sslinfo->ssl_context)
 		bozo_ssl_err(httpd, EXIT_FAILURE,
 		    "SSL context creation failed");
 
+	options = SSL_CTX_set_options(sslinfo->ssl_context,
+	    BOZO_SSL_OPTIONS);
+	if ((options & BOZO_SSL_OPTIONS) != BOZO_SSL_OPTIONS)
+		bozo_ssl_err(httpd, EXIT_FAILURE,
+		    "Error setting ssl options requested %#lx, got %#lx",
+		    BOZO_SSL_OPTIONS, options);
+
+	if (!SSL_CTX_set_cipher_list(sslinfo->ssl_context,
+	    sslinfo->ciphers ? sslinfo->ciphers : BOZO_SSL_CIPHERS))
+		bozo_ssl_err(httpd, EXIT_FAILURE,
+		    "Error setting cipher list '%s'", sslinfo->ciphers);
+
 	if (1 != SSL_CTX_use_certificate_chain_file(sslinfo->ssl_context,
 	    sslinfo->certificate_file))
 		bozo_ssl_err(httpd, EXIT_FAILURE,
@@ -194,50 +251,73 @@ bozo_ssl_init(bozohttpd_t *httpd)
 		    "Check private key failed");
 }
 
-void
+/*
+ * returns non-zero for failure
+ */
+int
 bozo_ssl_accept(bozohttpd_t *httpd)
 {
-	sslinfo_t	*sslinfo;
+	sslinfo_t *sslinfo = httpd->sslinfo;
 
-	sslinfo = httpd->sslinfo;
-	if (sslinfo != NULL && sslinfo->ssl_context) {
-		sslinfo->bozossl = SSL_new(sslinfo->ssl_context);
-		SSL_set_rfd(sslinfo->bozossl, 0);
-		SSL_set_wfd(sslinfo->bozossl, 1);
-		SSL_accept(sslinfo->bozossl);
-	}
+	if (sslinfo == NULL || !sslinfo->ssl_context)
+		return 0;
+
+	sslinfo->bozossl = SSL_new(sslinfo->ssl_context);
+	if (sslinfo->bozossl == NULL)
+		bozoerr(httpd, 1, "SSL_new failed");
+
+	SSL_set_rfd(sslinfo->bozossl, 0);
+	SSL_set_wfd(sslinfo->bozossl, 1);
+
+	const int ret = SSL_accept(sslinfo->bozossl);
+	bozo_check_error_queue(httpd, "accept", ret);
+
+	return ret != 1;
 }
 
 void
 bozo_ssl_destroy(bozohttpd_t *httpd)
 {
-	sslinfo_t	*sslinfo;
+	const sslinfo_t *sslinfo = httpd->sslinfo;
 
-	sslinfo = httpd->sslinfo;
 	if (sslinfo && sslinfo->bozossl)
 		SSL_free(sslinfo->bozossl);
 }
 
+static sslinfo_t *
+bozo_get_sslinfo(bozohttpd_t *httpd)
+{
+	sslinfo_t *sslinfo;
+	if (httpd->sslinfo)
+		return httpd->sslinfo;
+	sslinfo = bozomalloc(httpd, sizeof(*sslinfo));
+	if (sslinfo == NULL)
+		bozoerr(httpd, 1, "sslinfo allocation failed");
+	memset(sslinfo, 0, sizeof(*sslinfo));
+	return httpd->sslinfo = sslinfo;
+}
+
 void
 bozo_ssl_set_opts(bozohttpd_t *httpd, const char *cert, const char *priv)
 {
-	sslinfo_t	*sslinfo;
+	sslinfo_t *sslinfo = bozo_get_sslinfo(httpd);
 
-	if ((sslinfo = httpd->sslinfo) == NULL) {
-		sslinfo = bozomalloc(httpd, sizeof(*sslinfo));
-		if (sslinfo == NULL) {
-			bozo_err(httpd, 1, "sslinfo allocation failed");
-		}
-		httpd->sslinfo = sslinfo;
-	}
-	sslinfo->certificate_file = strdup(cert);
-	sslinfo->privatekey_file = strdup(priv);
+	sslinfo->certificate_file = bozostrdup(httpd, NULL, cert);
+	sslinfo->privatekey_file = bozostrdup(httpd, NULL, priv);
 	debug((httpd, DEBUG_NORMAL, "using cert/priv files: %s & %s",
-		sslinfo->certificate_file,
-		sslinfo->privatekey_file));
-	if (!httpd->bindport) {
-		httpd->bindport = strdup("https");
-	}
+	    sslinfo->certificate_file,
+	    sslinfo->privatekey_file));
+	if (!httpd->bindport)
+		httpd->bindport = bozostrdup(httpd, NULL, "https");
+}
+
+void
+bozo_ssl_set_ciphers(bozohttpd_t *httpd, const char *ciphers)
+{
+	sslinfo_t *sslinfo = bozo_get_sslinfo(httpd);
+
+	sslinfo->ciphers = bozostrdup(httpd, NULL, ciphers);
+	debug((httpd, DEBUG_NORMAL, "using ciphers: %s", sslinfo->ciphers));
 }
 
 #endif /* NO_SSL_SUPPORT */
@@ -250,13 +330,11 @@ bozo_printf(bozohttpd_t *httpd, const ch
 
 	va_start(args, fmt);
 #ifndef NO_SSL_SUPPORT
-	if (httpd->sslinfo) {
+	if (httpd->sslinfo)
 		cc = bozo_ssl_printf(httpd, fmt, args);
-		va_end(args);
-		return cc;
-	}
+	else
 #endif
-	cc = vprintf(fmt, args);
+		cc = vprintf(fmt, args);
 	va_end(args);
 	return cc;
 }
@@ -265,9 +343,8 @@ ssize_t
 bozo_read(bozohttpd_t *httpd, int fd, void *buf, size_t len)
 {
 #ifndef NO_SSL_SUPPORT
-	if (httpd->sslinfo) {
+	if (httpd->sslinfo)
 		return bozo_ssl_read(httpd, fd, buf, len);
-	}
 #endif
 	return read(fd, buf, len);
 }
@@ -276,9 +353,8 @@ ssize_t
 bozo_write(bozohttpd_t *httpd, int fd, const void *buf, size_t len)
 {
 #ifndef NO_SSL_SUPPORT
-	if (httpd->sslinfo) {
+	if (httpd->sslinfo)
 		return bozo_ssl_write(httpd, fd, buf, len);
-	}
 #endif
 	return write(fd, buf, len);
 }
@@ -287,9 +363,8 @@ int
 bozo_flush(bozohttpd_t *httpd, FILE *fp)
 {
 #ifndef NO_SSL_SUPPORT
-	if (httpd->sslinfo) {
-		return bozo_ssl_flush(httpd, fp);
-	}
+	if (httpd->sslinfo)
+		return 0;
 #endif
 	return fflush(fp);
 }

Index: src/libexec/httpd/tilde-luzah-bozo.c
diff -u src/libexec/httpd/tilde-luzah-bozo.c:1.9.8.1 src/libexec/httpd/tilde-luzah-bozo.c:1.9.8.2
--- src/libexec/httpd/tilde-luzah-bozo.c:1.9.8.1	Wed Jul  9 09:47:11 2014
+++ src/libexec/httpd/tilde-luzah-bozo.c	Fri Apr 15 19:38:13 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: tilde-luzah-bozo.c,v 1.9.8.1 2014/07/09 09:47:11 msaitoh Exp $	*/
+/*	$NetBSD: tilde-luzah-bozo.c,v 1.9.8.2 2016/04/15 19:38:13 snj Exp $	*/
 
 /*	$eterna: tilde-luzah-bozo.c,v 1.16 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -36,6 +36,7 @@
 
 #include <sys/param.h>
 
+#include <assert.h>
 #include <errno.h>
 #include <pwd.h>
 #include <stdlib.h>
@@ -55,27 +56,40 @@
  * enabled.
  */
 int
-bozo_user_transform(bozo_httpreq_t *request, int *isindex)
+bozo_user_transform(bozo_httpreq_t *request)
 {
 	bozohttpd_t *httpd = request->hr_httpd;
-	char	c, *s, *file = NULL;
+	char	*s, *file = NULL, *user;
 	struct	passwd *pw;
 
-	*isindex = 0;
+	/* find username */
+	user = strchr(request->hr_file + 1, '~');
 
-	if ((s = strchr(request->hr_file + 2, '/')) != NULL) {
+	/* this shouldn't happen, but "better paranoid than sorry" */
+	assert(user != NULL);
+	
+	user++;
+
+	if ((s = strchr(user, '/')) != NULL) {
 		*s++ = '\0';
-		c = s[strlen(s)-1];
-		*isindex = (c == '/' || c == '\0');
 	}
 
 	debug((httpd, DEBUG_OBESE, "looking for user %s",
-		request->hr_file + 2));
-	pw = getpwnam(request->hr_file + 2);
+		user));
+	pw = getpwnam(user);
+	request->hr_user = bozostrdup(httpd, request, user);
+
 	/* fix this up immediately */
-	if (s)
+	if (s) {
 		s[-1] = '/';
+		/* omit additional slashes at the beginning */
+		while (*s == '/')
+			s++;
+	}
+
 	if (pw == NULL) {
+		free(request->hr_user);
+		request->hr_user = NULL;
 		(void)bozo_http_error(httpd, 404, request, "no such user");
 		return 0;
 	}
@@ -85,40 +99,25 @@ bozo_user_transform(bozo_httpreq_t *requ
 	      pw->pw_uid, pw->pw_gid));
 
 	if (chdir(pw->pw_dir) < 0) {
-		bozo_warn(httpd, "chdir1 error: %s: %s", pw->pw_dir,
+		bozowarn(httpd, "chdir1 error: %s: %s", pw->pw_dir,
 			strerror(errno));
 		(void)bozo_http_error(httpd, 404, request,
 			"can't chdir to homedir");
 		return 0;
 	}
 	if (chdir(httpd->public_html) < 0) {
-		bozo_warn(httpd, "chdir2 error: %s: %s", httpd->public_html,
+		bozowarn(httpd, "chdir2 error: %s: %s", httpd->public_html,
 			strerror(errno));
 		(void)bozo_http_error(httpd, 404, request,
 			"can't chdir to public_html");
 		return 0;
 	}
 	if (s == NULL || *s == '\0') {
-		file = bozostrdup(httpd, httpd->index_html);
+		file = bozostrdup(httpd, request, "/");
 	} else {
-		file = bozomalloc(httpd, strlen(s) +
-		    (*isindex ? strlen(httpd->index_html) + 1 : 1));
-		strcpy(file, s);
-		if (*isindex)
-			strcat(file, httpd->index_html);
-	}
-
-	/* see transform_request() */
-	if (*file == '/' || strcmp(file, "..") == 0 ||
-	    strstr(file, "/..") || strstr(file, "../")) {
-		(void)bozo_http_error(httpd, 403, request, "illegal request");
-		free(file);
-		return 0;
-	}
-
-	if (bozo_auth_check(request, file)) {
-		free(file);
-		return 0;
+		file = bozomalloc(httpd, strlen(s) + 2);
+		strcpy(file, "/");
+		strcat(file, s);
 	}
 
 	free(request->hr_file);

Index: src/libexec/httpd/lua/bozo.lua
diff -u src/libexec/httpd/lua/bozo.lua:1.1.1.1 src/libexec/httpd/lua/bozo.lua:1.1.1.1.14.1
--- src/libexec/httpd/lua/bozo.lua:1.1.1.1	Mon May 10 03:30:04 2010
+++ src/libexec/httpd/lua/bozo.lua	Fri Apr 15 19:38:13 2016
@@ -35,6 +35,7 @@ dofile "optparse.lua"
 opt = OptionParser{usage="%prog [options] root [vhost]", version="20091105"}                           
 
 opt.add_option{"-C", "--cgimap", action="store", dest="cgimap", help="--cgimap 's t'"}
+opt.add_option{"-E", "--enable-user-cgibin", action="store_true", dest="enableusercgibin", help="--enable-user-cgibin"}
 opt.add_option{"-H", "--hide-dots", action="store_true", dest="hidedots", help="--hide-dots"}
 opt.add_option{"-I", "--portnum", action="store", dest="portnum", help="--portnum number"}
 opt.add_option{"-M", "--dynamicmime", action="store", dest="dynmime", help="--dynamicmime 'suffix type a b'"}
@@ -50,7 +51,6 @@ opt.add_option{"-f", "--foreground", act
 opt.add_option{"-i", "--bindaddr", action="store", dest="bindaddress", help="--bindaddr address"}
 opt.add_option{"-n", "--numeric", action="store_true", dest="numeric", help="--numeric"}
 opt.add_option{"-p", "--public-html", action="store", dest="public_html", help="--public-html dir"}
-opt.add_option{"-r", "--trusted-referal", action="store_true", dest="trustedref", help="trusted referal"}
 opt.add_option{"-s", "--logtostderr", action="store_true", dest="logstderr", help="log to stderr"}
 opt.add_option{"-t", "--chroot", action="store", dest="chroot", help="--chroot dir"}
 opt.add_option{"-u", "--enable-users", action="store_true", dest="enableusers", help="--enable-users"}
@@ -141,6 +141,9 @@ end
 if options.hidedots then
         bozohttpd.set_pref(prefs, "hide dots", "true")
 end
+if options.enableusercgibin then
+        bozohttpd.set_pref(prefs, "enable user cgibin", "true")
+end
 if options.dirindex then
         bozohttpd.set_pref(prefs, "directory indexing", "true")
 end
Index: src/libexec/httpd/lua/glue.c
diff -u src/libexec/httpd/lua/glue.c:1.1.1.1 src/libexec/httpd/lua/glue.c:1.1.1.1.14.1
--- src/libexec/httpd/lua/glue.c:1.1.1.1	Mon May 10 03:30:04 2010
+++ src/libexec/httpd/lua/glue.c	Fri Apr 15 19:38:13 2016
@@ -92,11 +92,13 @@ l_init_httpd(lua_State *L)
 static int
 l_init_prefs(lua_State *L)
 {
+	bozohttpd_t	*httpd;
 	bozoprefs_t	*prefs;
 
 	prefs = lua_newuserdata(L, sizeof(*prefs));
 	(void) memset(prefs, 0x0, sizeof(*prefs));
-	(void) bozo_init_prefs(prefs);
+	httpd = lua_touserdata(L, 1);
+	(void) bozo_init_prefs(httpd, prefs);
 	return 1;
 }
 

Index: src/libexec/httpd/testsuite/Makefile
diff -u src/libexec/httpd/testsuite/Makefile:1.4 src/libexec/httpd/testsuite/Makefile:1.4.14.1
--- src/libexec/httpd/testsuite/Makefile:1.4	Sat May 23 02:26:03 2009
+++ src/libexec/httpd/testsuite/Makefile	Fri Apr 15 19:38:13 2016
@@ -6,7 +6,7 @@ BIGFILETESTS=	partial4000 partial8000
 BOZOHTTPD?=	../bozohttpd
 BOZOHTTPD?=	../debug/bozohttpd-debug
 WGET?=		wget
-
+DATA?=		$(.CURDIR)/data 
 all:
 
 clean:
@@ -19,14 +19,14 @@ check: check-simple check-bigfile
 check-simple:
 .for a in $(SIMPLETESTS)
 	echo "Running test $a"
-	$(BOZOHTTPD) ./data < $(.CURDIR)/$a.in > tmp.$a.out || true
+	$(BOZOHTTPD) "$(DATA)" < $(.CURDIR)/$a.in > tmp.$a.out || true
 	$(.CURDIR)/html_cmp $(.CURDIR)/$a.out tmp.$a.out
 .endfor
 
 check-bigfile:
 .for a in $(BIGFILETESTS)
 	echo "Running test $a"
-	$(.CURDIR)/test-bigfile "$a" "${BOZOHTTPD}" "${WGET}" "./data"
+	$(.CURDIR)/test-bigfile "$a" "${BOZOHTTPD}" "${WGET}" "$(DATA)"
 .endfor
 
 .include <bsd.obj.mk>

Index: src/libexec/httpd/testsuite/t10.out
diff -u src/libexec/httpd/testsuite/t10.out:1.1.1.1 src/libexec/httpd/testsuite/t10.out:1.1.1.1.16.1
--- src/libexec/httpd/testsuite/t10.out:1.1.1.1	Sat Apr 18 07:09:27 2009
+++ src/libexec/httpd/testsuite/t10.out	Fri Apr 15 19:38:13 2016
@@ -1,8 +1,8 @@
-HTTP/1.0 500 Internal Error
+HTTP/1.0 404 Not Found
 Content-Type: text/html
-Content-Length: 10296
-Server: bozohttpd/20090417
+Content-Length: 1024
+Server: bozohttpd/20140708
 
-<html><head><title>500 Internal Error</title></head>
-<body><h1>500 Internal Error</h1>
-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\ No newline at end of file
+<html><head><title>404 Not Found</title></head>
+<body><h1>404 Not Found</h1>
+/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
\ No newline at end of file

Index: src/libexec/httpd/testsuite/test-bigfile
diff -u src/libexec/httpd/testsuite/test-bigfile:1.1.1.1 src/libexec/httpd/testsuite/test-bigfile:1.1.1.1.14.1
--- src/libexec/httpd/testsuite/test-bigfile:1.1.1.1	Sat May 23 02:21:19 2009
+++ src/libexec/httpd/testsuite/test-bigfile	Fri Apr 15 19:38:13 2016
@@ -8,7 +8,7 @@ datadir="$4"
 bozotestport=11111
 
 # copy beginning file
-cp ./data/bigfile.${test} ./bigfile
+cp ${datadir}/bigfile.${test} ./bigfile
 
 # fire up bozohttpd
 ${bozohttpd} -b -b -I ${bozotestport} -n -s -f ${datadir} &
@@ -18,7 +18,7 @@ ${wget} -c http://localhost:${bozotestpo
 
 kill -9 $bozopid
 
-if cmp ./bigfile ./data/bigfile; then
+if cmp ./bigfile ${datadir}/bigfile; then
 	rm -f ./bigfile
 	exit 0
 else

Reply via email to