Module Name:    src
Committed By:   martin
Date:           Sun Apr 10 10:33:11 UTC 2016

Modified Files:
        src/libexec/httpd [netbsd-7]: 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/testsuite [netbsd-7]: Makefile test-bigfile

Log Message:
Catch up to -current (via patch), requested by mspo in #1141:

        libexec/httpd/CHANGES                            up to 1.21
        libexec/httpd/Makefile                           up to 1.26
        libexec/httpd/auth-bozo.c                        up to 1.18
        libexec/httpd/bozohttpd.8                        up to 1.58
        libexec/httpd/bozohttpd.c                        up to 1.79
        libexec/httpd/bozohttpd.h                        up to 1.44
        libexec/httpd/cgi-bozo.c                         up to 1.32
        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/main.c                             up to 1.13
        libexec/httpd/netbsd_queue.h                     up to 1.1
        libexec/httpd/printenv.lua                       up to 1.3
        libexec/httpd/ssl-bozo.c                         up to 1.22
        libexec/httpd/tilde-luzah-bozo.c                 up to 1.14
        libexec/httpd/testsuite/Makefile                 up to 1.5
        libexec/httpd/testsuite/test-bigfile             up to 1.2

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


To generate a diff of this commit:
cvs rdiff -u -r1.19.2.1 -r1.19.2.2 src/libexec/httpd/CHANGES
cvs rdiff -u -r1.22.2.1 -r1.22.2.2 src/libexec/httpd/Makefile
cvs rdiff -u -r1.13.2.1 -r1.13.2.2 src/libexec/httpd/auth-bozo.c
cvs rdiff -u -r1.46.4.4 -r1.46.4.5 src/libexec/httpd/bozohttpd.8
cvs rdiff -u -r1.56.2.4 -r1.56.2.5 src/libexec/httpd/bozohttpd.c
cvs rdiff -u -r1.33.2.2 -r1.33.2.3 src/libexec/httpd/bozohttpd.h
cvs rdiff -u -r1.25.2.2 -r1.25.2.3 src/libexec/httpd/cgi-bozo.c
cvs rdiff -u -r1.10.2.2 -r1.10.2.3 src/libexec/httpd/content-bozo.c
cvs rdiff -u -r1.16 -r1.16.4.1 src/libexec/httpd/daemon-bozo.c
cvs rdiff -u -r1.19.4.1 -r1.19.4.2 src/libexec/httpd/dir-index-bozo.c
cvs rdiff -u -r1.10.2.1 -r1.10.2.2 src/libexec/httpd/lua-bozo.c
cvs rdiff -u -r1.8 -r1.8.2.1 src/libexec/httpd/main.c
cvs rdiff -u -r1.2 -r1.2.18.1 src/libexec/httpd/printenv.lua
cvs rdiff -u -r1.18 -r1.18.2.1 src/libexec/httpd/ssl-bozo.c
cvs rdiff -u -r1.10 -r1.10.4.1 src/libexec/httpd/tilde-luzah-bozo.c
cvs rdiff -u -r1.4 -r1.4.24.1 src/libexec/httpd/testsuite/Makefile
cvs rdiff -u -r1.1.1.1 -r1.1.1.1.30.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.19.2.1 src/libexec/httpd/CHANGES:1.19.2.2
--- src/libexec/httpd/CHANGES:1.19.2.1	Sun Apr 19 04:44:03 2015
+++ src/libexec/httpd/CHANGES	Sun Apr 10 10:33:11 2016
@@ -1,5 +1,15 @@
 $eterna: CHANGES,v 1.78 2011/11/18 01:25:11 mrg Exp $
 
+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

