From dc071c74a501c38468a02f0e1a816ff49dac7e8f Mon Sep 17 00:00:00 2001
From: Per Hedbor <ph@opera.com>
Date: Sun, 14 Nov 1999 01:47:21 +0100
Subject: [PATCH] Initial version in pike

Rev: src/modules/CommonLog/.cvsignore:1.1
Rev: src/modules/CommonLog/Makefile.in:1.1
Rev: src/modules/CommonLog/acconfig.h:1.1
Rev: src/modules/CommonLog/clf.c:1.1
Rev: src/modules/CommonLog/configure.in:1.1
Rev: src/modules/CommonLog/testsuite.in:1.1
Rev: src/modules/HTTPLoop/.cvsignore:1.1
Rev: src/modules/HTTPLoop/Makefile.in:1.1
Rev: src/modules/HTTPLoop/accept_and_parse.c:1.1
Rev: src/modules/HTTPLoop/accept_and_parse.h:1.1
Rev: src/modules/HTTPLoop/acconfig.h:1.1
Rev: src/modules/HTTPLoop/cache.c:1.1
Rev: src/modules/HTTPLoop/cache.h:1.1
Rev: src/modules/HTTPLoop/configure.in:1.1
Rev: src/modules/HTTPLoop/extensions:1.1
Rev: src/modules/HTTPLoop/filesystem.c:1.1
Rev: src/modules/HTTPLoop/filesystem.h:1.1
Rev: src/modules/HTTPLoop/log.c:1.1
Rev: src/modules/HTTPLoop/log.h:1.1
Rev: src/modules/HTTPLoop/requestobject.c:1.1
Rev: src/modules/HTTPLoop/requestobject.h:1.1
Rev: src/modules/HTTPLoop/static_strings.h:1.1
Rev: src/modules/HTTPLoop/test.pike:1.1
Rev: src/modules/HTTPLoop/testsuite.in:1.1
Rev: src/modules/HTTPLoop/timeout.c:1.1
Rev: src/modules/HTTPLoop/timeout.h:1.1
Rev: src/modules/HTTPLoop/util.c:1.1
Rev: src/modules/HTTPLoop/util.h:1.1
Rev: src/modules/HTTPLoop/wwwserver.pike:1.1
---
 .gitattributes                          |   10 +
 src/modules/CommonLog/.cvsignore        |    4 +
 src/modules/CommonLog/.gitignore        |    4 +
 src/modules/CommonLog/Makefile.in       |   17 +
 src/modules/CommonLog/acconfig.h        |   11 +
 src/modules/CommonLog/clf.c             |  726 +++++++++++++++
 src/modules/CommonLog/configure.in      |   13 +
 src/modules/CommonLog/testsuite.in      |    0
 src/modules/HTTPLoop/.cvsignore         |    4 +
 src/modules/HTTPLoop/.gitignore         |    4 +
 src/modules/HTTPLoop/Makefile.in        |   22 +
 src/modules/HTTPLoop/accept_and_parse.c |  787 ++++++++++++++++
 src/modules/HTTPLoop/accept_and_parse.h |  158 ++++
 src/modules/HTTPLoop/acconfig.h         |    9 +
 src/modules/HTTPLoop/cache.c            |  215 +++++
 src/modules/HTTPLoop/cache.h            |   19 +
 src/modules/HTTPLoop/configure.in       |  143 +++
 src/modules/HTTPLoop/extensions         |  219 +++++
 src/modules/HTTPLoop/filesystem.c       |   81 ++
 src/modules/HTTPLoop/filesystem.h       |    7 +
 src/modules/HTTPLoop/log.c              |  237 +++++
 src/modules/HTTPLoop/log.h              |    9 +
 src/modules/HTTPLoop/requestobject.c    | 1092 +++++++++++++++++++++++
 src/modules/HTTPLoop/requestobject.h    |   13 +
 src/modules/HTTPLoop/static_strings.h   |   42 +
 src/modules/HTTPLoop/test.pike          |  100 +++
 src/modules/HTTPLoop/testsuite.in       |    1 +
 src/modules/HTTPLoop/timeout.c          |  263 ++++++
 src/modules/HTTPLoop/timeout.h          |    8 +
 src/modules/HTTPLoop/util.c             |  105 +++
 src/modules/HTTPLoop/util.h             |   10 +
 src/modules/HTTPLoop/wwwserver.pike     |  194 ++++
 32 files changed, 4527 insertions(+)
 create mode 100644 src/modules/CommonLog/.cvsignore
 create mode 100644 src/modules/CommonLog/.gitignore
 create mode 100644 src/modules/CommonLog/Makefile.in
 create mode 100644 src/modules/CommonLog/acconfig.h
 create mode 100644 src/modules/CommonLog/clf.c
 create mode 100755 src/modules/CommonLog/configure.in
 create mode 100644 src/modules/CommonLog/testsuite.in
 create mode 100644 src/modules/HTTPLoop/.cvsignore
 create mode 100644 src/modules/HTTPLoop/.gitignore
 create mode 100644 src/modules/HTTPLoop/Makefile.in
 create mode 100644 src/modules/HTTPLoop/accept_and_parse.c
 create mode 100644 src/modules/HTTPLoop/accept_and_parse.h
 create mode 100644 src/modules/HTTPLoop/acconfig.h
 create mode 100644 src/modules/HTTPLoop/cache.c
 create mode 100644 src/modules/HTTPLoop/cache.h
 create mode 100644 src/modules/HTTPLoop/configure.in
 create mode 100644 src/modules/HTTPLoop/extensions
 create mode 100644 src/modules/HTTPLoop/filesystem.c
 create mode 100644 src/modules/HTTPLoop/filesystem.h
 create mode 100644 src/modules/HTTPLoop/log.c
 create mode 100644 src/modules/HTTPLoop/log.h
 create mode 100644 src/modules/HTTPLoop/requestobject.c
 create mode 100644 src/modules/HTTPLoop/requestobject.h
 create mode 100644 src/modules/HTTPLoop/static_strings.h
 create mode 100644 src/modules/HTTPLoop/test.pike
 create mode 100644 src/modules/HTTPLoop/testsuite.in
 create mode 100644 src/modules/HTTPLoop/timeout.c
 create mode 100644 src/modules/HTTPLoop/timeout.h
 create mode 100644 src/modules/HTTPLoop/util.c
 create mode 100644 src/modules/HTTPLoop/util.h
 create mode 100644 src/modules/HTTPLoop/wwwserver.pike

diff --git a/.gitattributes b/.gitattributes
index 1ced9d96be..5c45faa2aa 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -177,6 +177,10 @@ testfont binary
 /src/module.h foreign_ident
 /src/module_support.c foreign_ident
 /src/module_support.h foreign_ident
+/src/modules/CommonLog/Makefile.in foreign_ident
+/src/modules/CommonLog/acconfig.h foreign_ident
+/src/modules/CommonLog/clf.c foreign_ident
+/src/modules/CommonLog/configure.in foreign_ident
 /src/modules/Gdbm/Makefile.in foreign_ident
 /src/modules/Gdbm/acconfig.h foreign_ident
 /src/modules/Gdbm/configure.in foreign_ident
@@ -197,6 +201,12 @@ testfont binary
 /src/modules/Gz/gz_test.c foreign_ident
 /src/modules/Gz/testsuite.in foreign_ident
 /src/modules/Gz/zlibmod.c foreign_ident
+/src/modules/HTTPLoop/Makefile.in foreign_ident
+/src/modules/HTTPLoop/acconfig.h foreign_ident
+/src/modules/HTTPLoop/configure.in foreign_ident
+/src/modules/HTTPLoop/extensions foreign_ident
+/src/modules/HTTPLoop/requestobject.c foreign_ident
+/src/modules/HTTPLoop/testsuite.in foreign_ident
 /src/modules/Image/Makefile foreign_ident
 /src/modules/Image/Makefile.in foreign_ident
 /src/modules/Image/acconfig.h foreign_ident
