
Custom error messages and directory redirect patch
for publicfile 0.52

Changes to httpd:

	If <hostname>/errors/<errorcode>.html exists, display it instead
	of the default error response (i.e. "file does not exist").
	Available error codes include 400, 404, 412, 417, and 501.  All
	failed open() calls are treated as error 404 (this is
	desirable).

	If the given url is a directory, but the trailing slash is
	omitted, redirect the user to the same url, slash appended.
	Note that if the browser does not supply a desired hostname, the
	prior behavior will hold true, and error code 404 will be
	returned.  Other servers may simply use the default hostname for
	redirection.  Any browsers that do not supply a hostname have no
	virtual host support, and are not likely to be used.


ari edelkind (08/16/2001)
last modified 08/16/2001


diff -r -u publicfile-0.52/error.c publicfile-0.52+errors+redirect/error.c
--- publicfile-0.52/error.c	Tue Nov  9 02:23:46 1999
+++ publicfile-0.52+errors+redirect/error.c	Tue Oct 16 22:22:19 2001
@@ -100,3 +100,11 @@
 #else
 -14;
 #endif
+
+int error_isdir =
+#ifdef EISDIR
+EISDIR;
+#else
+-15;
+#endif
+
diff -r -u publicfile-0.52/error.h publicfile-0.52+errors+redirect/error.h
--- publicfile-0.52/error.h	Tue Nov  9 02:23:46 1999
+++ publicfile-0.52+errors+redirect/error.h	Tue Oct 16 22:22:41 2001
@@ -17,6 +17,7 @@
 extern int error_perm;
 extern int error_acces;
 extern int error_nodevice;
+extern int error_isdir;
 
 extern char *error_str();
 extern int error_temp();
diff -r -u publicfile-0.52/error_str.c publicfile-0.52+errors+redirect/error_str.c
--- publicfile-0.52/error_str.c	Tue Nov  9 02:23:46 1999
+++ publicfile-0.52+errors+redirect/error_str.c	Tue Oct 16 22:22:32 2001
@@ -21,6 +21,7 @@
   X(error_perm,"permission denied")
   X(error_acces,"access denied")
   X(error_nodevice,"device not configured")
+  X(error_isdir,"is a directory")
 #ifdef ESRCH
   X(ESRCH,"no such process")
 #endif
diff -r -u publicfile-0.52/file.c publicfile-0.52+errors+redirect/file.c
--- publicfile-0.52/file.c	Tue Nov  9 02:23:46 1999
+++ publicfile-0.52+errors+redirect/file.c	Tue Oct 16 22:09:49 2001
@@ -66,6 +66,12 @@
     return -1;
   }
   if (flagread)
+    if ((st.st_mode & S_IFMT) == S_IFDIR) {
+      log(fn,": ","is a directory",flagread);
+      close(fd);
+      errno = error_isdir;
+      return -1;
+    }
     if ((st.st_mode & S_IFMT) != S_IFREG) {
       log(fn,": ","not a regular file",flagread);
       close(fd);
diff -r -u publicfile-0.52/httpd.c publicfile-0.52+errors+redirect/httpd.c
--- publicfile-0.52/httpd.c	Tue Nov  9 02:23:46 1999
+++ publicfile-0.52+errors+redirect/httpd.c	Tue Oct 16 23:41:47 2001
@@ -15,6 +15,7 @@
 #include "substdio.h"
 #include "error.h"
 #include "getln.h"
+#include <fcntl.h>
 
 int safewrite(int fd,char *buf,int len)
 {
@@ -75,8 +76,44 @@
   out_puts("\r\n");
 }
 
+stralloc fn = {0};
+stralloc contenttype = {0};
+
+extern void get();
 void barf(char *code,char *message)
 {
+
+  static char barfed = 0;
+  int codelen = str_len(code);
+  char cod[codelen];
+
+  if (!barfed) {
+    barfed++;
+    if (!stralloc_copys(&path, "errors/")) _exit(21);
+    memcpy (cod, code, codelen-1);
+    cod[codelen-1] = 0;
+    if (!stralloc_cats(&path, cod)) _exit(21);
+    if (!stralloc_cats(&path, ".html")) _exit(21);
+
+    host.len = byte_chr(host.s,host.len,':');
+    if (!host.len) {
+      if (!stralloc_copys(&host,"0")) _exit(21);
+    }
+    case_lowerb(host.s,host.len);
+    percent(&path);
+
+    if (!stralloc_copys(&fn, "./")) _exit(21);
+    if (!stralloc_cat(&fn, &host)) _exit(21);
+    if (!stralloc_cats(&fn, "/")) _exit(21);
+    if (!stralloc_cat(&fn, &path)) _exit(21);
+    if (!stralloc_0(&fn)) _exit(21);
+
+    if (close(open(fn.s, O_RDONLY)) != -1) {
+      get(code, message);
+      _exit(0);
+    }
+  }
+
   if (protocolnum > 0) {
     tai_now(&now);
     header(code,message);
@@ -100,15 +137,25 @@
   _exit(0);
 }
 
-stralloc fn = {0};
-stralloc contenttype = {0};
+void redirect(char *code, char *message)
+{
+  header (code, message);
+  out_puts("Location: ");
+  out_put(url.s, url.len);
+  out_puts("\r\n");
+  out_puts("Content-Length: 0\r\n");
+  out_puts("Content-Type: text/html\r\n");
+  out_puts("Connection: close\r\n\r\n");
+  out_flush();
+  _exit(0);
+}
 
-void get(void)
+void get(char *rcode, char *rcomment)
 {
   unsigned long length;
   int fd;
   int r;
-
+  
   host.len = byte_chr(host.s,host.len,':');
   if (!host.len) {
     if (protocolnum > 1)
@@ -126,18 +173,33 @@
   if (!stralloc_0(&fn)) _exit(21);
 
   fd = file_open(fn.s,&mtime,&length,1);
-  if (fd == -1)
+  if (fd == -1) {
+    /* if host is '0', we don't necessarily know what to return. */
+    if (errno == error_isdir && (!(host.s[0] == '0' && host.len == 1))) {
+      if (!case_startb(url.s,url.len,"http://")) {
+        if (!stralloc_copys(&url, "http://")) _exit(21);
+        if (!stralloc_cat(&url, &host)) _exit(21);
+        if (path.s[0] != '/') if (!stralloc_cats(&url, "/")) _exit(21);
+        if (!stralloc_cat(&url, &path)) _exit(21);
+      }
+      if (!stralloc_cats(&url, "/")) _exit(21);
+      redirect("302 ", "Please add a trailing slash");
+    }
     barf("404 ",error_str(errno));
+  }
 
   if (protocolnum > 0) {
     tai_now(&now);
     if (!httpdate(&mtimestr,&mtime)) _exit(21);
-    if ((ims.len < mtimestr.len) || byte_diff(mtimestr.s,mtimestr.len,ims.s))
-      header("200 ","OK");
-    else {
-      header("304 ","OK");
-      flagbody = 0;
-    }
+    if (!rcode) {
+      if ((ims.len < mtimestr.len) || byte_diff(mtimestr.s,mtimestr.len,ims.s))
+        header("200 ","OK");
+      else {
+        header("304 ","OK");
+        flagbody = 0;
+      }
+    } else
+      header(rcode,rcomment);
     if (tai_less(&mtime,&now)) {
       tai_sub(&mtimeage,&now,&mtime);
       if (tai_approx(&mtimeage) >= 60.0) {
@@ -309,6 +371,6 @@
       }
     }
 
-    get();
+    get(0, 0);
   }
 }