Index: src/libexec/httpd/Makefile
diff -u src/libexec/httpd/Makefile:1.22.2.1 src/libexec/httpd/Makefile:1.22.2.2
--- src/libexec/httpd/Makefile:1.22.2.1	Thu Apr 23 19:38:11 2015
+++ src/libexec/httpd/Makefile	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.22.2.1 2015/04/23 19:38:11 snj Exp $
+#	$NetBSD: Makefile,v 1.22.2.2 2016/04/10 10:33:11 martin 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"
@@ -33,6 +37,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.13.2.1 src/libexec/httpd/auth-bozo.c:1.13.2.2
--- src/libexec/httpd/auth-bozo.c:1.13.2.1	Mon Jan 12 10:02:29 2015
+++ src/libexec/httpd/auth-bozo.c	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: auth-bozo.c,v 1.13.2.1 2015/01/12 10:02:29 martin Exp $	*/
+/*	$NetBSD: auth-bozo.c,v 1.13.2.2 2016/04/10 10:33:11 martin 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");
 	}
@@ -136,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;
 
@@ -159,8 +160,8 @@ bozo_auth_check_headers(bozo_httpreq_t *
 		*pass++ = '\0';
 		free(request->hr_authuser);
 		free(request->hr_authpass);
-		request->hr_authuser = bozostrdup(httpd, authbuf);
-		request->hr_authpass = bozostrdup(httpd, pass);
+		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));
@@ -190,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

Index: src/libexec/httpd/bozohttpd.8
diff -u src/libexec/httpd/bozohttpd.8:1.46.4.4 src/libexec/httpd/bozohttpd.8:1.46.4.5
--- src/libexec/httpd/bozohttpd.8:1.46.4.4	Sat May  9 08:50:42 2015
+++ src/libexec/httpd/bozohttpd.8	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-.\"	$NetBSD: bozohttpd.8,v 1.46.4.4 2015/05/09 08:50:42 snj Exp $
+.\"	$NetBSD: bozohttpd.8,v 1.46.4.5 2016/04/10 10:33:11 martin Exp $
 .\"
 .\"	$eterna: bozohttpd.8,v 1.101 2011/11/18 01:25:11 mrg Exp $
 .\"
@@ -26,7 +26,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 1, 2015
+.Dd December 12, 2015
 .Dt BOZOHTTPD 8
 .Os
 .Sh NAME
@@ -34,13 +34,14 @@
 .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
@@ -597,8 +613,9 @@ provided many fixes and enhancements for
 .It
 .An Mateusz Kocielski
 .Aq Mt s...@netbsd.org
-fixed memory leaks, information disclosure issues and added support
-for using CGI handlers with directory indexing.
+fixed memory leaks, various issues with userdir support,
+information disclosure issues, added support for using CGI handlers
+with directory indexing and provided various other fixes.
 .It
 .An Arnaud Lacombe
 .Aq Mt a...@netbsd.org
@@ -612,7 +629,7 @@ provided man page fixes
 .Aq Mt j...@netbsd.org
 Added the
 .Fl P
-option.
+option (pidfile support) and provided some man page fixes.
 .It
 .An Luke Mewburn
 .Aq Mt lu...@netbsd.org
@@ -665,9 +682,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.56.2.4 src/libexec/httpd/bozohttpd.c:1.56.2.5
--- src/libexec/httpd/bozohttpd.c:1.56.2.4	Sat May  9 08:50:42 2015
+++ src/libexec/httpd/bozohttpd.c	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: bozohttpd.c,v 1.56.2.4 2015/05/09 08:50:42 snj Exp $	*/
+/*	$NetBSD: bozohttpd.c,v 1.56.2.5 2016/04/10 10:33:11 martin Exp $	*/
 
 /*	$eterna: bozohttpd.c,v 1.178 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -109,7 +109,7 @@
 #define INDEX_HTML		"index.html"
 #endif
 #ifndef SERVER_SOFTWARE
-#define SERVER_SOFTWARE		"bozohttpd/20150501"
+#define SERVER_SOFTWARE		"bozohttpd/20151231"
 #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)) {
@@ -371,6 +369,7 @@ static bozoheaders_t *
 addmerge_header(bozo_httpreq_t *request, char *val,
 		char *str, ssize_t len)
 {
+	struct	bozohttpd_t *httpd = request->hr_httpd;
 	struct	bozoheaders *hdr;
 
 	USE_ARG(len);
@@ -384,22 +383,18 @@ addmerge_header(bozo_httpreq_t *request,
 		/* 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);
 		request->hr_nheaders++;
@@ -415,13 +410,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 +441,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 +481,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 +494,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");
 }
 
 /*
@@ -560,9 +557,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);
 
 	/*
@@ -578,15 +575,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
@@ -595,7 +593,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) {
@@ -614,11 +612,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);
@@ -682,7 +680,8 @@ bozo_read_request(bozohttpd_t *httpd)
 			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,
@@ -797,7 +796,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;
@@ -808,7 +807,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));
@@ -817,13 +816,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;
 	}
 
@@ -851,10 +850,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;
@@ -868,11 +868,6 @@ 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;
@@ -895,11 +890,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++;
@@ -912,115 +914,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 (absolute) {
+		char *sep = NULL;
+		const char *s;
 
-	if (request->hr_serverport && strcmp(request->hr_serverport, "80") != 0)
-		snprintf(portbuf, sizeof(portbuf), ":%s",
-		    request->hr_serverport);
-	else
+		/*
+		 * 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 */
 }
 
 /*
@@ -1037,9 +1042,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
 	 */