diff --git a/src/modules/CommonLog/.cvsignore b/src/modules/CommonLog/.cvsignore
new file mode 100644
index 0000000000..5af582f193
--- /dev/null
+++ b/src/modules/CommonLog/.cvsignore
@@ -0,0 +1,4 @@
+configure
+clf_machine.h.in
+dependencies
+stamp-h.in
diff --git a/src/modules/CommonLog/.gitignore b/src/modules/CommonLog/.gitignore
new file mode 100644
index 0000000000..d4eea05f4a
--- /dev/null
+++ b/src/modules/CommonLog/.gitignore
@@ -0,0 +1,4 @@
+/configure
+/clf_machine.h.in
+/dependencies
+/stamp-h.in
diff --git a/src/modules/CommonLog/Makefile.in b/src/modules/CommonLog/Makefile.in
new file mode 100644
index 0000000000..f28118a8e9
--- /dev/null
+++ b/src/modules/CommonLog/Makefile.in
@@ -0,0 +1,17 @@
+#
+# $Id: Makefile.in,v 1.1 1999/11/14 00:47:21 per Exp $
+#
+# Makefile for CommonLog
+#
+
+SRCDIR=@srcdir@
+VPATH=@srcdir@:@srcdir@/../..:../..
+MODULE_CPPFLAGS=@DEFS@ @CPPFLAGS@
+OBJS=clf.o
+MODULE_LDFLAGS=@LDFLAGS@
+
+CONFIG_HEADERS=@CONFIG_HEADERS@
+
+@dynamic_module_makefile@
+@dependencies@
+
diff --git a/src/modules/CommonLog/acconfig.h b/src/modules/CommonLog/acconfig.h
new file mode 100644
index 0000000000..f279ca45ce
--- /dev/null
+++ b/src/modules/CommonLog/acconfig.h
@@ -0,0 +1,11 @@
+/*
+ * $Id: acconfig.h,v 1.1 1999/11/14 00:47:21 per Exp $
+ */
+
+#ifndef COMMONLOG_MACHINE_H
+#define COMMONLOG_MACHINE_H
+
+@TOP@
+@BOTTOM@
+
+#endif /* COMMONLOG_MACHINE_H */
diff --git a/src/modules/CommonLog/clf.c b/src/modules/CommonLog/clf.c
new file mode 100644
index 0000000000..3b0d0c54c7
--- /dev/null
+++ b/src/modules/CommonLog/clf.c
@@ -0,0 +1,726 @@
+
+#include "global.h"
+RCSID("$Id: clf.c,v 1.1 1999/11/14 00:47:21 per Exp $");
+#include "fdlib.h"
+#include "stralloc.h"
+#include "pike_macros.h"
+#include "object.h"
+#include "program.h"
+#include "interpret.h"
+#include "builtin_functions.h"
+#include "module_support.h"
+#include "error.h"
+
+#include "threads.h"
+#include <stdio.h>
+#include <fcntl.h>
+
+/** Forward declarations of functions implementing Pike functions **/
+
+static void f_read_clf( INT32 args );
+
+extern int fd_from_object(struct object *o);
+
+
+/** Global tables **/
+
+#ifndef CHAR_BIT
+#define CHAR_BIT	9	/* Should be safe for most hardware */
+#endif /* !CHAR_BIT */
+
+static char char_class[1<<CHAR_BIT];
+
+#define CLS_WSPACE 0
+#define CLS_CRLF   1
+#define CLS_TOKEN  2
+#define CLS_DIGIT  3
+#define CLS_QUOTE  4
+#define CLS_LBRACK 5
+#define CLS_RBRACK 6
+#define CLS_SLASH  7
+#define CLS_COLON  8
+#define CLS_HYPHEN 9
+#define CLS_MINUS  9
+#define CLS_PLUS   10
+
+
+/* How much to read each round in the loop. */
+#define CLF_BLOCK_SIZE 4096
+
+/** Externally available functions **/
+
+/* Initialize and start module */
+
+void pike_module_init( void )
+{
+  int i;
+
+  MEMSET(char_class, CLS_TOKEN, sizeof(char_class));
+  for(i='\0'; i<=' '; i++)
+    char_class[i] = CLS_WSPACE;
+  for(i='0'; i<='9'; i++)
+    char_class[i] = CLS_DIGIT;
+  char_class['\n'] = CLS_CRLF;
+  char_class['\r'] = CLS_CRLF;
+  char_class['"'] = CLS_QUOTE;
+  char_class['['] = CLS_LBRACK;
+  char_class[']'] = CLS_RBRACK;
+  char_class['/'] = CLS_SLASH;
+  char_class[':'] = CLS_COLON;
+  char_class['-'] = CLS_HYPHEN;
+  char_class['+'] = CLS_PLUS;
+
+  add_function_constant( "read", f_read_clf,
+			 "function(function(array(string|int),int|void:void),"
+			 "string|object,int|void:int)", 0 );
+}
+
+
+/* Restore and exit module */
+
+void pike_module_exit( void )
+{
+}
+
+
+/** Functions implementing Pike functions **/
+
+/* CommonLog.read() */
+
+static void f_read_clf( INT32 args )
+{
+  char *read_buf;
+  struct svalue *logfun, *file;
+  FD f = -1;
+  int cls, c, my_fd=1, state=0, tzs=0;
+  char *char_pointer;
+  INT32 v=0, yy=0, mm=0, dd=0, h=0, m=0, s=0, tz=0, offs0=0, len=0;
+  struct svalue *old_sp;
+  /* #define DYNAMIC_BUF */
+#ifdef DYNAMIC_BUF
+  dynamic_buffer buf;
+#else
+#define BUFSET(X) do { if(bufpos == bufsize) { bufsize *= 2; buf = realloc(buf, bufsize+1); } buf[bufpos++] = c; } while(0)
+#define PUSHBUF() do { push_string( make_shared_binary_string( buf,bufpos ) ); bufpos=0; } while(0)
+  char *buf;
+  int bufsize=CLF_BLOCK_SIZE, bufpos=0;
+#endif
+
+  ONERROR unwind_protect;
+
+  if(args>2 && sp[-1].type == T_INT) {
+    offs0 = sp[-1].u.integer;
+    pop_n_elems(1);
+    --args;
+  }
+  old_sp = sp;
+
+  get_all_args("CommonLog.read", args, "%*%*", &logfun, &file);
+  if(logfun->type != T_FUNCTION)
+    error("Bad argument 1 to CommonLog.read, expected function.\n");
+
+  if(file->type == T_OBJECT)
+  {
+    f = fd_from_object(file->u.object);
+    
+    if(f == -1)
+      error("CommonLog.read: File is not open.\n");
+    my_fd = 0;
+  } else if(file->type == T_STRING &&
+	    file->u.string->size_shift == 0) {
+    THREADS_ALLOW();
+    do {
+      f=fd_open(STR0(file->u.string), fd_RDONLY, 0);
+    } while(f < 0 && errno == EINTR);
+    THREADS_DISALLOW();
+    
+    if(errno < 0)
+      error("CommonLog.read(): Failed to open file for reading (errno=%d).\n",
+	    errno);
+  } else 
+    error("Bad argument 1 to CommonLog.read, expected string or object .\n");
+  
+#ifdef HAVE_LSEEK64
+  lseek64(f, offs0, SEEK_SET);
+#else
+  fd_lseek(f, offs0, SEEK_SET);
+#endif
+  read_buf = malloc(CLF_BLOCK_SIZE+1);
+#ifndef DYNAMIC_BUF
+  buf = malloc(bufsize);
+#endif
+  while(1) {
+    THREADS_ALLOW();
+    do {
+      len = fd_read(f, read_buf, CLF_BLOCK_SIZE);
+    } while(len < 0 && errno == EINTR);
+    THREADS_DISALLOW();
+    if(len == 0)
+      break; /* nothing more to read. */
+    if(len < 0)
+      break;
+    char_pointer = read_buf;
+    while(len--) {
+      offs0++;
+      c = char_pointer[0];
+      char_pointer ++;
+      cls = char_class[c];
+#ifdef TRACE_DFA
+      fprintf(stderr, "DFA(%d): '%c' ", state, (c<32? '.':c));
+      switch(cls) {
+      case CLS_WSPACE: fprintf(stderr, "CLS_WSPACE"); break;
+      case CLS_CRLF: fprintf(stderr, "CLS_CRLF"); break;
+      case CLS_TOKEN: fprintf(stderr, "CLS_TOKEN"); break;
+      case CLS_DIGIT: fprintf(stderr, "CLS_DIGIT"); break;
+      case CLS_QUOTE: fprintf(stderr, "CLS_QUOTE"); break;
+      case CLS_LBRACK: fprintf(stderr, "CLS_LBRACK"); break;
+      case CLS_RBRACK: fprintf(stderr, "CLS_RBRACK"); break;
+      case CLS_SLASH: fprintf(stderr, "CLS_SLASH"); break;
+      case CLS_COLON: fprintf(stderr, "CLS_COLON"); break;
+      case CLS_HYPHEN: fprintf(stderr, "CLS_HYPHEN/CLS_MINUS"); break;
+      case CLS_PLUS: fprintf(stderr, "CLS_PLUS"); break;
+      default: fprintf(stderr, "???");
+      }
+      fprintf(stderr, " %d items on stack\n", sp-old_sp);
+#endif
+      switch(state) {
+      case 0:
+	if(sp != old_sp) {
+	  if(sp == old_sp+15) {
+	    f_aggregate(15);
+	    push_int(offs0);
+	    apply_svalue(logfun, 2);
+	    pop_stack();
+	  } else
+	    pop_n_elems(sp-old_sp);
+	}
+	if(cls > CLS_CRLF) {
+	  if(cls == CLS_HYPHEN) {
+	    push_int(0);
+	    state = 2;
+	    break;
+	  }
+#ifdef DYNAMIC_BUF
+	  buf.s.str = NULL;
+	  initialize_buf( &buf );
+	  low_my_putchar( c, &buf );
+#else
+	  bufpos = 0;
+	  BUFSET(c);
+#endif
+	  state=1;
+	}
+	break;
+      case 1:
+	if(cls > CLS_CRLF) {
+#ifdef DYNAMIC_BUF
+	  low_my_putchar( c, &buf );
+#else
+	  BUFSET(c);
+#endif
+	  break;
+	}
+#ifdef DYNAMIC_BUF
+	push_string( low_free_buf( &buf ) ); /* remotehost */
+#else
+	PUSHBUF();
+#endif
+	state = (cls == CLS_WSPACE? 2:0);
+	break;
+      case 2:
+	if(cls > CLS_CRLF) {
+	  if(cls == CLS_HYPHEN) {
+	    push_int(0);
+	    state = 4;
+	    break;
+	  }
+#ifdef DYNAMIC_BUF
+	  buf.s.str = NULL;
+	  initialize_buf( &buf );
+	  low_my_putchar( c, &buf );
+#else
+	  bufpos = 0;
+	  BUFSET(c);
+#endif
+
+	  state=3;
+	} else if(cls == CLS_CRLF)
+	  state=0;
+	break;
+      case 3:
+	if(cls > CLS_CRLF) {
+#ifdef DYNAMIC_BUF
+	  low_my_putchar( c, &buf );
+#else
+	  BUFSET(c);
+#endif
+
+	  break;
+	}
+#ifdef DYNAMIC_BUF
+	push_string( low_free_buf( &buf ) ); /* rfc931 */
+#else
+	PUSHBUF(); /* rfc931 */
+#endif
+	state = (cls == CLS_WSPACE? 4:0);
+	break;
+      case 4:
+	if(cls > CLS_CRLF) {
+	  if(cls == CLS_HYPHEN) {
+	    push_int(0);
+	    state = 6;
+	    break;
+	  }
+#ifdef DYNAMIC_BUF
+	  buf.s.str = NULL;
+	  initialize_buf( &buf );
+	  low_my_putchar( c, &buf );
+#else
+	  bufpos = 0;
+	  BUFSET(c);
+#endif
+
+	  state=5;
+	} else if(cls == CLS_CRLF)
+	  state=0;
+	break;
+      case 5:
+	if(cls > CLS_CRLF) {
+#ifdef DYNAMIC_BUF
+	  low_my_putchar( c, &buf );
+#else
+	  BUFSET(c);
+#endif
+
+	  break;
+	}
+#ifdef DYNAMIC_BUF
+	push_string( low_free_buf( &buf ) ); /* authuser */
+#else
+	PUSHBUF(); /* authuser */
+#endif
+	state = (cls == CLS_WSPACE? 6:0);
+	break;
+      case 6:
+	if(cls == CLS_LBRACK)
+	  state = 15;
+	else if(cls == CLS_CRLF)
+	  state = 0;
+	else if(cls == CLS_HYPHEN) {
+	  push_int(0);
+	  push_int(0);
+	  push_int(0);
+	  state = 7;
+	}
+	break;
+      case 7:
+	if(cls == CLS_QUOTE) {
+#ifdef DYNAMIC_BUF
+	  buf.s.str = NULL;
+	  initialize_buf( &buf );
+#else
+	  bufpos = 0;
+#endif
+	  state = 31;
+	} else if(cls == CLS_CRLF)
+	  state = 0;
+	else if(cls == CLS_HYPHEN) {
+	  push_int(0);
+	  push_int(0);
+	  push_int(0);
+	  state = 10;
+	}
+	break;
+      case 8:
+	if(cls == CLS_QUOTE)
+	  state = 9;
+	else if(cls == CLS_CRLF) {
+#ifdef DYNAMIC_BUF
+	  push_string( low_free_buf( &buf ) );
+#else
+	  PUSHBUF();
+#endif
+	  state = 0;
+	} else
+#ifdef DYNAMIC_BUF
+	  low_my_putchar( c, &buf );
+#else
+	  BUFSET(c);
+#endif
+
+	break;
+      case 9:
+	if(cls > CLS_CRLF) {
+#ifdef DYNAMIC_BUF
+	  low_my_putchar( '"', &buf);
+	  low_my_putchar( c, &buf);
+#else
+	  BUFSET('"');
+	  BUFSET(c);
+#endif
+	  state = 8;
+	  break;
+	}
+#ifdef DYNAMIC_BUF
+	push_string( low_free_buf( &buf ) ); /* protocol */
+#else
+	PUSHBUF(); /* protoocl */
+#endif
+	state = (cls == CLS_CRLF? 0 : 10);
+	break;
+      case 10:
+	if(cls == CLS_DIGIT) {
+	  v = c&0xf;
+	  state = 11;
+	} else if(cls == CLS_CRLF)
+	  state = 0;
+	else if(cls == CLS_HYPHEN) {
+	  push_int(0);
+	  state = 12;
+	}
+	break;
+      case 11:
+	if(cls == CLS_DIGIT)
+	  v = v*10+(c&0xf);
+	else if(cls == CLS_WSPACE) {
+	  push_int(v); /* status */
+	  state = 12;
+	} else state = 0;
+	break;
+      case 12:
+	if(cls == CLS_DIGIT) {
+	  v = c&0xf;
+	  state = 13;
+	} else if(cls == CLS_CRLF)
+	  state = 0;
+	else if(cls == CLS_HYPHEN) {
+	  push_int(0);
+	  state = 14;
+	}
+	break;
+      case 13:
+	if(cls == CLS_DIGIT)
+	  v = v*10+(c&0xf);
+	else {
+	  push_int(v); /* bytes */
+	  state = (cls == CLS_CRLF? 0:14);
+	}
+	break;
+      case 14:
+	if(cls == CLS_CRLF)
+	  state = 0;
+	break;
+
+      case 15:
+	if(cls == CLS_DIGIT) {
+	  dd = c&0xf;
+	  state = 16;
+	} else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 16:
+	/* getting day */
+	if(cls == CLS_DIGIT)
+	  dd = dd*10+(c&0xf);
+	else if(cls == CLS_SLASH)
+	  state = 17;
+	else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 17:
+	if(cls == CLS_DIGIT) {
+	  mm = c&0xf;
+	  state = 18;
+	} else if(cls == CLS_TOKEN) {
+	  mm = c|0x20;
+	  state = 21;
+	} else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 18:
+	/* getting numeric month */
+	if(cls == CLS_DIGIT)
+	  mm = mm*10+(c&0xf);
+	else if(cls == CLS_SLASH)
+	  state = 19;
+	else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 19:
+	if(cls == CLS_DIGIT) {
+	  yy = c&0xf;
+	  state = 20;
+	} else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 20:
+	/* getting year */
+	if(cls == CLS_DIGIT)
+	  yy = yy*10+(c&0xf);
+	else if(cls == CLS_COLON)
+	  state = 22;
+	else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 21:
+	/* getting textual month */
+	if(cls == CLS_TOKEN)
+	  mm = (mm<<8)|c|0x20;
+	else if(cls == CLS_SLASH) {
+	  state = 19;
+	  switch(mm) {
+	  case ('j'<<16)|('a'<<8)|'n': mm=1; break;
+	  case ('f'<<16)|('e'<<8)|'b': mm=2; break;
+	  case ('m'<<16)|('a'<<8)|'r': mm=3; break;
+	  case ('a'<<16)|('p'<<8)|'r': mm=4; break;
+	  case ('m'<<16)|('a'<<8)|'y': mm=5; break;
+	  case ('j'<<16)|('u'<<8)|'n': mm=6; break;
+	  case ('j'<<16)|('u'<<8)|'l': mm=7; break;
+	  case ('a'<<16)|('u'<<8)|'g': mm=8; break;
+	  case ('s'<<16)|('e'<<8)|'p': mm=9; break;
+	  case ('o'<<16)|('c'<<8)|'t': mm=10; break;
+	  case ('n'<<16)|('o'<<8)|'v': mm=11; break;
+	  case ('d'<<16)|('e'<<8)|'c': mm=12; break;
+	  default:
+	    state = 14;
+	  }
+	}
+	break;
+      case 22:
+	if(cls == CLS_DIGIT) {
+	  h = c&0xf;
+	  state = 23;
+	} else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 23:
+	/* getting hour */
+	if(cls == CLS_DIGIT)
+	  h = h*10+(c&0xf);
+	else if(cls == CLS_COLON)
+	  state = 24;
+	else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 24:
+	if(cls == CLS_DIGIT) {
+	  m = c&0xf;
+	  state = 25;
+	} else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 25:
+	/* getting minute */
+	if(cls == CLS_DIGIT)
+	  m = m*10+(c&0xf);
+	else if(cls == CLS_COLON)
+	  state = 26;
+	else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 26:
+	if(cls == CLS_DIGIT) {
+	  s = c&0xf;
+	  state = 27;
+	} else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 27:
+	/* getting second */
+	if(cls == CLS_DIGIT)
+	  s = s*10+(c&0xf);
+	else if(cls == CLS_WSPACE)
+	  state = 28;
+	else
+	  state = (cls == CLS_CRLF? 0:14);
+	break;
+      case 28:
+	if(cls>=CLS_MINUS) {
+	  state = 29;
+	  tzs = cls!=CLS_PLUS;
+	  tz = 0;
+	} else if(cls == CLS_DIGIT) {
+	  state = 29;
+	  tzs = 0;
+	  tz = c&0xf;
+	} else if(cls==CLS_CRLF)
+	  state = 0;
+	break;
+      case 29:
+	/* getting timezone */
+	if(cls == CLS_DIGIT)
+	  tz = tz*10+(c&0xf);
+	else {
+	  if(tzs)
+	    tz = -tz;
+	  push_int(yy);
+	  push_int(mm);
+	  push_int(dd);
+	  push_int(h);
+	  push_int(m);
+	  push_int(s);
+	  push_int(tz);
+	  if(cls == CLS_RBRACK)
+	    state = 7;
+	  else
+	    state = (cls == CLS_CRLF? 0 : 30);
+	}
+	break;
+      case 30:
+	if(cls == CLS_RBRACK)
+	  state = 7;
+	else if(cls == CLS_CRLF)
+	  state = 0;
+	break;
+      case 31:
+	if(cls == CLS_QUOTE) {
+#ifdef DYNAMIC_BUF
+	  push_string( low_free_buf( &buf ) );
+#else
+	  PUSHBUF();
+#endif
+	  push_int(0);
+	  push_int(0);
+	  state = 10;
+	} else if(cls >= CLS_TOKEN)
+#ifdef DYNAMIC_BUF
+	  low_my_putchar( c, &buf );
+#else
+	  BUFSET(c);
+#endif
+
+	else {
+#ifdef DYNAMIC_BUF
+	  push_string( low_free_buf( &buf ) ); /* method */
+#else
+	  PUSHBUF(); /* method */
+#endif
+	  state = (cls == CLS_CRLF? 0 : 32);
+	}
+	break;
+      case 32:
+	if(cls == CLS_QUOTE) {
+	  push_int(0);
+	  push_int(0);
+	  state = 10;
+	} else if(cls >= CLS_TOKEN) {
+#ifdef DYNAMIC_BUF
+	  buf.s.str = NULL;
+	  initialize_buf( &buf );
+	  low_my_putchar( c, &buf );
+#else
+	  bufpos = 0;
+	  BUFSET(c);
+#endif
+
+	  state = 33;
+	} else
+	  if(cls == CLS_CRLF)
+	    state = 0;
+	break;
+      case 33:
+	if(cls == CLS_QUOTE)
+	  state = 34;
+	else if(cls == CLS_CRLF) {
+#ifdef DYNAMIC_BUF
+	  push_string( low_free_buf( &buf ) ); 
+#else
+	  PUSHBUF(); 
+#endif
+	  state = 0;
+	} else if(cls == CLS_WSPACE) {
+#ifdef DYNAMIC_BUF
+	  push_string( low_free_buf( &buf ) );  /* path */
+#else
+	  PUSHBUF();  /* path */
+#endif
+	  state = 35;
+	} else	
+#ifdef DYNAMIC_BUF
+	  low_my_putchar( c, &buf );
+#else
+	  BUFSET(c);
+#endif
+
+	break;
+      case 34:
+	if(cls >= CLS_TOKEN) {
+#ifdef DYNAMIC_BUF
+	  low_my_putchar( '"', &buf );
+	  low_my_putchar( c, &buf );
+#else
+	  BUFSET('"');
+	  BUFSET(c);
+#endif
+
+	  state = 33;
+	} else if(cls == CLS_CRLF) {
+#ifdef DYNAMIC_BUF
+	  push_string( low_free_buf( &buf ) ); 
+#else
+	  PUSHBUF(); 
+#endif
+	  state = 0;
+	} else {
+#ifdef DYNAMIC_BUF
+	  push_string( low_free_buf( &buf ) ); 
+#else
+	  PUSHBUF(); 
+#endif
+	  push_text("HTTP/0.9");
+	  state = 10;
+	}
+	break;
+      case 35:
+	if(cls == CLS_QUOTE) {
+	  push_text("HTTP/0.9");
+	  state = 10;
+	} else if(cls >= CLS_TOKEN) {
+#ifdef DYNAMIC_BUF
+	  buf.s.str = NULL;
+	  initialize_buf( &buf );
+	  low_my_putchar( c, &buf );
+#else
+	  bufpos = 0;
+	  BUFSET(c);
+#endif
+
+	  state = 8;
+	} else
+	  if(cls == CLS_CRLF)
+	    state = 0;
+	break;
+      }
+    }
+  }
+  if(state == 1 || state == 3 || state == 5 ||
+     state == 8 || state == 9 ||
+     state == 31 || state == 33 || state == 34) {
+#ifdef DYNAMIC_BUF
+    push_string( low_free_buf( &buf ) ); 
+#else
+    PUSHBUF(); 
+#endif
+  }
+  if(sp == old_sp + 15) {
+    f_aggregate(15);
+    push_int(offs0);
+    apply_svalue(logfun, 2);
+    pop_stack();
+  }
+  free(read_buf);
+#ifndef DYNAMIC_BUF
+  free(buf);
+#endif
+  if(my_fd)
+    /* If my_fd == 0, the second argument was an object and thus we don't
+     * want to free it.
+     */
+    fd_close(f);
+  pop_n_elems(sp-old_sp+args);  
+  push_int(offs0);
+}
+
+
+
+
diff --git a/src/modules/CommonLog/configure.in b/src/modules/CommonLog/configure.in
new file mode 100755
index 0000000000..d55780712d
--- /dev/null
+++ b/src/modules/CommonLog/configure.in
@@ -0,0 +1,13 @@
+#
+# $Id: configure.in,v 1.1 1999/11/14 00:47:21 per Exp $
+#
+# configure.in for CommonLog.
+#
+
+AC_REVISION("$Id: configure.in,v 1.1 1999/11/14 00:47:21 per Exp $")
+AC_INIT(clf.c)
+AC_CONFIG_HEADER(clf_machine.h)
+
+AC_MODULE_INIT()
+
+AC_OUTPUT(Makefile, [echo foo >stamp-h])
diff --git a/src/modules/CommonLog/testsuite.in b/src/modules/CommonLog/testsuite.in
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/modules/HTTPLoop/.cvsignore b/src/modules/HTTPLoop/.cvsignore
new file mode 100644
index 0000000000..78360801d6
--- /dev/null
+++ b/src/modules/HTTPLoop/.cvsignore
@@ -0,0 +1,4 @@
+configure
+dependencies
+config.h.in
+stamp-h.in
diff --git a/src/modules/HTTPLoop/.gitignore b/src/modules/HTTPLoop/.gitignore
new file mode 100644
index 0000000000..8a856a6add
--- /dev/null
+++ b/src/modules/HTTPLoop/.gitignore
@@ -0,0 +1,4 @@
+/configure
+/dependencies
+/config.h.in
+/stamp-h.in
diff --git a/src/modules/HTTPLoop/Makefile.in b/src/modules/HTTPLoop/Makefile.in
new file mode 100644
index 0000000000..6837036bc8
--- /dev/null
+++ b/src/modules/HTTPLoop/Makefile.in
@@ -0,0 +1,22 @@
+# $Id: Makefile.in,v 1.1 1999/11/14 00:43:14 per Exp $
+SRCDIR=@srcdir@
+VPATH=@srcdir@:@srcdir@/../..:../..
+OBJS=accept_and_parse.o util.o cache.o requestobject.o log.o timeout.o 
+# filesystem.o
+MODNAME=HTTPAccept
+MODULE_LDFLAGS=@LDFLAGS@
+
+CONFIG_HEADERS=@CONFIG_HEADERS@
+
+@SET_MAKE@
+
+@dynamic_module_makefile@
+
+pike: all
+	cd ../..; $(MAKE) "AR=$(AR)"
+
+pure: all
+	cd ../..; $(MAKE) "AR=$(AR)" pure
+
+
+@dependencies@
diff --git a/src/modules/HTTPLoop/accept_and_parse.c b/src/modules/HTTPLoop/accept_and_parse.c
new file mode 100644
index 0000000000..6a3a4634b3
--- /dev/null
+++ b/src/modules/HTTPLoop/accept_and_parse.c
@@ -0,0 +1,787 @@
+/* Hohum. Here we go. This is try number four for a more optimized
+ *  Roxen.
+ */
+
+#include "config.h"
+/* #define AAP_DEBUG */
+
+#define PARSE_FAILED ("HTTP/1.0 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\nRequest parsing failed.\r\n")
+
+#include <global.h>
+	  
+#include <array.h>
+#include <backend.h>
+#include <machine.h>
+#include <mapping.h>
+#include <module_support.h>
+#include <multiset.h>
+#include <object.h>
+#include <operators.h>
+#include <pike_memory.h>
+#include <fdlib.h>
+#include <program.h>
+#include <stralloc.h>
+#include <svalue.h>
+#include <threads.h>
+
+#ifdef _REENTRANT
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "accept_and_parse.h"
+#include "log.h"
+#include "cache.h"
+#include "requestobject.h"
+#include "util.h"
+#include "timeout.h"
+
+static struct callback *my_callback;
+struct program *request_program;
+struct program *c_request_program;
+struct program *accept_loop_program;
+
+
+/* Define external global string variables ... */
+#define STRING(X,Y) extern struct pike_string *X
+#include "static_strings.h"
+#undef STRING
+/* there.. */
+
+void f_aggregate(INT32);
+
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a)<(b)?(b):(a))
+#endif
+
+#define MAXLEN (1024*1024*10)
+static MUTEX_T queue_mutex;
+static struct args *request, *last;
+
+/* This should probably be improved to include the reason for the 
+ * failure. Currently, all failed requests get the same (hardcoded) 
+ * error message.
+ * 
+ * It is virtually impossible to call a pike function from here, so that
+ * is not an option.
+ */
+static void failed(struct args *arg)
+{
+  WRITE(arg->fd, PARSE_FAILED, strlen(PARSE_FAILED));
+#ifdef AAP_DEBUG
+  fprintf(stderr, "AAP: Failed\n");
+#endif /* AAP_DEBUG */
+  close(arg->fd);
+  if(arg->res.data)
+    free(arg->res.data); 
+  free(arg);
+}
+
+
+/* Parse a request. This function is somewhat obscure for performance
+ * reasons.
+ */
+static int parse(struct args *arg)
+{
+  int s1=0, s2=0, i;
+  struct cache_entry *ce, *p; 
+  /* get: URL, Protocol, method, headers*/
+  for(i=0;i<arg->res.data_len;i++)
+    if(arg->res.data[i] == ' ') {
+      if(!s1) 
+	s1=i; 
+      else
+	s2=i;
+    } else if(arg->res.data[i]=='\r')
+      break;
+
+  if(!s1)
+  {
+    failed( arg );
+    return 0;
+  }
+
+  /* GET http://www.roxen.com/foo.html HTTP/1.1\r
+   * 0  ^s1                           ^s2      ^i
+   */
+
+  if(!s2) 
+    arg->res.protocol = s_http_09;
+  else if(!memcmp("HTTP/1.", arg->res.data+s2+1, 7))
+  {
+    if(arg->res.data[s2+8]=='0')
+      arg->res.protocol = s_http_10;
+    else if(arg->res.data[s2+8]=='1')
+      arg->res.protocol = s_http_11;
+  }
+  else
+    arg->res.protocol = 0;
+
+  arg->res.method_len = s1;
+
+  if(arg->res.protocol != s_http_09)
+    arg->res.header_start = i+2;
+  else
+    arg->res.header_start = 0;
+
+
+  /* find content */
+  arg->res.content_len=0;
+  aap_get_header(arg, "content-length", H_INT, &arg->res.content_len);
+  if((arg->res.data_len - arg->res.body_start) < arg->res.content_len)
+  {
+    int nr;
+    /* read the rest of the request.. OBS: This is done without any
+     * timeout right now. It is relatively easy to add one, though.
+     * The only problem is that this might be an upload of a _large_ file,
+     * if so the upload could take an hour or more. So there can be no 
+     * sensible default timeout. The only option is to reshedule the timeout
+     * for each and every read.
+     *
+     * This code should probably not allocate infinite amounts of 
+     * memory either...
+     * 
+     * TODO: rewrite this to use a mmaped file if the size is bigger than
+     * 1Mb or so.
+     * 
+     * This could cause trouble with the leftovers code below, so that 
+     * would have to be changed as well.
+     */
+    arg->res.data=realloc(arg->res.data,
+			  arg->res.body_start+arg->res.content_len);
+    while( arg->res.data_len < arg->res.body_start+arg->res.content_len)
+    {
+      while(((nr = fd_read(arg->fd, arg->res.data+arg->res.data_len, 
+			   (arg->res.body_start+arg->res.content_len)-
+			   arg->res.data_len)) < 0) && errno == EINTR);
+      if(nr <= 0)
+      {
+	failed(arg);
+	return 0;
+      }
+      arg->res.data_len += nr;
+    }
+  }
+  /* ok.. now we should find the leftovers... This is any extra
+   * data not belonging to this request that has already been read.
+   * Since it is rather hard to unread things in a portable way, we simply
+   * store them in the result structure for the next request.
+   */
+
+  arg->res.leftovers_len=
+    (arg->res.data_len-arg->res.body_start-arg->res.content_len);
+  if(arg->res.leftovers_len)
+    arg->res.leftovers=
+      (arg->res.data+arg->res.body_start+arg->res.content_len);
+
+  arg->res.url = arg->res.data+s1+1;
+  arg->res.url_len = (s2?s2:i)-s1-1;
+  
+  {
+    int hv;
+    struct pstring h;
+    h.len=0;
+    h.str="";
+    if(aap_get_header(arg, "host", H_STRING, &h))
+    {
+      arg->res.host = h.str;
+      arg->res.host_len = h.len;
+    } else {
+      arg->res.host = arg->res.data;
+      arg->res.host_len = 0;
+    }
+
+    if(arg->cache->max_size && arg->res.data[0]== 'G') /* GET, presumably */
+    {
+      if(!aap_get_header(arg, "pragma", H_EXISTS, 0))
+	if((ce = aap_cache_lookup(arg->res.url, arg->res.url_len, 
+			      arg->res.host, arg->res.host_len,
+			      arg->cache,0, &p, &hv)) && ce->data)
+	{
+	  int len = WRITE(arg->fd, ce->data->str, ce->data->len);
+	  LOG(len, arg, atoi(ce->data->str+MY_MIN(ce->data->len, 9))); 
+	  simple_aap_free_cache_entry( arg->cache, ce );
+	  /* if keepalive... */
+	  if((arg->res.protocol==s_http_11)
+	     ||aap_get_header(arg, "connection", H_EXISTS, 0))
+	  {
+	    return -1; 
+	  }
+#ifdef AAP_DEBUG
+	  fprintf(stderr, "Closing connection...\n");
+#endif /* AAP_DEBUG */
+	  close(arg->fd); 
+	  free(arg->res.data);
+	  free(arg);
+	  return 0;
+	}
+    }
+  }
+  return 1;
+}
+
+void aap_handle_connection(struct args *arg)
+{
+  char *buffer, *p, *tmp;
+  int pos, buffer_len;
+#ifdef HAVE_TIMEOUTS
+  int *timeout = NULL;
+#endif
+ start:
+  pos=0;
+  buffer_len=8192;
+
+  if(arg->res.data && arg->res.data_len > 0)
+  {
+    p = buffer = arg->res.data;
+    buffer_len = MAX(arg->res.data_len,8192);
+    arg->res.data=0;
+  }
+  else 
+    p = buffer = malloc(8192);
+
+  if(arg->res.leftovers && arg->res.leftovers_len)
+  {
+    if(!buffer)
+    {
+      perror("AAP: Failed to allocate buffer (leftovers)");
+      failed(arg);
+      return;
+    }
+    buffer_len = arg->res.leftovers_len;
+    MEMCPY(buffer, arg->res.leftovers, arg->res.leftovers_len);
+    pos = arg->res.leftovers_len;
+    arg->res.leftovers=0;
+    if((tmp = my_memmem("\r\n\r\n", 4, buffer, pos)))
+      goto ok;
+    p += arg->res.leftovers_len;
+  }
+  
+  if(!buffer)
+  {
+    perror("AAP: Failed to allocate buffer");
+    failed(arg);
+    return;
+  }
+#ifdef HAVE_TIMEOUTS
+  timeout = aap_add_timeout_thr(th_self(), arg->timeout);
+  while( !(*timeout) )
+#else
+  while(1)
+#endif /* HAVE_TIMEOUTS */
+  {
+    int data_read = fd_read(arg->fd, p, buffer_len-pos);
+    if(data_read <= 0)
+    {
+      free(buffer);
+#ifdef AAP_DEBUG
+      fprintf(stderr, "AAP: Read error/eof.\n");
+#endif /* AAP_DEBUG */
+      close(arg->fd);
+      free(arg);
+#ifdef HAVE_TIMEOUTS
+      aap_remove_timeout_thr( timeout );
+#endif
+      return;
+    }
+    pos += data_read;
+    if((tmp = my_memmem("\r\n\r\n", 4, MAX(p-3, buffer), 
+			data_read+(p-3>buffer?3:0))))
+      goto ok;
+    p += data_read;
+    if(pos >= buffer_len)
+    {
+      buffer_len *= 2;
+      if(buffer_len > MAXLEN)
+	break;
+
+      buffer = realloc(buffer, buffer_len);
+      p = buffer+pos;
+      if(!buffer) 
+      {
+	perror("AAP: Failed to allocate memory (reading)");
+	break;
+      }
+    }
+  }
+
+  arg->res.data = buffer;
+  failed( arg );
+#ifdef HAVE_TIMEOUTS
+  aap_remove_timeout_thr( timeout );
+#endif
+  return;
+
+ ok:
+#ifdef HAVE_TIMEOUTS
+  if (timeout) {
+    aap_remove_timeout_thr( timeout );
+  }
+#endif /* HAVE_TIMEOUTS */
+  arg->res.body_start = (tmp+4)-buffer;
+  arg->res.data = buffer;
+  arg->res.data_len = pos;
+  switch(parse(arg))
+  {
+   case 1:
+    mt_lock(&queue_mutex);
+    if(!request)
+    {
+      request = last = arg;
+      arg->next = 0;
+    }
+    else
+    {
+      last->next = arg;
+      last = arg;
+      arg->next = 0;
+    }
+    mt_unlock(&queue_mutex);
+    wake_up_backend();
+    return;
+
+   case -1:
+     goto start;
+
+   case 0:
+    ;
+  }
+}
+
+#ifndef HAVE_AND_USE_POLL
+#undef HAVE_POLL
+#endif /* !HAVE_AND_USE_POLL */
+
+#ifdef HAVE_POLL
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else /* !HAVE_POLL_H */
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#else  /* !HAVE_SYS_POLL_H */
+/* No <poll.h> or <sys/poll.h> */
+#undef HAVE_POLL
+#endif /* HAVE_SYS_POLL_H */
+#endif /* HAVE_POLL_H */
+
+#ifndef POLLRDBAND
+#define POLLRDBAND	POLLPRI
+#endif /* !POLLRDBAND */
+
+#endif /* HAVE_POLL */
+
+static void low_accept_loop(struct args *arg)
+{
+  struct args *arg2 = malloc(sizeof(struct args));
+  ACCEPT_SIZE_T len = sizeof(arg->from);
+#if 0 && defined(SOLARIS) && defined(HAVE_POLL)
+  struct pollfd fds[1];
+  fds[0].fd = arg->fd;
+  fds[0].events = POLLIN|POLLRDBAND;
+#endif
+  while(1)
+  {
+    MEMCPY(arg2, arg, sizeof(struct args));
+#if 0 && defined(SOLARIS) && defined(HAVE_POLL)
+    while(poll(fds,1,-1)==-1)   /* this makes it possible to close the fd. */
+      ;
+#endif
+    arg2->fd = fd_accept(arg->fd, (struct sockaddr *)&arg2->from, &len);
+    if(arg2->fd != -1)
+    {
+      th_farm((void (*)(void *))aap_handle_connection, arg2);
+      arg2 = malloc(sizeof(struct args));
+      arg2->res.leftovers = 0;
+    } else {
+      if(errno == EBADF)
+      {
+	int i;
+	struct cache_entry *e, *t;
+	struct cache *c, *p = NULL;
+	struct log *l, *n = NULL;
+	/* oups. */
+	mt_lock( &interpreter_lock );
+	for(i=0; i<CACHE_HTABLE_SIZE; i++)
+	{
+	  e = arg->cache->htable[i];
+	  while(e)
+	  {
+	    t = e;
+	    e = e->next;
+	    t->next = 0;
+	    free_string(t->data);
+	    free(t->url);
+	    free(t->host);
+	    free(t);
+	  }
+	}
+	while(arg->log->log_head)
+	{
+	  struct log_entry *l = arg->log->log_head->next;
+	  free(arg->log->log_head);
+	  arg->log->log_head = l;
+	}
+
+	c = first_cache;
+	while(c && c != arg->cache) {p=c;c = c->next;}
+	if(c)
+	{
+	  if(p) 
+	    p->next = c->next;
+	  else
+	    first_cache = c->next;
+	  c->gone = 1;
+	  free(c);
+	}
+
+
+	l = aap_first_log;
+	while(l && l != arg->log) {n=l;l = l->next;}
+	if(l)
+	{
+	  if(n)    n->next = l->next;
+	  else     aap_first_log = l->next;
+	  free(l);
+	}
+	mt_unlock( &interpreter_lock );
+	free(arg2);
+	free(arg);
+	return; /* No more accept here.. */
+      }
+    }
+  }
+}
+
+
+static void finished_p(struct callback *foo, void *b, void *c)
+{
+  while(request)
+  {
+    struct args *arg;
+    struct object *o;
+    struct c_request_object *obj;
+    mt_lock(&queue_mutex);
+    arg = request;
+    request = arg->next;
+    mt_unlock(&queue_mutex);
+#if defined(DEBUG) || defined(PIKE_DEBUG)
+    if(!arg->res.url) /* not parsed!? */
+      fatal("AAP: Very odd request.\n");
+#endif
+
+    if(arg->cache->unclean) 
+      /* This must be done from the backend.
+       * That's why we do it here.
+       */ 
+      aap_clean_cache(arg->cache, 0); 
+
+    o = clone_object( request_program, 0 ); /* see requestobject.c */
+    obj = (struct c_request_object *)get_storage(o, c_request_program );
+    MEMSET(obj, 0, sizeof(struct c_request_object));
+    obj->request = arg;
+    obj->done_headers = allocate_mapping( 20 );
+    obj->misc_variables = allocate_mapping( 40 );
+
+    apply(o, "__init", 0); pop_stack();
+
+    push_object( o );
+    assign_svalue_no_free(sp++, &arg->args);
+
+    /*  CATCH?  */
+    apply_svalue(&arg->cb, 2);
+    pop_stack();
+  }
+}
+
+static void f_accept_with_http_parse(INT32 nargs)
+{
+/* From socket.c */
+  struct port 
+  {
+    int fd;
+    int my_errno;
+    struct svalue accept_callback;
+    struct svalue id;
+  };
+  int ms, dolog;
+  int to;
+  struct object *port;
+  struct svalue *fun, *cb, *program;
+  struct cache *c;
+  struct args *args = LTHIS; 
+  get_all_args("accept_http_loop", nargs, "%o%*%*%*%d%d%d", &port, &program,
+	       &fun, &cb, &ms, &dolog, &to);
+  MEMSET(args, 0, sizeof(struct args));
+  if(dolog)
+  {
+    struct log *log = malloc(sizeof(struct log));
+    MEMSET(log, 0, sizeof(struct log));
+    args->log = log;
+    log->next = aap_first_log;
+    aap_first_log = log;
+  }
+  c = malloc(sizeof(struct cache));
+  c->next = first_cache;
+  first_cache = c;
+  MEMSET(c, 0, sizeof(struct cache));
+  args->cache = c;
+  c->max_size = ms;
+  args->fd = ((struct port *)port->storage)->fd;
+  args->filesystem = NULL;
+#ifdef HAVE_TIMEOUTS
+  args->timeout = to;
+#endif
+  assign_svalue_no_free(&args->cb, fun);
+  assign_svalue_no_free(&args->args, cb);
+
+  request_program = program_from_svalue( program );
+  if(!request_program)
+  {
+    free(args);
+    error("Invalid request program\n");
+  }
+  if(!my_callback)
+    my_callback = add_backend_callback( finished_p, 0, 0 );
+
+  {
+    int i;
+    for( i = 0; i<8; i++ )
+      th_farm((void (*)(void *))low_accept_loop, (void *)args);
+  }
+}
+
+static void f_cache_status(INT32 args)
+{
+  struct cache *c = LTHIS->cache;
+  pop_n_elems(args);
+  push_text("hits"); push_int(c->hits);
+  push_text("misses"); push_int(c->misses);
+  push_text("stale"); push_int(c->stale);
+  push_text("size"); push_int(c->size);
+  push_text("entries"); push_int(c->entries);
+  push_text("max_size"); push_int(c->max_size);
+
+  push_text("sent_bytes"); push_int(c->sent_data);        c->sent_data=0;
+  push_text("num_request"); push_int(c->num_requests);    c->num_requests=0;
+  push_text("received_bytes"); push_int(c->received_data);c->received_data=0;
+  f_aggregate_mapping( 18 );
+}
+
+#endif /* _REENTRANT */
+
+void f_aap_add_filesystem( INT32 args )
+{
+  int nosyms = 0;
+  struct pike_string *basedir, *mountpoint;
+  struct array *noparse;
+  struct pstring **skip_extensions;
+
+  if(args == 4)
+    get_all_args( "add_filesystem", args, 
+                  "%s%s%a%d", &basedir, &mountpoint, &noparse, &nosyms );
+  else
+    get_all_args( "add_filesystem", args, 
+                  "%s%s%a", &basedir, &mountpoint, &noparse );
+  
+
+}
+
+
+
+
+#ifndef OFFSETOF
+#define OFFSETOF(str_type, field) ((long)& (((struct str_type *)0)->field))
+#endif
+
+void pike_module_init()
+{
+#ifdef _REENTRANT
+  int offset;
+#define STRING(X,Y) X=make_shared_string(Y)
+#include "static_strings.h"
+#undef STRING
+
+#ifdef HAVE_TIMEOUTS
+  aap_init_timeouts();
+#endif
+
+  start_new_program();
+#ifdef ADD_STORAGE
+  ADD_STORAGE(struct args);
+#else
+  add_storage(sizeof(struct args));
+#endif
+  add_function("create", f_accept_with_http_parse, 
+	       "function(object,program,function,mixed,int,int,int:void)", 0);
+  add_function("cache_status", f_cache_status,"function(void:mapping)", 0);
+  add_function("log_as_array",f_aap_log_as_array,"function(void:array(object))",0);
+  add_function("log_as_commonlog_to_file", f_aap_log_as_commonlog_to_file, 
+	       "function(object:int)", 0);
+
+  add_function("log_size", f_aap_log_size, "function(void:int)", 0);
+  add_function("logp", f_aap_log_exists, "function(void:int)", 0);
+
+#if 0
+  add_function( "add_filesystem", f_aap_add_filesystem, 
+                "function(string,string,array(string),int|void:void)", 0);
+  add_function( "add_contenttype", f_aap_add_contenttype, 
+                "function(strign,string:void)", 0);
+#ifdef FS_STATS
+  add_function( "filesystem_stats", f_filesystem_stats,
+                "function(void:mapping)", 0);
+#endif
+#endif
+
+  add_program_constant("Loop", accept_loop_program = end_program(), 0);
+
+  start_new_program();
+#define OFFSET(X) (offset + OFFSETOF(log_object,X))
+#ifdef ADD_STORAGE
+  offset=ADD_STORAGE(struct log_object);
+#else
+  offset=add_storage(sizeof(struct log_object));
+#endif
+  map_variable("time", "int", 0, OFFSET(time), T_INT);
+  map_variable("sent_bytes", "int", 0, OFFSET(sent_bytes), T_INT);
+  map_variable("reply", "int", 0, OFFSET(reply), T_INT);
+  map_variable("received_bytes", "int", 0, OFFSET(received_bytes), T_INT);
+  map_variable("raw", "string", 0, OFFSET(raw), T_STRING);
+  map_variable("url", "string", 0, OFFSET(url), T_STRING);
+  map_variable("method", "string", 0, OFFSET(method), T_STRING);
+  map_variable("protocol", "string", 0, OFFSET(protocol), T_STRING);
+  map_variable("from", "string", 0, OFFSET(from), T_STRING);
+  add_program_constant("logentry", (aap_log_object_program=end_program()), 0);
+
+
+  start_new_program();
+#ifdef ADD_STORAGE
+  ADD_STORAGE(struct c_request_object);
+#else
+  add_storage(sizeof(struct c_request_object));
+#endif
+
+  add_function("`[]", f_aap_index_op, "function(string:mixed)",0);
+  add_function("`->", f_aap_index_op, "function(string:mixed)",0);
+
+/* add_function("`->=", f_index_equal_op, "function(string,mixed:mixed)",0); */
+/* add_function("`[]=", f_index_equal_op, "function(string,mixed:mixed)",0); */
+
+  add_function("scan_for_query", f_aap_scan_for_query, 
+	       "function(string:string)", OPT_TRY_OPTIMIZE);
+
+  add_function("end", f_aap_end, "function(string|void,int|void:void)", 0);
+  add_function("send", f_aap_output, "function(string:void)", 0);
+  add_function("reply", f_aap_reply,
+	       "function(string|void,object|void,int|void:void)", 0);
+  add_function("__init", f_aap_reqo__init, "function(void:void)", 0);
+  add_function("reply_with_cache", f_aap_reply_with_cache, 
+	       "function(string,int:void)", 0);
+  set_init_callback( aap_init_request_object );
+  set_exit_callback( aap_exit_request_object );
+  add_program_constant("prog", (c_request_program = end_program()), 0);
+#endif /* _REENTRANT */
+}
+
+void pike_module_exit() 
+{
+#ifdef _REENTRANT
+  struct log *log = aap_first_log;
+  /* This is very dangerous, since the 
+   * accept threads are still going strong.
+   */
+
+  /* Tell the handle_timeouts thread to go and die.
+   * It will be dead in ~1s.
+   * We wait at the end of this function to ensure that this time
+   * has passed.
+   */
+#ifdef HAVE_TIMEOUTS
+  aap_exit_timeouts();
+#endif
+
+  /* Lock all the global mutexes. This will freeze the accept threads
+   * sooner or later. At least no more requests will be done to the
+   * pike level.
+   */
+  mt_lock( &queue_mutex );
+#ifdef HAVE_TIMEOUTS
+  mt_lock( &aap_timeout_mutex );
+#endif
+  /* Now, in theory, if all threads are stopped, we can free all the data.
+   * BUT: There is no way to know _when_ all threads are stopped, they may
+   * be in read() or something similar. Also, the locking of aap_timeout_mutex
+   * above disables the timeout, so they might linger in read() indefinately.
+   * Thus, we cannot free _all_ structures, only the ones that are always
+   * protected by mutexes. Most notably, we must leave the main 'log' and 
+   * 'cache' structures alive. We can, on the other hand, free all data _in_
+   * the cache and log structures.
+   */
+
+  while(log)
+  {
+    mt_lock( &log->log_lock ); /* This lock is _not_ unlocked again.. */
+    {
+      struct log_entry *log_head = log->log_head;
+      struct log *next = log->next;
+      while(log_head)
+      {
+	struct log_entry *l = log_head->next;
+	free(log_head);
+	log_head = l;
+      }
+      log->next = NULL;
+      log->log_head = log->log_tail = NULL;
+      log = next;
+    }
+  }
+
+  while(first_cache)
+  {
+    int i;
+    struct cache_entry *e, *t;
+    struct cache *next;
+    mt_lock(&first_cache->mutex);  /* This lock is _not_ unlocked again.. */
+    next = first_cache->next;
+    for(i=0; i<CACHE_HTABLE_SIZE; i++)
+    {
+      e = first_cache->htable[i];
+      while(e)
+      {
+	t = e;
+	e = e->next;
+	t->next = 0;
+	free_string(t->data);
+	free(t->url);
+	free(t->host);
+	free(t);
+      }
+      first_cache->htable[i]=0;
+    }
+    first_cache->next = NULL;
+    first_cache = next;
+  }
+
+  /* This will free all the string constants. It might be dangerous, 
+   * but should not be so. No thread should enter the pike level code 
+   * again, since all mutex locks are now locked.
+   */
+#define STRING(X,Y) free_string(X)
+#include "static_strings.h"
+#undef STRING
+
+#endif /* _REENTRANT */
+}
+
diff --git a/src/modules/HTTPLoop/accept_and_parse.h b/src/modules/HTTPLoop/accept_and_parse.h
new file mode 100644
index 0000000000..075d4c01eb
--- /dev/null
+++ b/src/modules/HTTPLoop/accept_and_parse.h
@@ -0,0 +1,158 @@
+#define CACHE_HTABLE_SIZE 40951
+
+struct res
+{
+  struct pike_string *protocol;
+
+  int header_start;
+  int method_len;
+  int body_start;
+
+  char *url;
+  int url_len;
+
+  char *host;
+  int host_len;
+
+  char   *content;
+  int content_len;
+  
+  char *leftovers;
+  int leftovers_len;
+
+  char *data;
+  int data_len;
+};
+
+struct cache_entry
+{
+  struct cache_entry *next;
+  struct pike_string *data;
+  time_t stale_at;
+  char *url;  int url_len;
+  char *host; int host_len;
+  short dead;
+  short refs;
+};
+
+struct file_ret
+{
+  int fd;
+  off_t size;
+  time_t mtime;
+};
+
+struct pstring 
+{
+  int len;
+  char *str;
+};
+
+
+#define FS_STATS
+struct filesystem
+{
+  struct pstring base;
+
+#ifdef FS_STATS /* These can be wrong, but should give an indication.. */
+  int lookups;
+  int hits;
+  int notfile;
+  int noperm;
+#endif
+};
+
+struct cache
+{
+  MUTEX_T mutex;
+  struct cache *next;
+  struct cache_entry *htable[CACHE_HTABLE_SIZE];
+  int size, entries, max_size;
+  int hits, misses, stale;
+  int num_requests, sent_data, received_data;
+  short unclean;
+  short gone;
+};
+
+struct args 
+{
+  int fd;
+  struct args *next;
+  struct res res;
+  int timeout;
+
+  struct svalue cb;
+  struct svalue args;
+  struct sockaddr_in from;
+  struct cache *cache;
+  struct filesystem *filesystem;
+  struct log *log;
+};
+
+struct log_entry 
+{
+  struct log_entry *next;
+  int t;
+  int sent_bytes;
+  int reply;
+  int received_bytes;
+  struct pstring raw;
+  struct pstring url;
+  struct sockaddr_in from;
+  struct pstring method;
+  struct pike_string *protocol;
+};
+
+struct log 
+{
+  struct log *next;
+  struct log_entry *log_head;
+  struct log_entry *log_tail;
+  MUTEX_T log_lock;
+};
+
+
+struct log_object
+{
+  int time;
+  int reply;
+  int sent_bytes;
+  int received_bytes;
+  struct pike_string *raw;
+  struct pike_string *url;
+  struct pike_string *method;
+  struct pike_string *protocol;
+  struct pike_string *from;
+};
+
+
+struct c_request_object
+{
+  struct args *request;
+  struct mapping *done_headers;
+  struct mapping *misc_variables;
+  int headers_parsed;
+};
+
+#define MY_MIN(a,b) ((a)<(b)?(a):(b))
+
+#define LOG(X,Y,Z) do { \
+    if((Y)->cache) {\
+      (Y)->cache->num_requests++;\
+      (Y)->cache->sent_data+=(X);\
+      (Y)->cache->received_data+=(Y)->res.data_len;\
+    }\
+    if ((Y)->log) { \
+      aap_log_append((X),(Y),(Z)); \
+    }\
+  } while(0)
+
+#define WRITE(X,Y,Z) aap_swrite(X,Y,Z)
+#undef THIS
+#define THIS ((struct c_request_object *)fp->current_storage)
+#define LTHIS ((struct args *)fp->current_storage)
+
+
+
+void aap_handle_connection(struct args *arg);
+
diff --git a/src/modules/HTTPLoop/acconfig.h b/src/modules/HTTPLoop/acconfig.h
new file mode 100644
index 0000000000..9850f83432
--- /dev/null
+++ b/src/modules/HTTPLoop/acconfig.h
@@ -0,0 +1,9 @@
+/*
+ * $Id: acconfig.h,v 1.1 1999/11/14 00:43:14 per Exp $
+ */
+#undef CAN_HAVE_SENDFILE
+#undef CAN_HAVE_LINUX_SYSCALL4
+#undef CAN_HAVE_NONSHARED_LINUX_SYSCALL4
+
+/* Define if your sendfile() takes 7 args (FreeBSD style) */
+#undef HAVE_FREEBSD_SENDFILE
diff --git a/src/modules/HTTPLoop/cache.c b/src/modules/HTTPLoop/cache.c
new file mode 100644
index 0000000000..042b76578f
--- /dev/null
+++ b/src/modules/HTTPLoop/cache.c
@@ -0,0 +1,215 @@
+#include "config.h"
+#include <global.h>
+#include <threads.h>
+#include <stralloc.h>
+#include <fdlib.h>
+
+#ifdef _REENTRANT
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "accept_and_parse.h"
+#include "cache.h"
+#include "util.h"
+
+struct cache *first_cache;
+
+static int cache_hash(char *s, int len)
+{
+  unsigned int res = len * 9471111;
+  while(len--) { res=res<<1 ^ ((res&(~0x7ffffff))>>31); res ^= s[len]; }
+  return (res % CACHE_HTABLE_SIZE)/2;
+} /*                              ^^ OBS! */
+
+static void really_free_cache_entry(struct cache  *c, struct cache_entry *e,
+				    struct cache_entry *prev, int b)
+{
+#ifdef DEBUG
+  extern int d_flag;
+  if(d_flag>2)
+  {
+    if(b!=(cache_hash(e->url, e->url_len) +
+	   cache_hash(e->host, e->host_len)))
+      fatal("Cache entry did not hash to the same spot\n");
+    if(!mt_trylock( & c->mutex ))
+      fatal("Cache free_entry running unlocked\n");
+    if(prev && prev->next != e)
+      fatal("prev->next != e\n");
+  }
+#endif
+  if(!prev)
+    c->htable[ b ] = e->next;
+  else
+    prev->next = e->next;
+
+  c->size -= e->data->len;
+  c->entries--;
+  free_string(e->data);
+  free(e->host);
+  free(e->url);
+  free(e);
+}
+
+
+
+
+void aap_free_cache_entry(struct cache *c, struct cache_entry *e,
+			  struct cache_entry *prev, int b)
+{
+#ifdef DEBUG
+  if(e->refs<=0)
+    fatal("Freeing free cache entry\n");
+#endif
+  if(!--e->refs) really_free_cache_entry(c,e,prev,b);
+}
+
+void simple_aap_free_cache_entry(struct cache *c, struct cache_entry *e)
+{
+  mt_lock( &c->mutex );
+  if(!--e->refs)
+  {
+    struct cache_entry *t, *p=0;
+    int hv = cache_hash(e->url, e->url_len)+cache_hash(e->host,e->host_len);
+    t = c->htable[ hv ];
+    while(t)
+    {
+      if( t == e ) 
+      {
+	really_free_cache_entry(c,t,p,hv);
+	break;
+      }
+      p=t;
+      t=t->next;
+    }
+  }  
+  mt_unlock( &c->mutex );
+}
+
+
+void aap_cache_insert(struct cache_entry *ce, struct cache *c)
+{
+  struct cache_entry *head, *p;
+  char *t;
+  int hv;
+#ifdef DEBUG
+  extern int d_flag;
+  if((d_flag > 2) && !mt_trylock( & c->mutex ))
+    fatal("Cache insert running unlocked\n");
+#endif
+  c->size += ce->data->len;
+  if((head = aap_cache_lookup(ce->url, ce->url_len, 
+			  ce->host, ce->host_len, c, 1, 
+			  &p, &hv)) && !head->dead)
+  {
+    c->size -= head->data->len;
+    if(head->data != ce->data)
+    {
+      free_string(head->data);
+      head->data = ce->data;
+    } else
+      free_string(head->data);
+    head->stale_at = ce->stale_at;
+    head->dead = 0;
+    aap_free_cache_entry( c, head, p, hv );
+    free(ce);
+  } else {
+    c->entries++;
+    t = malloc(ce->url_len);  MEMCPY(t,ce->url,ce->url_len);   ce->url = t;
+    t = malloc(ce->host_len+1); MEMCPY(t,ce->host,ce->host_len); ce->host = t;
+    ce->next = c->htable[hv];
+    ce->refs = 1;
+    c->htable[hv] = ce;
+  }
+}
+
+struct cache_entry *aap_cache_lookup(char *s, int len,char *ho, int hlen, 
+				     struct cache *c, int nolock,
+				     struct cache_entry **p, int *hv)
+{
+  int h = cache_hash(s, len) + cache_hash(ho,hlen);
+  struct cache_entry *e, *prev=NULL;
+
+  *hv = h;
+  if(!nolock) 
+    mt_lock(&c->mutex);
+#ifdef DEBUG
+  else 
+  {
+    extern int d_flag;
+    if((d_flag>2) && !mt_trylock( & c->mutex ))
+      fatal("Cache lookup running unlocked\n");
+  }
+#endif
+  *p = 0;
+  e = c->htable[h];
+  while(e)
+  {
+    if(!e->dead && e->url_len == len && e->host_len == hlen 
+       && !memcmp(e->url,s,len) && !memcmp(e->host,ho,hlen))
+    {
+      int t = aap_get_time();
+      if(e->stale_at < t)
+      {
+	c->unclean = 1;
+	/* We cannot free the pike-string from here. 
+	 * Let the clean-up code handle that instead. 
+	 */
+	e->dead = 1;
+	c->stale++;
+	if(!nolock) mt_unlock(&c->mutex);
+	return 0;
+      }
+      c->hits++;
+      /* cache hit. Lets add it to the top of the list */
+      if(c->htable[h] != e)
+      {
+	if(prev) prev->next = e->next;
+	e->next = c->htable[h];
+	c->htable[h] = e;
+      }
+      if(!nolock) mt_unlock(&c->mutex);
+      e->refs++;
+      return e;
+    }
+    *p = prev = e;
+    e = e->next;
+  }
+  c->misses++;
+  if(!nolock) mt_unlock(&c->mutex);
+  return 0;
+}
+
+
+void aap_clean_cache(struct cache *c, int nolock)
+{
+  int i;
+  struct cache_entry *e, *p, *prev = 0;
+  if(!nolock) mt_lock(&c->mutex);
+  for(i=0; i<CACHE_HTABLE_SIZE; i++)
+  {
+    e = c->htable[i];
+    prev=0;
+    while(e)
+    {
+      p = e->next;
+      if(e->dead) 
+	aap_free_cache_entry(c, e, prev, i);
+      else 
+	prev = e;
+      e = p;
+    }
+  }
+  if(!nolock) mt_unlock(&c->mutex);
+}
+#endif
diff --git a/src/modules/HTTPLoop/cache.h b/src/modules/HTTPLoop/cache.h
new file mode 100644
index 0000000000..cc034754a5
--- /dev/null
+++ b/src/modules/HTTPLoop/cache.h
@@ -0,0 +1,19 @@
+struct cache_entry *aap_cache_lookup(char *s, int len, 
+				 char *h, int hlen,
+				 struct cache *c, int nl, 
+				 struct cache_entry **p, 
+				 int *hv);
+
+
+void aap_free_cache_entry(struct cache *c, struct cache_entry *e,
+		      struct cache_entry *prev, int b);
+
+void simple_aap_free_cache_entry(struct cache *c, struct cache_entry *e);
+
+void aap_cache_insert(struct cache_entry *ce, struct cache *c);
+
+void aap_clean_cache(struct cache *ce, int nolock);
+
+extern struct cache *first_cache;
+
+
diff --git a/src/modules/HTTPLoop/configure.in b/src/modules/HTTPLoop/configure.in
new file mode 100644
index 0000000000..95b47f113e
--- /dev/null
+++ b/src/modules/HTTPLoop/configure.in
@@ -0,0 +1,143 @@
+# $Id: configure.in,v 1.1 1999/11/14 00:43:14 per Exp $
+AC_INIT(accept_and_parse.c)
+AC_CONFIG_HEADER(config.h)
+
+AC_SET_MAKE
+
+sinclude(../module_configure.in)
+
+AC_CHECK_FUNCS(poll gmtime_r gmtime sendfile)
+
+AC_CHECK_HEADERS(poll.h sys/poll.h sys/socket.h netinet/in.h arpa/inet.h \
+                 asm/unistd.h sys/uio.h)
+
+AC_SUBST(RANLIB)
+
+if test "$ac_cv_func_sendfile" = "yes"; then
+  AC_MSG_CHECKING(if sendfile takes 4(Linux) or 7(FreeBSD) arguments)
+  AC_CACHE_VAL(pike_cv_freebsd_sendfile, [
+    AC_TRY_COMPILE([
+#include <sys/types.h>                                 
+#include <sys/socket.h>                                
+#include <sys/uio.h>                                   
+    ], [
+      return sendfile(0,0,0,0,(void *)0,(void *)0,0);
+    ], [
+      # Probably FreeBSD-style, but we need to check that
+      # we indeed have a prototype...
+      AC_TRY_COMPILE([
+#include <sys/types.h>                                 
+#include <sys/socket.h>                                
+#include <sys/uio.h>                                   
+      ], [
+        /* This should fail on FreeBSD
+         * If it succeeds, we don't have a prototype,
+         * which would indicate Linux.
+         */
+        return sendfile(0,0,(void *)0,0);
+      ], [ pike_cv_freebsd_sendfile=no ], [ pike_cv_freebsd_sendfile=yes ])
+    ], [ pike_cv_freebsd_sendfile=no ])
+  ])
+  if test "$pike_cv_freebsd_sendfile" = "yes"; then
+    AC_MSG_RESULT([7 - FreeBSD style])
+    AC_DEFINE(HAVE_FREEBSD_SENDFILE)
+  else
+    AC_MSG_RESULT([4 - Linux style])
+  fi
+else
+  if test "${pike_cv_sys_os}" = "Linux"; then
+    if test "$pike_cv_sys_ccshared" != ""; then
+      AC_MSG_CHECKING(sendfile on linux without sendfile in header files with shared code)
+      AC_CACHE_VAL(pike_cv_sendfile_for_early_linux_with_new_gcc_shared, [
+	OLD_CFLAGS="$CFLAGS"
+	CFLAGS="$CFLAGS $pike_cv_sys_ccshared"
+	AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <asm/unistd.h>
+#ifndef __NR_sendfile
+# define __NR_sendfile 187
+#endif
+_syscall4(ssize_t,sendfile,int,out,int,in,off_t*,off,size_t,size);
+	], [], [pike_cv_sendfile_for_early_linux_with_new_gcc_shared=yes], 
+	     [pike_cv_sendfile_for_early_linux_with_new_gcc_shared=no])
+	CFLAGS="$OLD_CFLAGS"
+      ])
+
+      if test "$pike_cv_sendfile_for_early_linux_with_new_gcc_shared" = "yes" ; then
+	AC_DEFINE(CAN_HAVE_LINUX_SYSCALL4)
+	AC_MSG_RESULT(perhaps)
+      else
+	AC_MSG_RESULT(no)
+      fi
+    else
+      pike_cv_sendfile_for_early_linux_with_new_gcc_shared=no
+    fi
+
+    if test "$pike_cv_sendfile_for_early_linux_with_new_gcc_shared" = "no" ; then
+      AC_MSG_CHECKING(sendfile on linux without sendfile in header files with nonshared code)
+      AC_CACHE_VAL(pike_cv_sendfile_for_early_linux_with_new_gcc_nonshared, [
+	AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <asm/unistd.h>
+#ifndef __NR_sendfile
+# define __NR_sendfile 187
+#endif
+_syscall4(ssize_t,sendfile,int,out,int,in,off_t*,off,size_t,size);
+	], [], [pike_cv_sendfile_for_early_linux_with_new_gcc_nonshared=yes], 
+	     [pike_cv_sendfile_for_early_linux_with_new_gcc_nonshared=no])
+      ])
+
+      if test "$pike_cv_sendfile_for_early_linux_with_new_gcc_nonshared" = "yes" ; then
+	AC_DEFINE(CAN_HAVE_NONSHARED_LINUX_SYSCALL4)
+	AC_MSG_RESULT(perhaps)
+      else
+	AC_MSG_RESULT(no)
+      fi
+    fi
+
+    if test "$pike_cv_sendfile_for_early_linux_with_new_gcc_shared" = "no" -o \
+            "$pike_cv_sendfile_for_early_linux_with_new_gcc_nonshared" = "no"; then
+      AC_MSG_CHECKING(sendfile on linux without sendfile in header files with new gcc)
+  
+      AC_CACHE_VAL(pike_cv_sendfile_for_early_linux, [
+        AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/errno.h>
+
+int sendfile(int out, int in, int *off, int size)
+{
+  int retval;
+  asm volatile("pushl %%ebx\n\t"
+               "movl %%edi,%%ebx\n\t"
+               "int $0x80\n\t"
+               "popl %%ebx"
+               :"=a" (retval)
+               :"0" (187),
+               "D" (out), /* pseudo-ebx */
+               "c" (in),
+               "d" (off),
+               "S" (size)
+               );
+  if ((unsigned long) retval > (unsigned long)-1000) 
+  {
+    errno = -retval;
+    retval = -1;
+  }
+  return retval;
+}
+        ],[],[pike_cv_sendfile_for_early_linux=yes],
+          [pike_cv_sendfile_for_early_linux=no])])
+
+      if test "$pike_cv_sendfile_for_early_linux" = "yes" ; then
+	AC_DEFINE(CAN_HAVE_SENDFILE)
+	AC_MSG_RESULT(perhaps)
+      else
+	AC_MSG_RESULT(no)
+      fi
+    fi
+  fi
+fi
+
+AC_OUTPUT(Makefile,echo FOO >stamp-h )
diff --git a/src/modules/HTTPLoop/extensions b/src/modules/HTTPLoop/extensions
new file mode 100644
index 0000000000..6b56a70e29
--- /dev/null
+++ b/src/modules/HTTPLoop/extensions
@@ -0,0 +1,219 @@
+# $Id: extensions,v 1.1 1999/11/14 00:43:14 per Exp $
+# name="Extension database";
+# doc="The database used to map from extension to mime-type (example: *.gif to image/gif)";
+#
+
+aif	audio/x-aiff
+aifc	audio/x-aiff
+aiff	audio/x-aiff
+au	audio/basic
+m3u 	audio/x-mpegurl
+mid	audio/midi
+midi    audio/midi 
+mod	audio/x-mod
+mp2	audio/x-mpeg
+mp3 	audio/x-mpeg
+mp3url	audio/x-mpegurl
+mpa	audio/x-mpeg
+psid	audio/x-psid
+# This is the standard extension used by the RealVideo 5.0 encoder
+rm	audio/x-pn-realaudio
+ra	audio/x-pn-realaudio
+ram	audio/x-pn-realaudio
+rpm 	audio/x-pn-realaudio-plugin
+sid	audio/x-psid
+snd	audio/basic
+vox     audio/voxware 
+wav	audio/x-wav
+
+fif     image/fif 
+gif	image/gif
+ief	image/ief
+iff	image/ilbm
+ilbm	image/ilbm
+jpe	image/jpeg
+jpeg	image/jpeg
+jpg	image/jpeg
+pbm	image/x-portable-bitmap
+pgm	image/x-portable-graymap
+pict  	image/pict
+pjpg    image/jpeg
+png	image/png
+pnm	image/x-portable-anymap
+ppm	image/x-portable-pixmap
+ras	image/x-cmu-raster
+rgb	image/x-rgb
+rs	image/x-sun-raster
+tga	image/x-targa
+tif	image/tiff
+tiff	image/tiff
+xbm	image/x-xbitmap
+xpm	image/x-xpixmap
+xwd	image/x-xwindowdump
+
+# AutoCad (AutoDesk)
+dwf	drawing/x-dwf
+
+anim3	video/x-anim
+anim5	video/x-anim
+anim7	video/x-anim
+anim8	video/x-anim
+avi	video/x-msvideo
+fli	video/x-fli
+mov	video/quicktime
+movie	video/x-sgi-movie
+mpe	video/mpeg
+mpeg	video/mpeg
+mpg	video/mpeg
+qt	video/quicktime
+viv     video/vnd.vivo
+vivo    video/vnd.vivo
+vos	video/vosaic
+
+c	text/x-c-code
+cc	text/x-c++-code
+css	text/css
+etx	text/x-setext
+h	text/x-include-file
+htm	text/html
+html	text/html
+java	text/plain
+rtx	text/richtext
+rxml	text/roxencode
+sgm     text/x-sgml
+sgml    text/x-sgml
+shtml	text/html
+spider  text/spidercode 
+spml	text/spinnercode
+spml    text/spidercode 
+tsv     text/tab-separated-values
+txt	text/plain
+
+aab 	application/x-authorware-bin
+aam 	application/x-authorware-map
+aas 	application/x-authorware-seg
+ai	application/postscript
+arj	application/octet-stream
+asn     application/astound
+asp     application/x-asap 
+bcpio	application/x-bcpio
+bin	application/octet-stream
+cert	application/x-x509-ca-cert
+cer	application/x-x509-ca-cert
+cdf	application/x-netcdf
+cdr	application/x-coreldraw
+cdrw    application/x-coreldraw
+chat	application/x-chat
+class	application/x-java-vm
+com	application/octet-stream
+cpio	application/x-cpio
+csh	application/x-csh
+dll	application/octet-stream
+dnt     application/donut
+# Too general, but also too common to not be defined.
+doc	application/msword
+donut   application/donut
+drv	application/octet-stream
+dvi	application/x-dvi
+eps	application/postscript
+evy     application/x-envoy 
+exe	application/octet-stream
+gtar	application/x-gtar
+hdf	application/x-hdf
+hqx	application/mac-binhex40
+iw      application/iconauthor 
+iwm     application/iconauthor 
+jar	application/x-java-archive
+js	application/x-javascript
+latex	application/x-latex
+lha	application/lha
+lzh	application/lha
+man	application/x-troff-man
+map	application/x-imagemap
+me	application/x-troff-me
+mif	application/x-mif
+mm      application/x-meme 
+ms	application/x-troff-ms
+msw     application/binary 
+mw	application/math
+nc	application/x-netcdf
+oda	application/oda
+pdf	application/pdf
+pfr	application/font-tdpfr
+pl	application/x-perl
+# Oracle plug-ins
+po	application/x-form
+pot	application/mspowerpoint
+ppt	application/mspowerpoint
+ppz	application/mspowerpoint
+pps	application/mspowerpoint
+ps	application/postscript
+quake	application/x-qplug-plugin
+roff	application/x-troff
+rtf	application/rtf
+sh	application/x-sh
+shar	application/x-shar
+sit	application/x-stuffit
+so	application/octet-stream
+spl     application/futuresplash 
+spr     application/x-sprite 
+sprite  application/x-sprite 
+src	application/x-wais-source
+sv4cpic application/x-sv4cpio
+sv4crc	application/x-sv4crc
+swf	application/x-shockwave-flash
+t	application/x-troff
+tar	application/unix-tar
+tcl	application/x-tcl
+tex	application/x-tex
+texi	application/x-texinfo
+texinfo application/x-texinfo
+tgz	application/unix-tar	x-gzip
+tr	application/x-troff
+tsp	application/dsptype            
+user	application/x-x509-user-cert
+usr	application/x-x509-user-cert
+ustar	application/x-ustar
+vcd	application/x-cdlink
+vmd 	application/vocaltec-media-desc
+vmf 	application/vocaltec-media-file
+wpc     application/wpc 
+xls	application/vnd.ms-excel
+zip	application/zip
+
+# This one hasn't been registred with IANA yet, but is added anyway.
+# LivePicture PhotoVista RealSpace
+ivr	i-world/i-vrml
+
+wrl	x-world/x-vrml
+wrml    x-world/x-vrml
+
+talk    plugin/talker 
+
+# Diskmasher -- Amiga thingie
+dms	application/x-diskmasher
+
+# Windows Write
+wri     application/x-write
+
+ice     x-conference/x-cooltalk
+
+# MacroMedia Director (http://www.macromedia.com/)
+dcr	application/x-director
+dir	application/x-director
+dxr	application/x-director
+
+# Netscape proxy config files.
+nspc    application/x-ns-proxy-autoconfig
+pac     application/x-ns-proxy-autoconfig
+
+# Certificate Authority Certificate (recognized at least by Netscape)
+ca_cert application/x-x509-ca-cert
+
+# This is supposed to be just 'gzip' and 'compress', but Netscape
+# barfs.. Fixed in Netscape 3.*, but quite a lot of people still run 2.*
+Z	STRIP			x-compress
+gz	STRIP			x-gzip
+
+uu	STRIP			uuencode
+b64	STRIP			base64
diff --git a/src/modules/HTTPLoop/filesystem.c b/src/modules/HTTPLoop/filesystem.c
new file mode 100644
index 0000000000..66d84772fb
--- /dev/null
+++ b/src/modules/HTTPLoop/filesystem.c
@@ -0,0 +1,81 @@
+#include "config.h"
+#include <global.h>
+#include <threads.h>
+#include <stralloc.h>
+#include <fdlib.h>
+
+#ifdef _REENTRANT
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "accept_and_parse.h"
+#include "filesystem.h"
+#include "cache.h"
+#include "util.h"
+
+
+struct file_ret *aap_find_file( char *s, int len, 
+                                char *ho, int hlen, 
+                                struct filesystem *f )
+{
+  struct stat s;
+  char *fname = alloca( fs->base.len + len + hlen + 2);
+  int res;
+
+
+#ifdef FS_STATS
+  fs->lookups++;
+#endif
+  MEMCPY( fname, fs->base.str, fs->base.len );
+  MEMCPY( fname+fs->base.len, ho, hlen );
+  MEMCPY( fname+fs->base.len+hlen, "/", 1 );
+  MEMCPY( fname+fs->base.len+hlen+1, s, len );
+
+  res = stat( fname, &s );
+    
+  if(!res) /* ok. The file exists. */
+  {
+    if(S_ISREG( s.st_mode )) /* and it is a file... */
+    {
+      int fd;
+      fd = open( fname, O_RDONLY, 0 );
+      if(fd != -1)            /* and it can be opened... */
+      {
+        struct file_ret *r = malloc(sizeof(struct file_ret));
+        r->fd = fd;
+        r->size = s.st_size;
+        r->mtime = s.st_mtime;
+#ifdef FS_STATS
+        fs->hits++;
+#endif
+        return r;
+      }
+#ifdef FS_STATS
+      else
+        fs->noperm++;
+#endif
+    }
+#ifdef FS_STATS
+    else
+      fs->notfile++;
+#endif
+  }
+#ifdef FS_STATS
+  else
+    fs->nofile++;
+#endif
+  return 0;
+}
+                    
+#endif
diff --git a/src/modules/HTTPLoop/filesystem.h b/src/modules/HTTPLoop/filesystem.h
new file mode 100644
index 0000000000..24ebc7a352
--- /dev/null
+++ b/src/modules/HTTPLoop/filesystem.h
@@ -0,0 +1,7 @@
+struct file_ret *aap_find_file( char *s, int len, 
+                                char *ho, int hlen, 
+                                struct filesystem *f );
+
+void set_filesystem( struct args *a, struct pstring loc )
+{
+}
diff --git a/src/modules/HTTPLoop/log.c b/src/modules/HTTPLoop/log.c
new file mode 100644
index 0000000000..2afeb4269c
--- /dev/null
+++ b/src/modules/HTTPLoop/log.c
@@ -0,0 +1,237 @@
+#include "config.h"
+#include <global.h>
+	  
+#include <array.h>
+#include <backend.h>
+#include <machine.h>
+#include <mapping.h>
+#include <module_support.h>
+#include <multiset.h>
+#include <object.h>
+#include <operators.h>
+#include <pike_memory.h>
+#include <program.h>
+#include <stralloc.h>
+#include <svalue.h>
+#include <threads.h>
+#include <fdlib.h>
+
+#ifdef _REENTRANT
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "accept_and_parse.h"
+#include "log.h"
+#include "requestobject.h"
+#include "util.h"
+
+struct program *aap_log_object_program;
+struct log *aap_first_log;
+
+static void push_log_entry(struct log_entry *le)
+{
+  struct object *o = clone_object( aap_log_object_program, 0 );
+  struct log_object *lo = (struct log_object*)o->storage;
+  lo->time = le->t;
+  lo->sent_bytes = le->sent_bytes;
+  lo->reply = le->reply;
+  lo->received_bytes = le->received_bytes;
+  lo->raw = make_shared_binary_string(le->raw.str, le->raw.len);
+  lo->url = make_shared_binary_string(le->url.str, le->url.len);
+  lo->method = make_shared_binary_string(le->method.str, le->method.len);
+  lo->protocol = le->protocol;
+  le->protocol->refs++;
+  lo->from = make_shared_string( inet_ntoa(le->from.sin_addr) );
+  push_object( o );
+}
+
+void f_aap_log_as_array(INT32 args)
+{
+  struct log_entry *le;
+  struct log *l = LTHIS->log;
+  int n = 0;
+  pop_n_elems(args);
+  
+  mt_lock( &l->log_lock );
+  le = l->log_head;
+  l->log_head = l->log_tail = 0;
+  mt_unlock( &l->log_lock );
+  
+  while(le)
+  {
+    struct log_entry *l;
+    n++;
+    push_log_entry(le);
+    l = le->next;
+    free(le);
+    le = l;
+  }
+  {
+    extern void f_aggregate(INT32 args);
+    f_aggregate(n);
+  }
+}
+
+void f_aap_log_exists(INT32 args)
+{
+  if(LTHIS->log->log_head) 
+    push_int(1);
+  else 
+    push_int(0);
+}
+
+void f_aap_log_size(INT32 args)
+{
+  int n=1;
+  struct log *l = LTHIS->log;
+  struct log_entry *le;
+  if(!l) {
+    push_int(0);
+    return;
+  }
+  mt_lock( &l->log_lock );
+  le = l->log_head; 
+  while((le = le->next))
+    n++;
+  mt_unlock( &l->log_lock );
+  push_int(n);
+}
+
+void f_aap_log_as_commonlog_to_file(INT32 args)
+{
+  struct log_entry *le;
+  struct log *l = LTHIS->log;
+  int n = 0;
+  int mfd, ot=0;
+  struct object *f;
+  struct tm tm;
+  FILE *foo;
+  static char *month[] = {
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Oct", "Sep", "Nov", "Dec",
+  };
+
+  get_all_args("log_as_commonlog_to_file", args, "%o", &f);
+  f->refs++;
+
+  pop_n_elems(args);
+  apply(f, "query_fd", 0);
+  mfd = dup(sp[-1].u.integer);
+  if(mfd < 1)error("Bad fileobject to ->log_as_commonlog_to_file\n");
+  pop_stack();
+
+  foo = fdopen( mfd, "w" );
+  if(!foo)
+    error("Bad fileobject to ->log_as_commonlog_to_file\n");
+
+  THREADS_ALLOW();
+
+  mt_lock( &l->log_lock );
+  le = l->log_head; 
+  l->log_head = l->log_tail = 0;
+  mt_unlock( &l->log_lock );
+
+  while(le)
+  {
+    int i;
+    struct log_entry *l = le->next;
+    /* remotehost rfc931 authuser [date] "request" status bytes */
+    if(le->t != ot)
+    {
+      time_t t = (time_t)le->t;
+#ifdef HAVE_GMTIME_R
+      gmtime_r( &t, &tm );
+#else
+#ifdef HAVE_GMTIME
+      tm = *gmtime( &t ); /* This will break if two threads run
+			    gmtime() at once. */
+#else
+#ifdef HAVE_LOCALTIME
+      tm = *localtime( &t ); /* This will break if two threads run
+			       localtime() at once. */
+#endif
+#endif
+#endif
+      ot = le->t;
+    }
+
+    /* date format:  [03/Feb/1998:23:08:20 +0000]  */
+
+    /* GET [URL] HTTP/1.0 */
+    for(i=13; i<le->raw.len; i++)
+      if(le->raw.str[i] == '\r')
+      {
+	le->raw.str[i] = 0;
+	break;
+      }
+
+    fprintf(foo, 
+    "%d.%d.%d.%d - %s [%02d/%s/%d:%02d:%02d:%02d +0000] \"%s\" %d %d\n",
+	    ((unsigned char *)&le->from.sin_addr)[ 0 ],
+	    ((unsigned char *)&le->from.sin_addr)[ 1 ],
+	    ((unsigned char *)&le->from.sin_addr)[ 2 ],
+	    ((unsigned char *)&le->from.sin_addr)[ 3 ], /* hostname */
+	    "-",                          /* remote-user */
+	    tm.tm_mday, month[tm.tm_mon], tm.tm_year+1900,
+	    tm.tm_hour, tm.tm_min, tm.tm_sec, /* date */
+	    le->raw.str, /* request line */
+	    le->reply, /* reply code */
+	    le->sent_bytes); /* bytes transfered */
+    free(le);
+    n++;
+    le = l;
+  }
+  fclose(foo);
+  close(mfd);
+  THREADS_DISALLOW();
+  push_int(n);
+}
+
+void aap_log_append(int sent, struct args *arg, int reply)
+{
+  struct log *l = arg->log;
+  /* we do not incude the body, only the headers et al.. */
+  struct log_entry *le=malloc(sizeof(struct log_entry)+arg->res.body_start-3);
+  char *data_to=((char *)le)+sizeof(struct log_entry);
+  
+  le->t = aap_get_time();
+  le->sent_bytes = sent;
+  le->reply = reply;
+  le->received_bytes = arg->res.body_start + arg->res.content_len;
+  MEMCPY(data_to, arg->res.data, arg->res.body_start-4);
+  le->raw.str = data_to;
+  le->raw.len = arg->res.body_start-4;
+  le->url.str = (data_to + (int)(arg->res.url-arg->res.data));
+  le->url.len = arg->res.url_len;
+  le->from = arg->from;
+  le->method.str = data_to;
+  le->method.len = arg->res.method_len;
+  le->protocol = arg->res.protocol;
+  le->next = 0;
+
+  mt_lock( &l->log_lock );
+  if(l->log_head)
+  {
+    l->log_tail->next = le;
+    l->log_tail = le;
+  }
+  else
+  {
+    l->log_head = le;
+    l->log_tail = le;
+  }
+  mt_unlock( &l->log_lock );
+}
+
+#endif
diff --git a/src/modules/HTTPLoop/log.h b/src/modules/HTTPLoop/log.h
new file mode 100644
index 0000000000..c9e7435b25
--- /dev/null
+++ b/src/modules/HTTPLoop/log.h
@@ -0,0 +1,9 @@
+void f_aap_log_as_array(INT32 args);
+void f_aap_log_exists(INT32 args);
+void f_aap_log_size(INT32 args);
+void f_aap_log_as_commonlog_to_file(INT32 args);
+void aap_log_append(int sent, struct args *arg, int reply);
+
+
+extern struct log *aap_first_log;
+extern struct program *aap_log_object_program;
diff --git a/src/modules/HTTPLoop/requestobject.c b/src/modules/HTTPLoop/requestobject.c
new file mode 100644
index 0000000000..1b8525ae1e
--- /dev/null
+++ b/src/modules/HTTPLoop/requestobject.c
@@ -0,0 +1,1092 @@
+/*
+ * $Id: requestobject.c,v 1.1 1999/11/14 00:43:15 per Exp $
+ */
+
+#include "global.h"
+#include "config.h"
+	  
+#include "array.h"
+#include "backend.h"
+#include "machine.h"
+#include "mapping.h"
+#include "module_support.h"
+#include "multiset.h"
+#include "object.h"
+#include "operators.h"
+#include "pike_memory.h"
+#include "program.h"
+#include "constants.h"
+#include "stralloc.h"
+#include "svalue.h"
+#include "threads.h"
+#include "fdlib.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef _REENTRANT
+#include "accept_and_parse.h"
+#include "log.h"
+#include "util.h"
+#include "cache.h"
+#include "requestobject.h"
+
+extern void f_aggregate(INT32 args); /* no prototype? */
+
+/* Used when fatal() can't be. */
+#define DWERROR(X)	write(2, X, sizeof(X) - sizeof(""))
+
+#if defined(CAN_HAVE_LINUX_SYSCALL4) || \
+    !defined(DYNAMIC_MODULE) && defined(CAN_HAVE_NONSHARED_LINUX_SYSCALL4)
+
+#include <asm/unistd.h>
+#ifndef __NR_sendfile
+#define __NR_sendfile 187
+#endif
+_syscall4(ssize_t,sendfile,int,out,int,in,off_t*,off,size_t,size);
+
+#elif defined(CAN_HAVE_SENDFILE)
+
+ssize_t sendfile(int out, int in, off_t *off, size_t size)
+{
+  ssize_t retval;
+  asm volatile(
+               "pushl %%ebx\n\t"
+               "movl %%edi,%%ebx\n\t"
+               "int $0x80\n\t"
+               "popl %%ebx"
+               :"=a" (retval)
+               :"0" (187),
+               "D" (out), /* pseudo-ebx (put it in edi)*/
+               "c" (in),
+               "d" (off),
+               "S" (size)
+               );
+  if ((unsigned long) retval > (unsigned long)-1000) 
+  {
+    errno = -retval;
+    retval = -1;
+  }
+  return retval;
+}
+# define HAVE_SENDFILE
+#endif
+
+
+/* Define local global string variables ... */
+#define STRING(X,Y) /*extern*/ struct pike_string *X
+#include "static_strings.h"
+#undef STRING
+/* there.. */
+
+#define SINSERT(MAP,INDEX,VAL) \
+do { 						\
+  push_string(VAL);				\
+  push_string(INDEX);                           \
+  mapping_insert((MAP),(sp-1),(sp-2));		\
+  sp-=2;                                        \
+} while(0)
+
+#define IINSERT(MAP,INDEX,VAL)\
+do { 						\
+  push_int(VAL);				\
+  push_string(INDEX);                           \
+  mapping_insert((MAP),sp-1,sp-2);		\
+  sp -= 2;                                      \
+} while(0)
+
+#define TINSERT(MAP,INDEX,VAL,LEN)\
+do { 						   \
+  push_string(make_shared_binary_string((VAL),(LEN))); \
+  push_string(INDEX);                           \
+  mapping_insert((MAP),sp-1,sp-2);		\
+  sp--;                                          \
+  pop_stack();					   \
+} while(0)
+
+#define AINSERT(MAP,INDEX,VAL)\
+do { 						\
+  push_array(VAL);				\
+  push_string(INDEX);                           \
+  mapping_insert((MAP),sp-1,sp-2);		\
+  sp -= 2;                                      \
+} while(0)
+
+#define OINSERT(MAP,INDEX,VAL)\
+do { 						\
+  push_object(VAL);				\
+  push_string(INDEX);                           \
+  mapping_insert((MAP),sp-1,sp-2);		\
+  sp -= 2;                                      \
+} while(0)
+
+#define MSINSERT(MAP,INDEX,VAL)\
+do { 						\
+  push_multiset(val);				\
+  push_string(INDEX);                           \
+  mapping_insert((MAP),sp-1,sp-2);		\
+  sp -= 2;                                      \
+} while(0)
+
+#define MAINSERT(MAP,INDEX,VAL)\
+do { 						\
+  push_mapping(VAL);				\
+  push_string(INDEX);                           \
+  mapping_insert((MAP),sp-1,sp-2);		\
+  sp -= 2;                                      \
+} while(0)
+
+
+static int dhex(char what)
+{
+  if(what >= '0' && what <= '9') return what - '0';
+  if(what >= 'A' && what <= 'F')  return 10+what-'A';
+  if(what >= 'a' && what <= 'f')  return 10+what-'a';
+  return 0;
+}
+
+
+void f_aap_scan_for_query(INT32 args)
+{
+  struct pike_string *_s;
+  char *s, *work_area;
+  int len, i,j, begin=0, c;
+  if(args)
+  {
+    get_all_args("HTTP C object->scan_for_query(string f)", args, "%S", &_s);
+    s = (char *)_s->str;
+    len = _s->len;
+  }
+  else
+  {
+    s = THIS->request->res.url;
+    len = THIS->request->res.url_len;
+  }
+
+  /*  [http://host:port]/(pre,states)/[url[?query]] */
+  work_area = malloc(len);
+  
+  /* find '?' if any */
+  for(j=i=0;i<len;i++)
+  {
+    switch(c=s[i])
+    {
+     case '?':
+       goto done;
+     case '%':
+       if(i<len-2)
+       {
+	 c = (dhex(s[i+1])<<4) + dhex(s[i+2]);
+	 i+=2;
+       }
+    }
+    work_area[j++]=c;
+  }
+
+ done:
+
+/* Now find prestates, if any */
+  j--;
+  if(j > 3 && work_area[1] == '(' && work_area[0]=='/')
+  {
+    int k, n=0, last=2;
+    for(k = 2; k < j; k++)
+    {
+      switch(work_area[k])
+      {
+       case ',':
+	 push_string(make_shared_binary_string(work_area+last,k-last));
+	 n++;
+	 last = k+1;
+	 break;
+       case ')':
+	 push_string(make_shared_binary_string(work_area+last,k-last));
+	 n++;
+	 begin = k+1;
+	 f_aggregate_multiset( n );
+	 goto done2;
+      }
+    }
+    pop_n_elems(n);
+    f_aggregate_multiset( 0 );
+  } else {
+    f_aggregate_multiset( 0 );
+  }
+ done2:
+  push_string(s_prestate);
+  mapping_insert(THIS->misc_variables, sp-1, sp-2);
+  sp--; pop_stack();
+
+  TINSERT(THIS->misc_variables, s_not_query, work_area+begin, j-begin+1);
+  free(work_area);
+  
+  if(i < len)
+    TINSERT(THIS->misc_variables, s_query, s+i+1, (len-i)-1);
+  else
+    IINSERT(THIS->misc_variables, s_query, 0);
+
+  push_string(s_variables);  map_delete(THIS->misc_variables, sp-1); sp--;
+  push_string(s_rest_query); map_delete(THIS->misc_variables, sp-1); sp--;
+}
+
+#define VAR_MAP_INSERT() 	 \
+         struct svalue *s;						 \
+	 push_string(make_shared_binary_string( dec+lamp, leq-lamp));	 \
+	 if((s = low_mapping_lookup(v, sp-1)))				 \
+	 {								 \
+	   dec[leq] = 0;						 \
+	   s->u.string->refs++;						 \
+	   map_delete(v, sp-1);						 \
+	   push_string(s->u.string);					 \
+	   push_string(make_shared_binary_string( dec+leq, dl-leq));	 \
+	   f_add(2);							 \
+	 } else 							 \
+	   push_string(make_shared_binary_string( dec+leq+1, dl-leq-1)); \
+	 mapping_insert(v, sp-2, sp-1);					 \
+	 pop_n_elems(2)
+
+
+static void decode_x_url_mixed(char *in, int l, struct mapping *v, char *dec, 
+			       char *rest_query, char **rp)
+{
+  int pos = 0, lamp = 0, leq=0, dl;
+
+  for(dl = pos = 0; pos<l; pos++, dl++)
+  {
+    unsigned char c;
+    switch(c=in[pos])
+    {
+     case '=': leq = dl; break;
+     case '+': c = ' '; break;
+     case '&': 
+       if(leq)
+       {
+	 VAR_MAP_INSERT();
+       } else if(rp) {
+	 int i;
+	 for(i=lamp-1;i<dl;i++) *((*rp)++)=dec[i];
+       }
+       leq = 0;
+       lamp = dl+1;
+       break;
+     case '%':
+       if(pos<l-2)
+       {
+	 c = (dhex(in[pos+1])<<4) + dhex(in[pos+2]);
+	 pos+=2;
+       }
+    }
+    dec[dl]=c;
+  }
+  if(leq)
+  {
+    VAR_MAP_INSERT();
+  } else if(rp) {
+    int i;
+    for(i=lamp-1;i<dl;i++) *((*rp)++)=dec[i];
+  }
+}
+
+static void parse_query(void)
+{
+  struct svalue *q;
+  struct mapping *v = allocate_mapping(10); /* variables */
+  push_string(s_query);
+  if(!(q = low_mapping_lookup(THIS->misc_variables, sp-1))) 
+    f_aap_scan_for_query(0);
+  q = low_mapping_lookup(THIS->misc_variables, sp-1);
+  sp--;
+
+  if(q->type == T_STRING) 
+  {
+    char *dec = malloc(q->u.string->len*2+1);
+    char *rest_query = dec+q->u.string->len+1, *rp=rest_query;
+    decode_x_url_mixed(q->u.string->str,q->u.string->len,v,dec,rest_query,&rp);
+    push_string(make_shared_binary_string(rest_query,rp-rest_query));
+    push_string(s_rest_query);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--; pop_stack();
+    free(dec);
+  } else {
+    push_int(0); push_string(s_rest_query);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--; pop_stack();
+  }
+
+  if(THIS->request->res.content_len &&
+     THIS->request->res.data[1]=='O')
+  {
+    struct pstring ct;
+    int nope = 0;
+    if(aap_get_header(THIS->request, "content-type", T_STRING, &ct))
+    {
+      if(ct.str[0]=='m') /* multipart is not wanted here... */
+	nope=1;
+    }
+    if(!nope)
+    {
+      char *tmp = malloc(THIS->request->res.content_len);
+      decode_x_url_mixed(THIS->request->res.data+
+			 THIS->request->res.body_start,
+			 THIS->request->res.content_len,v,tmp,0,0);
+      free(tmp);
+    }
+  }
+  push_mapping(v); push_string(s_variables);
+  mapping_insert(THIS->misc_variables, sp-1, sp-2);
+  sp--; pop_stack();
+}
+
+static void parse_headers(void)
+{
+  struct svalue *tmp;
+  struct mapping *headers = THIS->done_headers;
+  int os=0,i,j,l=THIS->request->res.body_start-THIS->request->res.header_start;
+  unsigned char *in=
+    (unsigned char*)THIS->request->res.data + THIS->request->res.header_start;
+
+  THIS->headers_parsed = 1;
+  for(i=0; i<l; i++)
+  {
+    switch(in[i])
+    {
+     case ':':
+       /* in[os..i-1] == the header */
+       for(j=os;j<i;j++) if(in[j] > 63 && in[j] < 91) in[j]+=32;
+       push_string(make_shared_binary_string((char*)in+os,i-os));
+       os = i+1;
+       while(in[os]==' ') os++;
+       for(j=os;j<l;j++) if(in[j] == '\r') break;
+       push_string(make_shared_binary_string((char*)in+os,j-os));
+       f_aggregate(1);
+       if((tmp = low_mapping_lookup(headers, sp-2)))
+       {
+	 tmp->u.array->refs++;
+	 push_array(tmp->u.array);
+	 map_delete(headers, sp-3);
+	 f_add(2);
+       }
+       mapping_insert(headers, sp-2, sp-1);
+       pop_n_elems(2);
+       os = i = j+2;
+    }
+  }
+}
+
+void f_aap_index_op(INT32 args)
+{
+  struct svalue *res;
+  struct pike_string *s;
+  if (!args) error("HTTP C object->`[]: too few args\n");
+  pop_n_elems(args-1);
+  if(!THIS->misc_variables) 
+  {
+    struct svalue s;
+    object_index_no_free2(&s, fp->current_object, sp-1);
+    pop_stack();
+    *sp=s;
+    sp++;
+    return;
+  }
+
+  if((res = low_mapping_lookup(THIS->misc_variables, sp-1)))
+  {
+    pop_stack();
+    assign_svalue_no_free( sp++, res );
+    return;
+  }
+  
+  if(!THIS->request) error("Reply called. No data available\n");
+  get_all_args("`[]", args, "%S", &s);
+  
+  if(s == s_not_query || s==s_query || s==s_prestate)
+  {
+    f_aap_scan_for_query(0);
+    f_aap_index_op(1);
+    return;
+  }
+
+  if(s == s_my_fd)
+  {
+    struct object *file_make_object_from_fd(int fd, int mode, int guess);
+    /* 0x3800 is from modules/files/file.h, 
+     * FILE_READ|FILE_WRITE|FILE_SET_CLOSE_ON_EXEC
+     */
+    push_object(file_make_object_from_fd
+		(dup(THIS->request->fd),0x3800,
+		 SOCKET_CAPABILITIES|PIPE_CAPABILITIES));
+    push_string(s_my_fd);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--;
+    return; /* the wanted value is on the stack now */
+  }
+
+  if(s == s_remoteaddr)
+  {
+    push_text(inet_ntoa(THIS->request->from.sin_addr));
+    push_string(s_remoteaddr);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--;
+    return;
+  }
+
+  if(s == s_method)
+  {
+    push_string(make_shared_binary_string(THIS->request->res.data,
+					  THIS->request->res.method_len));
+    push_string(s_method);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--;
+    return;
+  }
+
+  if(s == s_raw)
+  {
+    pop_stack();
+    push_string(make_shared_binary_string(THIS->request->res.data,
+					  THIS->request->res.data_len));
+    push_string(s_raw);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--;
+    return;
+  }
+
+  if(s == s_rest_query || s == s_variables)
+  {
+    parse_query();
+    f_aap_index_op(1);
+    return;
+  }
+
+  if(s == s_headers)
+  {
+    if(!THIS->headers_parsed) parse_headers();
+    THIS->done_headers->refs++;
+    push_mapping(THIS->done_headers);
+    return;
+  }
+
+  /* FIXME: Not actually implemented */
+/*   if(s == s_cookies || s == s_config) */
+/*   { */
+/*     if(!THIS->headers_parsed) parse_headers(); */
+
+/*     f_aap_index_op(1); */
+/*     return; */
+/*   } */
+
+  if(s == s_pragma)
+  {
+    struct svalue *tmp;
+    if(!THIS->headers_parsed) parse_headers();
+    if((tmp = low_mapping_lookup(THIS->done_headers, sp-1)))
+    {
+      pop_stack();
+      push_multiset(mkmultiset( tmp->u.array ));
+    } else {
+      pop_stack();
+      f_aggregate_multiset(0);
+    }
+    push_string(s_pragma);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--;
+    return;
+  }
+
+  if(s == s_client)
+  {
+    struct svalue *tmp;
+    if(!THIS->headers_parsed) parse_headers();
+    pop_stack();
+    push_string(s_user_agent);
+    if((tmp = low_mapping_lookup(THIS->done_headers, sp-1)))
+      assign_svalue_no_free( sp-1, tmp );
+    else 
+    {
+      sp--;
+      f_aggregate(0);
+    }
+    push_string(s_client);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--;
+    return;
+  }
+
+  if(s == s_referer)
+  {
+    struct svalue *tmp;
+    if(!THIS->headers_parsed) parse_headers();
+    if((tmp = low_mapping_lookup(THIS->done_headers, sp-1)))
+      assign_svalue( sp-1, tmp );
+    else
+    {
+      pop_stack();
+      push_int(0);
+    }
+    push_string(s_referer);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--;
+    return;
+  }
+  
+  if(s == s_since)
+  {
+    struct svalue *tmp;
+    if(!THIS->headers_parsed) parse_headers();
+    pop_stack();
+    push_string(s_if_modified_since);
+    if((tmp = low_mapping_lookup(THIS->done_headers, sp-1)))
+    {
+      tmp->u.array->item[0].u.string->refs++;
+      sp[-1] = tmp->u.array->item[0];
+    }
+    else
+    {
+      sp[-1].type = T_INT;
+      sp[-1].u.integer = 0;
+    }
+    push_string(s_since);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--;
+
+    f_aap_index_op(1);
+    return;
+  }
+
+  if( s == s_supports )
+  {
+    struct svalue *tmp;
+    pop_stack();
+    push_constant_text("roxen");
+    if((tmp = low_mapping_lookup(get_builtin_constants(), sp-1)) 
+       && tmp->type == T_OBJECT)
+    {
+      pop_stack( );
+      ref_push_object( tmp->u.object );
+      push_constant_text( "find_supports" );
+      f_index( 2 );
+      ref_push_string(s_client);
+      f_aap_index_op( 1 );
+      push_constant_text("");
+      f_multiply( 2 );
+      apply_svalue( sp-2, 1 );
+      push_string(s_supports);
+      mapping_insert(THIS->misc_variables, sp-1, sp-2);
+      sp--;
+      stack_swap();
+      pop_stack();
+
+    } else {
+      pop_stack();
+      f_aggregate_multiset( 0 );
+      push_string(s_supports);
+      mapping_insert(THIS->misc_variables, sp-1, sp-2);
+      sp--;
+    }
+
+
+
+      /* 
+         if(contents = o->misc["accept-encoding"])
+         {
+         foreach((contents-" ")/",", string e) {
+         if (lower_case(e) == "gzip") {
+         o->supports["autogunzip"] = 1;
+         }
+         }
+         }
+      */
+
+    return;
+  }
+
+
+/*   if(s == s_realauth || s == s_rawauth) */
+/*   { */
+/*     pop_stack(); */
+/*     push_int(0); */
+/*     return; */
+/*   } */
+
+  if(s == s_data)
+  {
+    pop_stack();
+    push_string(make_shared_binary_string(THIS->request->res.data
+					  +  THIS->request->res.body_start,
+					  THIS->request->res.content_len));
+    push_string(s_data);
+    mapping_insert(THIS->misc_variables, sp-1, sp-2);
+    sp--;
+    return;
+  }
+  {
+    struct svalue s;
+    object_index_no_free2(&s, fp->current_object, sp-1);
+    pop_stack();
+    *sp=s;
+    sp++;
+  }
+}
+
+/* static void f_index_equal_op(INT32 args) */
+/* { */
+  
+/* } */
+
+void f_aap_end(INT32 args)
+{
+  /* end connection. */
+}
+
+void f_aap_output(INT32 args)
+{
+  if(sp[-1].type != T_STRING) error("Bad argument 1 to output\n");
+  WRITE(THIS->request->fd, sp[-1].u.string->str, sp[-1].u.string->len);
+}
+
+#define BUFFER 8192
+struct thread_args *done;
+
+struct send_args
+{
+  struct args *to;
+  int from_fd;
+  char *data;
+  int data_len;
+  int len;
+  int sent;
+  char buffer[BUFFER];
+};
+
+/* WARNING! This function is running _without_ any stack etc. */
+void actually_send(struct send_args *a)
+{
+  int fail, first=0;
+  char foo[10];
+  foo[9]=0; foo[6]=0;
+
+#ifdef HAVE_FREEBSD_SENDFILE
+  if (a->len)
+  {
+    struct iovec vec;
+    struct sf_hdtr headers = { NULL, 0, NULL, 0 };
+    off_t off = 0;
+    off_t sent = 0;
+    size_t len = a->len;
+
+#ifdef AAP_DEBUG
+    fprintf(stderr, "sendfile... \n");
+#endif
+
+    if (a->data) {
+      /* Set up the iovec */
+      vec.iov_base = a->data;
+      vec.iov_len = a->data_len;
+      headers.headers = &vec;
+      headers.hdr_cnt = 1;
+    }
+
+    if (a->len < 0) {
+      /* Negative: Send all */
+      len = 0;
+    }
+
+    if ((off = tell(a->from_fd)) < 0) {
+      /* Probably a pipe, so sendfile() will probably fail anyway,
+       * but it doesn't hurt to try...
+       */
+      off = 0;
+    }
+    
+    if (sendfile(a->from_fd, a->to->fd, off, len,
+		 &headers, &sent, 0) < 0) {
+      /* FIXME: We aren't looking very hard at the errno, since
+       * sent usually tells us what to do.
+       */
+      switch(errno) {
+      case EBADF:	/* Either of the fds is invalid */
+      case ENOTSOCK:	/* to_fd is not a socket */
+      case EINVAL:	/* from_fd is not a file, to_fd not a SOCK_STREAM,
+			 * or offset is negative or out of range.
+			 */
+      case ENOTCONN:	/* to_fd is not a connected socket */
+      case EPIPE:	/* to_fd is closed at the other end */
+      case EIO:		/* Error reading from from_fd */
+	if (!sent) {
+	  /* Probably a bad fd-combo. Try sending by hand. */
+	  goto send_data;
+	}
+	break;
+      case EFAULT:	/* Invalid address specified as arg */
+	/* NOTE: Can't use fatal(), since we're not in a valid Pike context. */
+	DWERROR("FreeBSD-style sendfile() returned EFAULT.\n");
+	abort();
+	break;
+      case EAGAIN:	/* socket in nonblocking mode, sent is valid */
+	break;
+      }
+    }
+
+    /* At this point sent will always contain the actual number
+     * of bytes sent.
+     */
+    a->sent += sent;
+
+    if (a->data) {
+      if (sent < a->data_len) {
+	a->data += sent;
+	a->data_len -= sent;
+	sent = 0;
+
+	/* Make sure we don't terminate early due to len being 0. */
+	goto send_data;
+      } else {
+	sent -= a->data_len;
+	a->data = NULL;
+	a->data_len = 0;
+      }
+    }
+
+    if (len) {
+      a->len -= sent;
+      if (!a->len) {
+	goto end;
+      }
+      goto normal;
+    } else {
+      /* Assume all was sent */
+      a->len = 0;
+      goto end;
+    }
+  }
+
+ send_data:
+#endif /* !HAVE_FREEBSD_SENDFILE */
+
+#ifdef AAP_DEBUG
+  fprintf(stderr, "actually_send... \n");
+#endif
+  if(a->data)
+  {
+    MEMCPY(foo, a->data+MY_MIN((a->data_len-4),9), 4);
+    first=1;
+#ifdef TCP_CORK
+#ifdef AAP_DEBUG
+    fprintf(stderr, "cork... \n");
+#endif
+    {
+      int true=1;
+      setsockopt( a->to->fd, SOL_TCP, TCP_CORK, &true, 4 );
+    }
+#endif
+    fail = WRITE(a->to->fd, a->data, a->data_len);
+    a->sent += fail;
+    if(fail != a->data_len)
+      goto end;
+  }
+  fail = 0;
+
+#if !defined(HAVE_FREEBSD_SENDFILE) && defined(HAVE_SENDFILE)
+  if(a->len)
+  {
+#ifdef AAP_DEBUG
+    fprintf(stderr, "pre sendfile... \n");
+#endif
+    if(!first)
+    {
+      first=1;
+      fail = read(a->from_fd, foo, 10);
+      if(fail < 0)
+        goto end;
+      WRITE( a->to->fd, foo, fail );
+      a->len  -= fail;
+    }
+#ifdef AAP_DEBUG
+    fprintf(stderr, "sendfile... \n");
+#endif
+    switch(fail = sendfile(a->to->fd, a->from_fd, NULL, a->len ))
+    {
+     case -ENOSYS:
+#ifdef AAP_DEBUG
+       fprintf(stderr, "syscall does not exist.\n");
+#endif
+       goto normal;
+     default:
+       if(fail != a->len)
+         fprintf(stderr, "sendfile returned %d; len=%d\n", fail, a->len);
+    }
+    goto end;
+  }
+#endif /* HAVE_SENDFILE && !HAVE_FREEBSD_SENDFILE */
+
+ normal:
+#ifdef AAP_DEBUG
+  fprintf(stderr, "using normal fallback... \n");
+#endif
+#ifdef DIRECTIO_ON
+  if(a->len > (65536*4))
+    directio(a->from_fd, DIRECTIO_ON);
+#endif
+  
+  /*
+   * Ugly optimization...
+   * Removes the sign-bit from the len.
+   * => -1 => 0x7fffffff, which means that cgi will work.
+   *
+   *	/grubba 1998-08-30
+   */
+  a->len &= 0x7fffffff;
+
+  while(a->len)
+  {
+    int nread, written=0;
+    nread = fd_read(a->from_fd, a->buffer, MY_MIN(BUFFER,a->len));
+    if(!first)
+    {
+      first=1;
+      MEMCPY(foo,a->buffer+9,5);
+    }
+    if(nread <= 0)
+    {
+      if((nread < 0) && (errno == EINTR))
+	continue;
+      else
+      {
+	/* CGI's will come here at eof (nread == 0),
+	 * and get keep-alive disabled.
+	 */
+	fail = 1;
+	break;
+      }
+    }
+    if(fail || (WRITE(a->to->fd, a->buffer, nread) != nread))
+      goto end;
+  }
+
+ end:
+#ifdef TCP_CORK
+  {
+    int false = 0;
+    setsockopt( a->to->fd, SOL_TCP, TCP_CORK, &false, 4 );
+  }
+#endif
+  {
+    struct args *arg = a->to;
+    LOG(a->sent, a->to, atoi(foo));
+    if(a->from_fd) close(a->from_fd);
+    if(a->data) free(a->data);
+    free(a);
+
+    if(!fail && 
+       ((arg->res.protocol==s_http_11)
+	||aap_get_header(arg, "Connection", H_EXISTS, 0)))
+    {
+      aap_handle_connection(arg);
+    }
+    else
+    {
+      if(arg->res.data) free(arg->res.data);
+      if(arg->fd) close(arg->fd);
+      free(arg);
+    }
+  }
+}
+
+void f_aap_reply(INT32 args)
+{
+  int reply_string=0, reply_object=0;
+  struct send_args *q;
+  if(!THIS->request)
+    error("reply already called.\n");
+  if(args && sp[-args].type == T_STRING) 
+    reply_string = 1;
+
+  if(args>1)
+  {
+    if(args<3)
+      error("->reply(string|void pre,object(Stdio.file) fd,int len)\n");
+    if(sp[-args+1].type != T_OBJECT)
+      error("Bad argument 2 to reply\n");
+    if(sp[-args+2].type != T_INT)
+      error("Bad argument 3 to reply\n");
+    reply_object = 1;
+  }
+
+  if(reply_string && !reply_object)
+  {
+    if(sp[-1].u.string->len < 8192)
+    {
+      int amnt = WRITE(THIS->request->fd,sp[-1].u.string->str,
+		       sp[-1].u.string->len);
+      LOG(amnt, THIS->request, 
+	  atoi(sp[-1].u.string->str+MY_MIN(sp[-1].u.string->len,9)));
+      if((sp[-1].u.string->len == amnt) &&
+	 ((THIS->request->res.protocol==s_http_11)
+	  ||aap_get_header(THIS->request, "Connection", H_EXISTS, 0)))
+      {
+	th_farm((void (*)(void *))aap_handle_connection, 
+		(void *)THIS->request);
+	THIS->request = 0;
+	return;
+      } else {
+	if(THIS->request->res.data)
+	  free(THIS->request->res.data);
+	close(THIS->request->fd);
+	free(THIS->request);
+	THIS->request = 0;
+	return;
+      }
+    }
+  }
+
+  q = malloc(sizeof(struct send_args));
+  q->to = THIS->request;
+  THIS->request = 0;
+
+  if(reply_object)
+  {
+    /* safe_apply() needed to avoid leak of q */
+    safe_apply(sp[-2].u.object, "query_fd", 0);
+    if((sp[-1].type != T_INT) || (sp[-1].u.integer <= 0))
+    {
+      free(q);
+      error("Bad fileobject to request_object->reply()\n");
+    }
+    if((q->from_fd = dup(sp[-1].u.integer)) == -1)
+      error("Bad file object to request_object->reply()\n");
+    pop_stack();
+
+    q->len = sp[-1].u.integer;
+  } else {
+    q->from_fd = 0;
+    q->len = 0;
+  }
+
+  if(reply_string)
+  {
+    char *s = malloc(sp[-args].u.string->len);
+    MEMCPY(s, sp[-args].u.string->str, sp[-args].u.string->len);
+    q->data = s;
+    q->data_len = sp[-args].u.string->len;
+  } else {
+    q->data = 0;
+    q->data_len = 0;
+  }
+  q->sent = 0;
+
+  th_farm( (void (*)(void *))actually_send, (void *)q );
+
+  pop_n_elems(args);
+  push_int(0);
+}
+
+void f_aap_reply_with_cache(INT32 args)
+{
+  struct cache_entry *ce;
+  struct pike_string *reply;
+  int time_to_keep, t, freed=0;
+  if(!THIS->request)
+    error("Reply already called.\n");
+
+  get_all_args("reply_with_cache", args, "%S%d", &reply, &time_to_keep);
+
+  if(reply->len < THIS->request->cache->max_size/2)
+  {
+    if(THIS->request->cache->gone) /* freed..*/
+    {
+      close(THIS->request->fd);
+      if(THIS->request->res.data)
+	free(THIS->request->res.data);
+      free(THIS->request);
+      THIS->request = 0;
+      return;
+    }
+    t = aap_get_time();
+    mt_lock(&THIS->request->cache->mutex);
+    if(THIS->request->cache->size > THIS->request->cache->max_size)
+    {
+      struct cache_entry *p,*pp=0,*ppp=0;
+      if(THIS->request->cache->unclean) 
+	aap_clean_cache(THIS->request->cache, 1);
+      while(THIS->request->cache->size > THIS->request->cache->max_size)
+      {
+	int i;
+	freed=0;
+	for(i = 0; i<CACHE_HTABLE_SIZE; i++)
+	{
+	  p = THIS->request->cache->htable[i];
+	  ppp=pp=0;
+	  while(p)
+	  {
+	    ppp = pp;
+	    pp = p;
+	    p = p->next;
+	  }
+	  if(pp) aap_free_cache_entry(THIS->request->cache,pp,ppp,i);
+	  freed++;
+	  if(THIS->request->cache->size < THIS->request->cache->max_size)
+	    break;
+	}
+	if(!freed)  /* no way.. */
+	  break;
+      }
+    }
+    ce = malloc(sizeof(struct cache_entry));
+    MEMSET(ce, 0, sizeof(struct cache_entry));
+    ce->stale_at = t+time_to_keep;
+    ce->data = reply;
+    ce->url = THIS->request->res.url;
+    ce->url_len = THIS->request->res.url_len;
+    reply->refs++;
+
+    ce->host = THIS->request->res.host;
+    ce->host_len = THIS->request->res.host_len;
+    aap_cache_insert(ce, THIS->request->cache);
+    mt_unlock(&THIS->request->cache->mutex);
+  }
+  pop_stack();
+  f_aap_reply(1);
+}
+
+void f_aap_reqo__init(INT32 args)
+{
+  if(THIS->request->res.protocol)
+    SINSERT(THIS->misc_variables, s_prot, THIS->request->res.protocol);
+  IINSERT(THIS->misc_variables, s_time, aap_get_time());
+  TINSERT(THIS->misc_variables, s_rawurl, 
+	  THIS->request->res.url, THIS->request->res.url_len);
+}
+
+void aap_init_request_object(struct object *o)
+{
+  MEMSET(THIS, 0, sizeof(*THIS));
+}
+
+void aap_exit_request_object(struct object *o)
+{
+  if(THIS->request)
+  {
+    close(THIS->request->fd);
+    if(THIS->request->res.data) free(THIS->request->res.data);
+    free(THIS->request);
+  }
+  if(THIS->misc_variables)
+    free_mapping(THIS->misc_variables);
+  if(THIS->done_headers)
+    free_mapping(THIS->done_headers);
+}
+#endif
diff --git a/src/modules/HTTPLoop/requestobject.h b/src/modules/HTTPLoop/requestobject.h
new file mode 100644
index 0000000000..fbe34d1aec
--- /dev/null
+++ b/src/modules/HTTPLoop/requestobject.h
@@ -0,0 +1,13 @@
+void f_aap_scan_for_query(INT32 args);
+void f_aap_index_op(INT32 args);
+void f_aap_end(INT32 args);
+void f_aap_output(INT32 args);
+void f_aap_reply(INT32 args);
+void f_aap_reply_with_cache(INT32 args);
+void f_aap_reqo__init(INT32 args);
+void aap_init_request_object(struct object *o);
+void aap_exit_request_object(struct object *o);
+
+
+
+
diff --git a/src/modules/HTTPLoop/static_strings.h b/src/modules/HTTPLoop/static_strings.h
new file mode 100644
index 0000000000..98ee9767fa
--- /dev/null
+++ b/src/modules/HTTPLoop/static_strings.h
@@ -0,0 +1,42 @@
+/* methods */
+
+STRING(s_http_09,"HTTP/0.9");
+STRING(s_http_10,"HTTP/1.0");
+STRING(s_http_11,"HTTP/1.1");
+
+/* headers */
+STRING(s_user_agent,"user-agent");
+STRING(s_if_modified_since,"if-modified-since");
+
+/* variables */
+
+STRING(s_not_query,"not_query");
+STRING(s_query,"query");
+STRING(s_prestate,"prestate");
+STRING(s_time,"time");
+STRING(s_my_fd,"my_fd");
+STRING(s_prot,"prot");
+STRING(s_method,"method");
+STRING(s_rawurl,"rawurl");
+STRING(s_raw,"raw");
+STRING(s_data,"data");
+STRING(s_remoteaddr,"remoteaddr");
+STRING(s_headers,"headers");
+STRING(s_pragma,"pragma");
+STRING(s_client,"client");
+STRING(s_referer,"referer");
+STRING(s_since,"since");
+
+
+STRING(s_variables,"variables");
+STRING(s_rest_query,"rest_query");
+
+/* To be implemented */
+
+STRING(s_cookies,"cookies");
+/* STRING(s_config,"config"); */
+
+STRING(s_rawauth,"rawauth");
+STRING(s_realauth,"realauth");
+STRING(s_supports, "supports");
+
diff --git a/src/modules/HTTPLoop/test.pike b/src/modules/HTTPLoop/test.pike
new file mode 100644
index 0000000000..9720abae60
--- /dev/null
+++ b/src/modules/HTTPLoop/test.pike
@@ -0,0 +1,100 @@
+string MP;
+
+class request_program 
+{
+  inherit HTTPAccept.prog;
+}
+
+string type_from_fname(string fname)
+{
+  if(sscanf(fname, "%*s.ht")) return "text/html";
+  if(sscanf(fname, "%*s.gi"))  return "image/gif";
+  if(sscanf(fname, "%*s.jp"))  return "image/jpeg";
+  return "text/plain";
+}
+
+void handle(object o)
+{
+  object fd = Stdio.File();
+  string q = o->not_query;
+  int len;
+  if(!strlen(q)) q="foo";
+  if(q[-1]=='/') 
+    q += "index.html";
+  sscanf(q, "%*[/]%s", q);
+  if(fd->open(combine_path(MP, q), "r"))
+  {
+    len = fd->stat()[1];
+    string data = 
+      ("HTTP/1.0 200 Ok\r\n"
+       "Server: Quick'n'dirty\r\n"
+       "Content-type: "+type_from_fname(q)+"\r\n"
+       "Connection: Keep-Alive\r\n"
+       "Content-length: "+len+"\r\n"
+       "\r\n");
+    if(len < 65535)
+    {
+      data += fd->read();
+      o->reply_with_cache( data, 20 );
+    } else {
+      o->reply(data, fd, len);
+    }
+  } else {
+    string er = "No such file";
+    o->reply_with_cache("HTTP/1.0 402 No such file\r\n"
+			"Server: Quick'n'dirty\r\n"
+			"Content-type: text/plain\r\n"
+			"Connection: Keep-Alive\r\n"
+			"Content-length: "+strlen(er)+"\r\n"
+			"\r\n"+er, 200);
+  }
+}
+
+function lw;
+#define PCT(X) ((int)(((X)/(float)(c->total+0.1))*100))
+
+void cs()
+{
+  call_out(cs, 10);
+  catch
+  {
+    mapping c = HTTPAccept.cache_status()[0];
+    werror("\nCache statistics\n");
+    c->total = c->hits + c->misses + c->stale;
+    werror(" %d elements in cache, size is %1.1fKb max is %1.1fMb\n"
+	   " %d cache lookups, %d%% hits, %d%% misses and %d%% stale.\n",
+	   c->entries, c->size/1024.0, c->max_size/(1024*1024.0),
+	   c->total, PCT(c->hits), PCT(c->misses), PCT(c->stale));
+  };
+}
+
+object port;
+int main(int argc, array argv)
+{
+  MP = argv[-1];
+  object t = Stdio.File();
+  t->open("log", "cwt");
+
+  thread_create(lambda(object lf) 
+		{
+		  while(1)
+		  {
+		    HTTPAccept.log_as_commonlog_to_file(lf);
+		    sleep(2);
+		  }
+		}, t);
+
+#if efun(thread_set_concurrency)
+  thread_set_concurrency(10);
+#endif
+  port = files.port();
+  if(!port->bind(3000))
+  {
+    werror("Bind failed.\n");
+    return 1;
+  }
+  HTTPAccept.accept_http_loop( port, request_program, handle, 0, 1024*1024, 1);
+  call_out(cs, 0, 0);
+//   call_out(destruct, 20, port);
+  return -1;
+}
diff --git a/src/modules/HTTPLoop/testsuite.in b/src/modules/HTTPLoop/testsuite.in
new file mode 100644
index 0000000000..eb84989585
--- /dev/null
+++ b/src/modules/HTTPLoop/testsuite.in
@@ -0,0 +1 @@
+// $Id: testsuite.in,v 1.1 1999/11/14 00:43:15 per Exp $
diff --git a/src/modules/HTTPLoop/timeout.c b/src/modules/HTTPLoop/timeout.c
new file mode 100644
index 0000000000..35db6afec2
--- /dev/null
+++ b/src/modules/HTTPLoop/timeout.c
@@ -0,0 +1,263 @@
+#include "config.h"
+#ifndef __NT__
+#include <global.h>
+#include <threads.h>
+#include <stralloc.h>
+#include <signal.h>
+#include <fdlib.h>
+
+#ifdef _REENTRANT
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "accept_and_parse.h"
+#include "timeout.h"
+#include "util.h"
+
+#ifdef HAVE_POLL
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#else /* !HAVE_POLL_H */
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#else /* !HAVE_SYS_POLL_H */
+#undef HAVE_POLL
+#endif /* HAVE_SYS_POLL_H */
+#endif /* HAVE_POLL_H */
+#endif /* HAVE_POLL */
+
+MUTEX_T aap_timeout_mutex;
+
+struct timeout {
+  int raised;
+  struct timeout *next, *prev;
+  THREAD_T thr;
+  int when;
+} *first_timeout, *last_timeout;
+
+struct timeout *next_free_timeout;
+
+
+
+/*
+ * Debug code, since I for some reason always manage
+ * to mess up my double linked lists (this time I had forgot a mt_lock()..
+ */
+#ifdef DEBUG
+extern int d_flag;
+/*
+ * If I call the 'fatal' in pike, I run out of stack space. This is
+ * not a good idea, since you cannot debug this program with gdb
+ * if this happends.  Thus this macro... 
+ */
+#define FATAL(X) do{fprintf(stderr,"%s:%d: %s",__FILE__,__LINE__,X);abort();}while(0)
+
+static void debug_tchain(void)
+{
+  if(first_timeout)
+  {
+    struct timeout *t = first_timeout, *p=NULL;
+    if(!last_timeout)
+      FATAL("First timeout, but no last timeout.\n");
+    if(last_timeout->next)
+      FATAL("Impossible timeout list: lt->next != NULL\n");
+    
+    while( t )
+    {
+      p = t;
+      t = t->next;
+      if(p == t)
+	FATAL("Impossible timeout list: t->next==t\n");
+      if(t->prev != p)
+	FATAL("Impossible timeout list: t->prev!=p\n");
+    }
+    if(p != last_timeout)
+      FATAL("End of to list != lt\n");
+  }
+}
+#endif
+
+
+#define CHUNK 1000
+
+static struct timeout *new_timeout(THREAD_T thr, int secs)
+{
+  struct timeout *t;
+  if(next_free_timeout)
+  {
+    t = next_free_timeout;
+    next_free_timeout = t->next;
+  } else {
+    int i;
+    char *foo = malloc(sizeof(struct timeout) * CHUNK);
+    for(i = 1; i<CHUNK; i++)
+    {
+      struct timeout *c = (struct timeout *)(foo+(i*sizeof(struct timeout)));
+      c->next = next_free_timeout;
+      next_free_timeout = c;
+    }
+    t = (struct timeout *)foo;
+  }
+  t->next = NULL;
+  t->prev = NULL;
+  t->thr = thr;
+  t->raised = 0;
+  t->when = aap_get_time() + secs;
+  return t;
+}
+
+static void free_timeout( struct timeout *t )
+{
+  t->next = next_free_timeout;
+  next_free_timeout = t;
+}
+
+int  *aap_add_timeout_thr(THREAD_T thr, int secs)
+{
+  struct timeout *to;
+  mt_lock( &aap_timeout_mutex );
+  to = new_timeout( thr, secs );
+  if(last_timeout)
+  {
+    last_timeout->next = to;
+    to->prev = last_timeout;
+    last_timeout = to;
+  } else
+    last_timeout = first_timeout = to;
+#ifdef DEBUG
+  if(d_flag>2) debug_tchain();
+#endif
+  mt_unlock( &aap_timeout_mutex );
+  return &to->raised;
+}
+
+
+#ifndef OFFSETOF
+#define OFFSETOF(str_type, field) ((long)& (((struct str_type *)0)->field))
+#endif
+
+void aap_remove_timeout_thr(int *to)
+{
+  mt_lock( &aap_timeout_mutex );
+  {
+    /* This offset is most likely 0.
+     * But who knows what odd things compilers
+     * can come up with when you are not looking...
+     */
+    struct timeout *t=(struct timeout *)(((char *)to)
+					 -OFFSETOF(timeout,raised));
+    if(t)
+    {
+      if(t->next) 
+	t->next->prev = t->prev;
+      else
+	last_timeout = t->prev;
+      if(t->prev) 
+	t->prev->next = t->next;
+      else
+	first_timeout = t->next;
+      free_timeout( t );
+    }
+  }
+#ifdef DEBUG
+  if(d_flag>2) debug_tchain();
+#endif
+  mt_unlock( &aap_timeout_mutex );
+}
+
+static volatile int aap_time_to_die = 0;
+
+static void *handle_timeouts(void *ignored)
+{
+  while(!aap_time_to_die)
+  {
+    if(first_timeout)
+    {
+      mt_lock( &aap_timeout_mutex );
+      {
+	struct timeout *t = first_timeout;
+	if(t)
+	{
+	  if(t->when < aap_get_time())
+	  {
+	    t->raised++;
+	    th_kill( t->thr, SIGCHLD );
+	  }
+	  if(last_timeout != first_timeout)
+	  {
+	    first_timeout = t->next;
+            first_timeout->prev = NULL;
+	    t->next=NULL;
+	    last_timeout->next = t;
+            t->prev = last_timeout;
+	    last_timeout = t;
+	  }
+	}
+      }
+      mt_unlock( &aap_timeout_mutex );
+    }
+#ifdef DEBUG
+    if(d_flag>2) debug_tchain();
+#endif
+#ifdef HAVE_POLL
+    poll(0,0,300);
+#else
+    sleep(1);
+#endif
+  }
+  /*
+   * Now we're dead...
+   */
+#ifdef AAP_DEBUG
+  fprintf(stderr, "AAP: handle_timeout() is now dead.\n");
+#endif /* AAP_DEBUG */
+  return(NULL);
+}
+
+static THREAD_T aap_timeout_thread;
+
+void aap_init_timeouts(void)
+{
+#ifdef AAP_DEBUG
+  fprintf(stderr, "AAP: aap_init_timeouts.\n");
+#endif /* AAP_DEBUG */
+  mt_init(&aap_timeout_mutex);
+  th_create_small(&aap_timeout_thread, handle_timeouts, 0);
+#ifdef AAP_DEBUG
+  fprintf(stderr, "AAP: handle_timeouts started.\n");
+#endif /* AAP_DEBUG */
+}
+
+void aap_exit_timeouts(void)
+{
+  void *res;
+
+#ifdef AAP_DEBUG
+  fprintf(stderr, "AAP: aap_exit_timeouts.\n");
+#endif /* AAP_DEBUG */
+  aap_time_to_die = 1;
+  if (thread_id) {
+    THREADS_ALLOW();
+    th_join(aap_timeout_thread, &res);
+    THREADS_DISALLOW();
+  } else {
+    th_join(aap_timeout_thread, &res);
+  }
+#ifdef AAP_DEBUG
+  fprintf(stderr, "AAP: aap_exit_timeouts done.\n");
+#endif /* AAP_DEBUG */
+}
+
+#endif
+#endif
diff --git a/src/modules/HTTPLoop/timeout.h b/src/modules/HTTPLoop/timeout.h
new file mode 100644
index 0000000000..246616ae39
--- /dev/null
+++ b/src/modules/HTTPLoop/timeout.h
@@ -0,0 +1,8 @@
+#if 0
+void aap_init_timeouts(void);
+void aap_exit_timeouts(void);
+void aap_remove_timeout_thr(int *hmm);
+int *aap_add_timeout_thr(THREAD_T thr, int secs);
+
+extern MUTEX_T aap_timeout_mutex;
+#endif
diff --git a/src/modules/HTTPLoop/util.c b/src/modules/HTTPLoop/util.c
new file mode 100644
index 0000000000..ff9ab2c614
--- /dev/null
+++ b/src/modules/HTTPLoop/util.c
@@ -0,0 +1,105 @@
+#include <global.h>
+#include <threads.h>
+#include <fdlib.h>
+
+#ifdef _REENTRANT
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "accept_and_parse.h"
+#include "util.h"
+
+
+int aap_get_time(void)
+{
+  static int t = 0;
+  static int last_time;
+  if(!(t++%10)) last_time = TIME(0);
+  return last_time;
+}
+
+int aap_swrite(int to, char *buf, int towrite)
+{
+  int res=0;
+  int sent = 0;
+  while(towrite)
+  {
+    while((res = fd_write(to, buf, towrite)) < 0)
+    {
+      switch(errno)
+      {
+       case EAGAIN:
+       case EINTR:
+	 continue;
+
+       default:
+	if(errno != EPIPE)
+	  perror("accept_and_parse->request->shuffle: While writing");
+	res = 0;
+	return sent;
+      }
+    }
+    towrite -= res;
+    buf += res;
+    sent += res;
+  }
+  return sent;
+}
+
+int aap_get_header(struct args *req, char *header, int operation, void *res)
+{
+  int os=0,i,hl=strlen(header),l=req->res.body_start-req->res.header_start;
+  char *in = req->res.data + req->res.header_start;
+  for(i=0; i<l; i++)
+  {
+    switch(in[i])
+    {
+     case ':':
+       /* in[os..i-1] == the header */
+       if(i-os == hl)
+       {
+	 int j;
+	 for(j=0;j<hl; j++)
+	   if((in[os+j]&95) != (header[j]&95))
+	     break; /* no match */
+	 if(j == hl)
+	 {
+	   struct pstring *r = res;
+	   switch(operation)
+	   {
+	    case H_EXISTS:
+	      return 1;
+	    case H_INT:
+	      *((int *)res) = atoi(in+i+1);
+	      return 1;
+	    case H_STRING:
+	      os = i+1;
+	      for(j=os;j<l;j++) if(in[j] == '\r') break;
+	      /* okie.. */
+	      while(in[os]==' ') os++;
+	      r->len = j-os;
+	      r->str = in+os;
+	      return 1;
+	   }
+	 }
+       }
+     case '\r':
+     case '\n':
+       os = i+1;
+    }
+  }
+  return 0;
+} 
+#endif
+
diff --git a/src/modules/HTTPLoop/util.h b/src/modules/HTTPLoop/util.h
new file mode 100644
index 0000000000..0078f90c87
--- /dev/null
+++ b/src/modules/HTTPLoop/util.h
@@ -0,0 +1,10 @@
+int aap_get_time(void);
+int aap_swrite(int to, char *buf, int towrite);
+
+#define H_EXISTS 0
+#define H_INT    1
+#define H_STRING 2
+
+
+int aap_get_header(struct args *req, char *header, int operation, void *res);
+
diff --git a/src/modules/HTTPLoop/wwwserver.pike b/src/modules/HTTPLoop/wwwserver.pike
new file mode 100644
index 0000000000..a1182ff179
--- /dev/null
+++ b/src/modules/HTTPLoop/wwwserver.pike
@@ -0,0 +1,194 @@
+import Stdio;
+
+mapping exts = ([]);
+constant days = ({ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" });
+constant months = ({ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" });
+
+constant prefix = ({ "bytes", "kB", "MB", "GB", "TB", "HB"});
+
+string sizetostring( int size )
+{
+  int s;
+  if(size<0)
+    return "--------";
+  s=size*10; 
+  size = 0;
+  while( s > 10240 || size==0)
+  {
+    s /= 1024;
+    size ++;
+  }
+  return sprintf("%d.%01d %s", s/10, s%10, prefix[ size ]);
+}
+
+
+// The server root.
+#ifdef __NT__
+#define BASE "H:\\html\\per.hedbor.org\\"
+#else
+#define BASE "/tmp/"
+#endif
+
+#define PORT 4002
+
+string http_date(int t)
+{
+  mapping l = localtime(t);
+
+#if 1
+
+  t += l->timezone - 3600*l->isdst;
+  l = localtime(t);
+
+  return(sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
+		 days[l->wday], l->mday, months[l->mon], 1900+l->year,
+		 l->hour, l->min, l->sec));
+
+#else
+  string s=ctime(t + l->timezone - 3600*l->isdst);
+  return (s[0..2] + sprintf(", %02d ", (int)s[8..9])
+	  + s[4..6]+" "+(1900+l->year)
+	  + s[10..18]+" GMT"); 
+#endif /* 1 */
+}
+
+
+class request_program 
+{
+  inherit HTTPAccept.prog;
+}
+
+void handle(object o)
+{
+  string url = o->not_query;
+  string f = combine_path(BASE, url[1..]);
+  array s;
+  if(f[-1] == '/' && (s = file_stat(f+"index.html"))) {
+    url += "index.html";
+    f += "index.html";
+  } else
+    s = file_stat(f);
+  if(!s || s[1] == -1 || search(f, BASE) == -1)
+  {
+    string nofile = "<title>No such file</title>"
+      "<h1>No such file or directory: "+f+"</h1>";
+    o->reply_with_cache("HTTP/1.0 404 Unknown file\r\n"
+			"Content-type: text/html\r\n"
+			"Content-Length: "+strlen(nofile)+"\r\n"
+			"MIME-Version: 1.0\r\n"
+			"Date: "+http_date(time(1))+"\r\n"
+			"Server: Neo-FastSpeed\r\n"
+			"Connection: Keep-Alive\r\n"
+			"\r\n"
+			+nofile, 60);
+  }
+  else if(s[1] == -2) 
+  {
+    if(f[-1] == '/' || f[-1]=='\\')
+    {
+      string head = ("HTTP/1.1 200 Ok\r\n"
+		     "Content-type: text/html\r\n"
+		     "Date: "+http_date(time(1))+"\r\n"
+		     "Last-Modified: "+http_date(s[4])+"\r\n"
+		     "MIME-Version: 1.0\r\n"
+		     "Server: Neo-FastSpeed\r\n"
+		     "Connection: Keep-Alive\r\n");
+      string res = ("<title>Directory listing of "+url+"</title>"
+		    "<h1 align=center>Directory listing of "+
+		    url+"</h1>\n<pre>");
+      if(url != "/")
+	res += "<b><a href=../>Parent Directory...</a></b>\n\n";
+      string file2;
+      foreach(sort(get_dir(f)||({})), string file) {
+	int size = file_size(f+file);
+	int islink;
+#if constant(readlink)
+	catch { readlink(f+file); islink = 1; };
+#endif
+	string ext = (file / ".") [-1];
+	string ctype = (size >= 0 ? exts[ext] || "text/plain" :
+			"Directory");
+	if(size < -1)
+	  file += "/";
+	if(islink)
+	  file2 = "@"+file;
+	else
+	  file2 = file;
+	res += sprintf("<a href=%s>%-30s</a> %15s %s\n",
+		       file, file2, sizetostring(size),
+		       ctype);
+      }
+      return o->reply_with_cache(head +
+				 "Content-Length: "+strlen(res)+
+				 "\r\n\r\n"+res, 30);
+    } else {
+      o->reply_with_cache("HTTP/1.0 302  Redirect\r\n"
+			  "Content-type: text/plain\r\n"
+			  "Date: "+http_date(time(1))+"\r\n"
+			  "MIME-Version: 1.0\r\n"
+			  "Server: Neo-FastSpeed\r\n"
+			  "Connection: Keep-Alive\r\n"
+			  "Content-Length: 0\r\n"
+			  "Location: "+url+"/"+
+			  (o->query ? "?"+o->query : "")+
+			  "\r\n\r\n", 60);
+    }
+  } else {
+    string ext = (f / ".") [-1];
+    string ctype = exts[ext] || "text/plain";
+#if 1
+    o->reply_with_cache("HTTP/1.1 200 Ok\r\n"
+			"Content-type: "+ctype+"\r\n"
+			"Content-Length: "+s[1]+"\r\n"
+			"Date: "+http_date(time(1))+"\r\n"
+			"Last-Modified: "+http_date(s[4])+"\r\n"
+			"MIME-Version: 1.0\r\n"
+			"Server: Neo-FastSpeed\r\n"
+			"Connection: Keep-Alive\r\n"
+			"\r\n"+
+			read_bytes(f), 20);
+#else
+    o->reply("HTTP/1.1 200 Ok\r\n"
+             "Content-type: "+ctype+"\r\n"
+             "Content-Length: "+s[1]+"\r\n"
+             "Date: "+http_date(time(1))+"\r\n"
+             "Last-Modified: "+http_date(s[4])+"\r\n"
+             "MIME-Version: 1.0\r\n"
+             "Server: Neo-FastSpeed\r\n"
+             "Connection: Keep-Alive\r\n"
+             "\r\n",
+             Stdio.File(f, "r"), s[1]);
+#endif
+    destruct(o);
+  }
+}
+
+object port;
+object l;
+
+int main(int argc, array (string) argv)
+{
+#if efun(thread_set_concurrency)
+  thread_set_concurrency(100);
+#endif
+  port = Stdio.Port();
+  array foo;
+  foreach(read_file((argv[0] - "wwwserver.pike") + "extensions")/"\n",
+	  string s) {
+    if(strlen(s) && s[0] != '#' && (foo = (s / "\t" - ({""}))) &&
+       sizeof(foo) == 2)
+      exts[foo[0]] = foo[1];
+  }
+  werror(sprintf("Found %d extensions...\n", sizeof(exts)));
+  if(!port->bind(PORT))
+  {
+    werror("Bind failed.\n");
+    return 1;
+  }
+  
+  l = HTTPAccept.Loop( port, request_program, handle, 0, 1024*1024, 0, 
+		       0);
+  werror("WWW-server listening to port "+PORT+".\n");
+  return -1;
+}
-- 
GitLab