@@ -1050,12 +1052,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)
@@ -1069,6 +1071,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
@@ -1093,15 +1118,14 @@ check_virtual(bozo_httpreq_t *request)
 				}
 				debug((httpd, DEBUG_OBESE, "looking at dir``%s''",
 			 	   d->d_name));
-				if (d->d_namlen == len && strcmp(d->d_name,
-				    request->hr_host) == 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;
 				}
 			}
@@ -1138,6 +1162,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];
@@ -1150,27 +1175,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) {
@@ -1178,9 +1207,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;
 		}
@@ -1188,16 +1217,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);
@@ -1205,19 +1232,17 @@ 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;
 }
@@ -1297,7 +1322,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
@@ -1315,7 +1339,6 @@ 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;
@@ -1333,84 +1356,49 @@ transform_request(bozo_httpreq_t *reques
 		goto bad_done;
 	}
 
-	switch(check_bzredirect(request)) {
-	case -1:
-		goto bad_done;
-	case 1:
-		return 0;
-	}
+	/* omit additional slashes at the beginning */
+	while (file[1] == '/')
+		file++;
 
-	if (httpd->untrustedref) {
-		int to_indexhtml = 0;
-
-#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;
@@ -1420,10 +1408,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,
@@ -1437,15 +1425,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");
@@ -1549,7 +1536,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";
@@ -1563,7 +1550,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:
 		case EACCES:
 			(void)bozo_http_error(httpd, 403, request,
@@ -1642,9 +1629,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
@@ -1726,7 +1710,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);
 }
@@ -1756,7 +1740,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;
 
@@ -1771,7 +1755,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;
 
@@ -1786,6 +1770,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
@@ -1906,7 +1904,7 @@ bozo_http_error(bozohttpd_t *httpd, int 
 
 	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;
@@ -1920,26 +1918,47 @@ 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;
 
@@ -2078,12 +2097,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 *
@@ -2092,26 +2110,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 */
@@ -2144,15 +2163,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;
 }
@@ -2161,7 +2181,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 */
@@ -2183,16 +2203,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 */
@@ -2200,16 +2219,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);
@@ -2219,19 +2234,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;
@@ -2245,11 +2264,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.
@@ -2257,39 +2277,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.33.2.2 src/libexec/httpd/bozohttpd.h:1.33.2.3
--- src/libexec/httpd/bozohttpd.h:1.33.2.2	Sat May  9 08:50:42 2015
+++ src/libexec/httpd/bozohttpd.h	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: bozohttpd.h,v 1.33.2.2 2015/05/09 08:50:42 snj Exp $	*/
+/*	$NetBSD: bozohttpd.h,v 1.33.2.3 2016/04/10 10:33:11 martin Exp $	*/
 
 /*	$eterna: bozohttpd.h,v 1.39 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -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 */
@@ -85,11 +90,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 */
@@ -104,6 +107,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 */
@@ -146,6 +150,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;
@@ -176,8 +183,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;
@@ -210,34 +217,42 @@ 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);
 
-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_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)		do { /* nothing */ } while (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 *);
 int	bozo_ssl_accept(bozohttpd_t *);
 void	bozo_ssl_destroy(bozohttpd_t *);
@@ -255,13 +270,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)			do { /* nothing */ } while (0)
+#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 */
 
@@ -288,9 +303,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 *);
@@ -300,9 +315,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 */
 
 
@@ -320,7 +337,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 */
@@ -331,7 +349,7 @@ 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 *);
@@ -339,7 +357,7 @@ void bozo_process_request(bozo_httpreq_t
 void bozo_clean_request(bozo_httpreq_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.25.2.2 src/libexec/httpd/cgi-bozo.c:1.25.2.3
--- src/libexec/httpd/cgi-bozo.c:1.25.2.2	Sat May  9 08:50:42 2015
+++ src/libexec/httpd/cgi-bozo.c	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: cgi-bozo.c,v 1.25.2.2 2015/05/09 08:50:42 snj Exp $	*/
+/*	$NetBSD: cgi-bozo.c,v 1.25.2.3 2016/04/10 10:33:11 martin Exp $	*/
 
 /*	$eterna: cgi-bozo.c,v 1.40 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -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));
 		}		
@@ -214,7 +215,7 @@ append_index_html(bozohttpd_t *httpd, ch
 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));
 }
@@ -247,8 +248,7 @@ bozo_process_cgi(bozo_httpreq_t *request
 	char	date[40];
 	bozoheaders_t *headp;
 	const char *type, *clen, *info, *cgihandler;
-	char	*query, *s, *t, *path, *env, *file, *url;
-	char	command[MAXPATHLEN];
+	char	*query, *s, *t, *path, *env, *command, *file, *url;
 	char	**envp, **curenvp, *argv[4];
 	char	*uri;
 	size_t	len;
@@ -260,31 +260,36 @@ 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;
 	envp = NULL;
 	cgihandler = NULL;
+	command = NULL;
 	info = NULL;
 
 	len = strlen(url);
@@ -309,15 +314,14 @@ bozo_process_cgi(bozo_httpreq_t *request
 
 	ix = 0;
 	if (cgihandler) {
-		snprintf(command, sizeof(command), "%s", file + 1);
-		path = bozostrdup(httpd, cgihandler);
+		command = file + 1;
+		path = bozostrdup(httpd, request, cgihandler);
 		argv[ix++] = path;
 			/* argv[] = [ path, command, query, NULL ] */
 	} else {
-		snprintf(command, sizeof(command), "%s",
-		    file + CGIBIN_PREFIX_LEN + 1);
+		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,
@@ -414,21 +418,18 @@ 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, %s %s %s",
 	    path, argv[0], strornull(argv[1]), strornull(argv[2])));
 
 	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));
 
 	/*
@@ -439,7 +440,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]);
@@ -451,19 +452,23 @@ 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);
+
 	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);
@@ -487,7 +492,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));
 		}		
 	}
@@ -504,7 +509,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;
 

Index: src/libexec/httpd/content-bozo.c
diff -u src/libexec/httpd/content-bozo.c:1.10.2.2 src/libexec/httpd/content-bozo.c:1.10.2.3
--- src/libexec/httpd/content-bozo.c:1.10.2.2	Sat May  9 08:50:42 2015
+++ src/libexec/httpd/content-bozo.c	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: content-bozo.c,v 1.10.2.2 2015/05/09 08:50:42 snj Exp $	*/
+/*	$NetBSD: content-bozo.c,v 1.10.2.3 2016/04/10 10:33:11 martin Exp $	*/
 
 /*	$eterna: content-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -258,7 +258,7 @@ 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;

Index: src/libexec/httpd/daemon-bozo.c
diff -u src/libexec/httpd/daemon-bozo.c:1.16 src/libexec/httpd/daemon-bozo.c:1.16.4.1
--- src/libexec/httpd/daemon-bozo.c:1.16	Thu Jan  2 08:21:38 2014
+++ src/libexec/httpd/daemon-bozo.c	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: daemon-bozo.c,v 1.16 2014/01/02 08:21:38 mrg Exp $	*/
+/*	$NetBSD: daemon-bozo.c,v 1.16.4.1 2016/04/10 10:33:11 martin 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.19.4.1 src/libexec/httpd/dir-index-bozo.c:1.19.4.2
--- src/libexec/httpd/dir-index-bozo.c:1.19.4.1	Mon Jan 12 10:02:29 2015
+++ src/libexec/httpd/dir-index-bozo.c	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: dir-index-bozo.c,v 1.19.4.1 2015/01/12 10:02:29 martin Exp $	*/
+/*	$NetBSD: dir-index-bozo.c,v 1.19.4.2 2016/04/10 10:33:11 martin 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;
@@ -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.10.2.1 src/libexec/httpd/lua-bozo.c:1.10.2.2
--- src/libexec/httpd/lua-bozo.c:1.10.2.1	Mon Jan 12 10:02:29 2015
+++ src/libexec/httpd/lua-bozo.c	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: lua-bozo.c,v 1.10.2.1 2015/01/12 10:02:29 martin Exp $	*/
+/*	$NetBSD: lua-bozo.c,v 1.10.2.2 2016/04/10 10:33:11 martin Exp $	*/
 
 /*
  * Copyright (c) 2013 Marc Balmer <m...@msys.ch>
@@ -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,19 +184,19 @@ 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
@@ -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);
 }
@@ -311,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.8 src/libexec/httpd/main.c:1.8.2.1
--- src/libexec/httpd/main.c:1.8	Wed Jul 16 07:41:43 2014
+++ src/libexec/httpd/main.c	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: main.c,v 1.8 2014/07/16 07:41:43 mrg Exp $	*/
+/*	$NetBSD: main.c,v 1.8.2.1 2016/04/10 10:33:11 martin 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,42 +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(&prefs, "chroot dir", optarg);
+			bozo_set_pref(&httpd, &prefs, "chroot dir", optarg);
 			break;
 
 #ifdef NO_USER_SUPPORT
 		case 'p':
 		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);
+			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 src/libexec/httpd/printenv.lua:1.2.18.1
--- src/libexec/httpd/printenv.lua:1.2	Thu Jan  2 08:21:38 2014
+++ src/libexec/httpd/printenv.lua	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
--- $NetBSD: printenv.lua,v 1.2 2014/01/02 08:21:38 mrg Exp $
+-- $NetBSD: printenv.lua,v 1.2.18.1 2016/04/10 10:33:11 martin 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.18 src/libexec/httpd/ssl-bozo.c:1.18.2.1
--- src/libexec/httpd/ssl-bozo.c:1.18	Thu Jul 17 06:27:52 2014
+++ src/libexec/httpd/ssl-bozo.c	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ssl-bozo.c,v 1.18 2014/07/17 06:27:52 mrg Exp $	*/
+/*	$NetBSD: ssl-bozo.c,v 1.18.2.1 2016/04/10 10:33:11 martin Exp $	*/
 
 /*	$eterna: ssl-bozo.c,v 1.15 2011/11/18 09:21:15 mrg Exp $	*/
 
@@ -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,6 +74,7 @@ typedef struct sslinfo_t {
 	SSL			*bozossl;
 	char			*certificate_file;
 	char			*privatekey_file;
+	char			*ciphers;
 } sslinfo_t;
 
 /*
@@ -83,7 +103,7 @@ bozo_clear_ssl_queue(bozohttpd_t *httpd)
 }
 
 /*
- * bozo_ssl_warn works just like bozo_warn, plus the SSL error queue
+ * 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, ...)
@@ -103,7 +123,7 @@ bozo_ssl_warn(bozohttpd_t *httpd, const 
 
 
 /*
- * bozo_ssl_err works just like bozo_err, plus the SSL error queue
+ * 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, ...)
@@ -187,6 +207,7 @@ void
 bozo_ssl_init(bozohttpd_t *httpd)
 {
 	sslinfo_t *sslinfo = httpd->sslinfo;
+	long options;
 
 	if (sslinfo == NULL || !sslinfo->certificate_file)
 		return;
@@ -200,6 +221,18 @@ bozo_ssl_init(bozohttpd_t *httpd)
 		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,
@@ -231,7 +264,7 @@ bozo_ssl_accept(bozohttpd_t *httpd)
 
 	sslinfo->bozossl = SSL_new(sslinfo->ssl_context);
 	if (sslinfo->bozossl == NULL)
-		bozo_err(httpd, 1, "SSL_new failed");
+		bozoerr(httpd, 1, "SSL_new failed");
 
 	SSL_set_rfd(sslinfo->bozossl, 0);
 	SSL_set_wfd(sslinfo->bozossl, 1);
@@ -251,24 +284,40 @@ bozo_ssl_destroy(bozohttpd_t *httpd)
 		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 = httpd->sslinfo;
+	sslinfo_t *sslinfo = bozo_get_sslinfo(httpd);
 
-	if (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));
+	    sslinfo->certificate_file,
+	    sslinfo->privatekey_file));
 	if (!httpd->bindport)
-		httpd->bindport = strdup("https");
+		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 */

Index: src/libexec/httpd/tilde-luzah-bozo.c
diff -u src/libexec/httpd/tilde-luzah-bozo.c:1.10 src/libexec/httpd/tilde-luzah-bozo.c:1.10.4.1
--- src/libexec/httpd/tilde-luzah-bozo.c:1.10	Thu Jan  2 08:21:38 2014
+++ src/libexec/httpd/tilde-luzah-bozo.c	Sun Apr 10 10:33:11 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: tilde-luzah-bozo.c,v 1.10 2014/01/02 08:21:38 mrg Exp $	*/
+/*	$NetBSD: tilde-luzah-bozo.c,v 1.10.4.1 2016/04/10 10:33:11 martin 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/testsuite/Makefile
diff -u src/libexec/httpd/testsuite/Makefile:1.4 src/libexec/httpd/testsuite/Makefile:1.4.24.1
--- src/libexec/httpd/testsuite/Makefile:1.4	Sat May 23 02:26:03 2009
+++ src/libexec/httpd/testsuite/Makefile	Sun Apr 10 10:33:11 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/test-bigfile
diff -u src/libexec/httpd/testsuite/test-bigfile:1.1.1.1 src/libexec/httpd/testsuite/test-bigfile:1.1.1.1.30.1
--- src/libexec/httpd/testsuite/test-bigfile:1.1.1.1	Sat May 23 02:21:19 2009
+++ src/libexec/httpd/testsuite/test-bigfile	Sun Apr 10 10:33:11 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