From 275133b216b04f8a9518f2f8039dd0f6fc932b21 Mon Sep 17 00:00:00 2001
From: patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>
Date: Sat, 15 Aug 2009 22:59:59 +0000
Subject: [PATCH] Continued THTTPD debug

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@2023 42af7a65-404d-4744-a932-0658087f49c3
---
 fs/fs_stat.c                  |    8 +-
 netutils/thttpd/libhttpd.c    | 1576 +++++++++++++++++----------------
 netutils/thttpd/tdate_parse.c |    8 +
 3 files changed, 804 insertions(+), 788 deletions(-)

diff --git a/fs/fs_stat.c b/fs/fs_stat.c
index 2f236264f0..5923a04f10 100644
--- a/fs/fs_stat.c
+++ b/fs/fs_stat.c
@@ -89,11 +89,13 @@ static inline int statpsuedo(FAR struct inode *inode, FAR struct stat *buf)
     }
   else
     {
-      /* If it has no operations, then it must just be a intermeidate
+      /* If it has no operations, then it must just be a intermediate
        * node in the inode tree.  It is something like a directory.
+       * We'll say that all psuedo-directories are read-able but not
+       * write-able.
        */
 
-      buf->st_mode |= S_IFDIR;
+      buf->st_mode |= S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
     }
 
   return OK;
@@ -108,7 +110,7 @@ static inline int statroot(FAR struct stat *buf)
   /* There is no inode associated with the fake root directory */
   
   memset(buf, 0, sizeof(struct stat) );
-  buf->st_mode = S_IFDIR;
+  buf->st_mode = S_IFDIR|S_IROTH|S_IRGRP|S_IRUSR;
   return OK;
 }
 
diff --git a/netutils/thttpd/libhttpd.c b/netutils/thttpd/libhttpd.c
index 60366ecfd5..19b0599c60 100644
--- a/netutils/thttpd/libhttpd.c
+++ b/netutils/thttpd/libhttpd.c
@@ -197,7 +197,6 @@ static int  cgi_child(int argc, char **argv);
 static int  cgi(httpd_conn *hc);
 #endif
 
-static int  really_start_request(httpd_conn *hc, struct timeval *nowP);
 static int  check_referer(httpd_conn *hc);
 #ifdef CONFIG_THTTPD_URLPATTERN
 static int  really_check_referer(httpd_conn *hc);
@@ -226,6 +225,10 @@ static pid_t  main_thread;
 static int    str_alloc_count = 0;
 static size_t str_alloc_size  = 0;
 
+/* This is the 'root' of the Filesystem as seen by the HTTP client */
+
+static const char httpd_root[] = CONFIG_THTTPD_PATH;
+
 /* Include MIME encodings and types */
 
 #include "mime_types.h"
@@ -738,7 +741,7 @@ static int auth_check(httpd_conn *hc, char *dirname)
   else
 #endif
     {
-      topdir = CONFIG_THTTPD_PATH;
+      topdir = httpd_root;
     }
 
   switch (auth_check2(hc, topdir))
@@ -1248,12 +1251,16 @@ static char *expand_filename(char *path, char **restP, boolean tildemapped)
   static char *rest;
   static size_t maxchecked = 0, maxrest = 0;
   size_t checkedlen, restlen, prevcheckedlen, prevrestlen;
+#if 0 // REVISIT
   struct stat sb;
+#endif
   int nlinks, i;
   char *r;
   char *cp1;
   char *cp2;
 
+  nvdbg("path: \"%s\"\n", path);
+#if 0 // REVISIT
   /* We need to do the pathinfo check.  we do a single stat() of the whole
    * filename - if it exists, then we return it as is with nothing in restP.
    * If it doesn't exist, we fall through to the existing code.
@@ -1278,12 +1285,36 @@ static char *expand_filename(char *path, char **restP, boolean tildemapped)
       *restP = rest;
       return checked;
     }
+#endif
+
+  /* Handle leading /, or . or by copying the default directory into checked */
+
+  if (path[0] == '.' || (path[0] == '/' && strncmp(path, httpd_root, strlen(httpd_root)) != 0))
+    {
+      /* Start out with httpd_root in checked */
+ 
+      checkedlen = strlen(httpd_root) + 1;
+      httpd_realloc_str(&checked, &maxchecked, checkedlen);
+      strcpy(checked, httpd_root);
+
+      /* Skip over leading '.' */
+
+      if (path[0] == '.')
+        {
+          path++;
+        }
+    }
+  else
+    {
+      /* Start out with nothing in checked */
 
-  /* Start out with nothing in checked and the whole filename in rest. */
+      httpd_realloc_str(&checked, &maxchecked, 1);
+      checked[0] = '\0';
+      checkedlen = 0;
+    }       
+
+  /* Copy the whole filename (minus the leading '.') into rest. */
 
-  httpd_realloc_str(&checked, &maxchecked, 1);
-  checked[0] = '\0';
-  checkedlen = 0;
   restlen = strlen(path);
   httpd_realloc_str(&rest, &maxrest, restlen);
   (void)strcpy(rest, path);
@@ -1295,17 +1326,6 @@ static char *expand_filename(char *path, char **restP, boolean tildemapped)
       rest[--restlen] = '\0';
     }
 
-  if (!tildemapped)
-    {
-      /* Remove any leading slashes. */
-
-      while (rest[0] == '/')
-        {
-          (void)strcpy(rest, &(rest[1]));
-          --restlen;
-        }
-    }
-
   r = rest;
   nlinks = 0;
 
@@ -1316,7 +1336,7 @@ static char *expand_filename(char *path, char **restP, boolean tildemapped)
       /* Save current checkedlen.  Save current restlen in case we get a non-existant component. */
 
       prevcheckedlen = checkedlen;
-      prevrestlen = restlen;
+      prevrestlen    = restlen;
 
       /* Grab one component from r and transfer it to checked. */
 
@@ -1418,8 +1438,10 @@ static char *expand_filename(char *path, char **restP, boolean tildemapped)
   *restP = r;
   if (checked[0] == '\0')
     {
-      (void)strcpy(checked, CONFIG_THTTPD_PATH);
+      (void)strcpy(checked, httpd_root);
     }
+
+  nvdbg("checked: \"%s\"\n", checked);
   return checked;
 }
 
@@ -2108,11 +2130,11 @@ static void create_environment(httpd_conn *hc)
       (void)snprintf(buf, sizeof(buf), "/%s", hc->pathinfo);
       setenv("PATH_INFO", buf, TRUE);
 
-      l = strlen(CONFIG_THTTPD_PATH) + strlen(hc->pathinfo) + 1;
+      l = strlen(httpd_root) + strlen(hc->pathinfo) + 1;
       cp2 = NEW(char, l);
       if (cp2)
         {
-          (void)snprintf(cp2, l, "%s%s", CONFIG_THTTPD_PATH, hc->pathinfo);
+          (void)snprintf(cp2, l, "%s%s", httpd_root, hc->pathinfo);
           setenv("PATH_TRANSLATED", cp2, TRUE);
         }
     }
@@ -2890,761 +2912,460 @@ static int cgi(httpd_conn *hc)
 }
 #endif
 
-static int really_start_request(httpd_conn *hc, struct timeval *nowP)
+/* Returns 1 if ok to serve the url, 0 if not. */
+
+static int check_referer(httpd_conn *hc)
 {
-  static char *indexname;
-  static size_t maxindexname = 0;
-  static const char *index_names[] = { CONFIG_THTTPD_INDEX_NAMES };
-  int i;
-#ifdef CONFIG_THTTPD_AUTH_FILE
-  static char *dirname;
-  static size_t maxdirname = 0;
-#endif                                 /* CONFIG_THTTPD_AUTH_FILE */
-  size_t expnlen, indxlen;
-  char *cp;
-  char *pi;
+  /* Are we doing referer checking at all? */
 
-  expnlen = strlen(hc->expnfilename);
+#ifdef CONFIG_THTTPD_URLPATTERN
+  int r;
+  char *cp;
 
-  if (hc->method != METHOD_GET && hc->method != METHOD_HEAD &&
-      hc->method != METHOD_POST)
-    {
-      NOTIMPLEMENTED("really start");
-      httpd_send_err(hc, 501, err501title, "", err501form,
-                     httpd_method_str(hc->method));
-      return -1;
-    }
+  child = really_check_referer(hc);
 
-  /* Stat the file. */
-  if (stat(hc->expnfilename, &hc->sb) < 0)
+  if (!r)
     {
-      INTERNALERROR(hc->expnfilename);
-      httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
-      return -1;
-    }
+#ifdef CONFIG_THTTPD_VHOST
+      if (hc->vhostname != NULL)
+        {
+          cp = hc->vhostname;
+        }
+      else
+#endif
+        {
+          cp = hc->hs->hostname;
+        }
 
-  /* Is it world-readable or world-executable? We check explicitly instead
-   * of just trying to open it, so that no one ever gets surprised by a file 
-   * that's not set world-readable and yet somehow is readable by the HTTP
-   * server and therefore the *whole* world.
-   */
+      if (cp == NULL)
+        {
+          cp = "";
+        }
 
-  if (!(hc->sb.st_mode & (S_IROTH | S_IXOTH)))
-    {
-      ndbg("%s URL \"%s\" resolves to a non world-readable file\n",
-             httpd_ntoa(&hc->client_addr), hc->encodedurl);
+      ndbg("%s non-local referer \"%s%s\" \"%s\"\n",
+             httpd_ntoa(&hc->client_addr), cp, hc->encodedurl, hc->referer);
       httpd_send_err(hc, 403, err403title, "",
                      ERROR_FORM(err403form,
-                                "The requested URL '%s' resolves to a file that is not world-readable.\n"),
+                                "You must supply a local referer to get URL '%s' from this server.\n"),
                      hc->encodedurl);
-      return -1;
     }
+  return r;
+#else
+  return 1;
+#endif
+}
 
-  /* Is it a directory? */
+/* Returns 1 if ok to serve the url, 0 if not. */
 
-  if (S_ISDIR(hc->sb.st_mode))
+#ifdef CONFIG_THTTPD_URLPATTERN
+static int really_check_referer(httpd_conn *hc)
+{
+  httpd_server *hs;
+  char *cp1;
+  char *cp2;
+  char *cp3;
+  static char *refhost = (char *)0;
+  static size_t refhost_size = 0;
+  char *lp;
+
+  hs = hc->hs;
+
+  /* Check for an empty referer. */
+
+  if (hc->referer == NULL || hc->referer[0] == '\0' ||
+      (cp1 = strstr(hc->referer, "//")) == NULL)
     {
-      /* If there's pathinfo, it's just a non-existent file. */
+      /* Disallow if the url matches. */
 
-      if (hc->pathinfo[0] != '\0')
+      if (match(CONFIG_THTTPD_URLPATTERN, hc->origfilename))
         {
-          httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl);
-          return -1;
+          return 0;
         }
 
-      /* Special handling for directory URLs that don't end in a slash. We
-       * send back an explicit redirect with the slash, because otherwise
-       * many clients can't build relative URLs properly.
-       */
+      /* Otherwise ok. */
 
-      if (strcmp(hc->origfilename, "") != 0 &&
-          strcmp(hc->origfilename, ".") != 0 &&
-          hc->origfilename[strlen(hc->origfilename) - 1] != '/')
-        {
-          send_dirredirect(hc);
-          return -1;
-        }
+      return 1;
+    }
 
-      /* Check for an index file. */
+  /* Extract referer host. */
 
-      for (i = 0; i < sizeof(index_names) / sizeof(char *); ++i)
-        {
-          httpd_realloc_str(&indexname, &maxindexname,
-                            expnlen + 1 + strlen(index_names[i]));
-          (void)strcpy(indexname, hc->expnfilename);
-          indxlen = strlen(indexname);
-          if (indxlen == 0 || indexname[indxlen - 1] != '/')
-            {
-              (void)strcat(indexname, "/");
-            }
+  cp1 += 2;
+  for (cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2)
+    {
+      continue;
+    }
 
-          if (strcmp(indexname, "./") == 0)
-            {
-              indexname[0] = '\0';
-            }
+  httpd_realloc_str(&refhost, &refhost_size, cp2 - cp1);
+  for (cp3 = refhost; cp1 < cp2; ++cp1, ++cp3)
+    if (isupper(*cp1))
+      {
+        *cp3 = tolower(*cp1);
+      }
+    else
+      {
+        *cp3 = *cp1;
+      }
+  *cp3 = '\0';
 
-          (void)strcat(indexname, index_names[i]);
-          if (stat(indexname, &hc->sb) >= 0)
-            {
-              goto got_one;
-            }
-        }
+  /* Local pattern? */
 
-      /* Nope, no index file, so it's an actual directory request. */
-#ifdef CONFIG_THTTPD_GENERATE_INDICES
-      /* Directories must be readable for indexing. */
-      if (!(hc->sb.st_mode & S_IROTH))
-        {
-          ndbg("%s URL \"%s\" tried to index a directory with indexing disabled\n",
-                 httpd_ntoa(&hc->client_addr), hc->encodedurl);
-          httpd_send_err(hc, 403, err403title, "",
-                         ERROR_FORM(err403form,
-                                    "The requested URL '%s' resolves to a directory that has indexing disabled.\n"),
-                         hc->encodedurl);
-          return -1;
-        }
-#  ifdef CONFIG_THTTPD_AUTH_FILE
-      /* Check authorization for this directory. */
+#ifdef CONFIG_THTTPD_LOCALPATTERN
+  lp = CONFIG_THTTPD_LOCALPATTERN;
+#else
 
-      if (auth_check(hc, hc->expnfilename) == -1)
-        {
-          return -1;
-        }
-#  endif /* CONFIG_THTTPD_AUTH_FILE */
+  /* No local pattern.  What's our hostname? */
 
-      /* Referer check. */
+#ifndef CONFIG_THTTPD_VHOST
+  /* Not vhosting, use the server name. */
 
-      if (!check_referer(hc))
-        {
-          return -1;
-        }
+  lp = hs->hostname;
+  if (!lp)
+    {
+      /* Couldn't figure out local hostname - give up. */
 
-      /* Ok, generate an index. */
-      return ls(hc);
-#else
-      ndbg("%s URL \"%s\" tried to index a directory\n",
-             httpd_ntoa(&hc->client_addr), hc->encodedurl);
-      httpd_send_err(hc, 403, err403title, "",
-                     ERROR_FORM(err403form,
-                                "The requested URL '%s' is a directory, and directory indexing is disabled on this server.\n"),
-                     hc->encodedurl);
-      return -1;
-#endif
+     return 1;
+    }
 
-    got_one:
+#else
+  /* We are vhosting, use the hostname on this connection. */
 
-      /* Got an index file.  Expand again.  More pathinfo means
-       * something went wrong.
+  lp = hc->vhostname;
+  if (!lp)
+    {
+      /* Oops, no hostname.  Maybe it's an old browser that doesn't
+       * send a Host: header.  We could figure out the default
+       * hostname for this IP address, but it's not worth it for the
+       * few requests like this.
        */
 
-      cp = expand_filename(indexname, &pi, hc->tildemapped);
-      if (cp == (char *)0 || pi[0] != '\0')
-        {
-          INTERNALERROR(indexname);
-          httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
-          return -1;
-        }
+      return 1;
+    }
+#endif
+#endif /* CONFIG_THTTPD_LOCALPATTERN */
 
-      expnlen = strlen(cp);
-      httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, expnlen);
-      (void)strcpy(hc->expnfilename, cp);
+  /* If the referer host doesn't match the local host pattern, and the
+   * filename does match the url pattern, it's an illegal reference.
+   */
 
-      /* Now, is the index version world-readable or world-executable? */
+#ifdef CONFIG_THTTPD_URLPATTERN
+  if (!match(lp, refhost) && match(CONFIG_THTTPD_URLPATTERN, hc->origfilename))
+    {
+      return 0;
+    }
+#endif
 
-      if (!(hc->sb.st_mode & (S_IROTH | S_IXOTH)))
-        {
-          ndbg("%s URL \"%s\" resolves to a non-world-readable index file\n",
-                 httpd_ntoa(&hc->client_addr), hc->encodedurl);
-          httpd_send_err(hc, 403, err403title, "",
-                         ERROR_FORM(err403form,
-                                    "The requested URL '%s' resolves to an index file that is not world-readable.\n"),
-                         hc->encodedurl);
-          return -1;
-        }
-    }
+  /* Otherwise ok. */
 
-  /* Check authorization for this directory. */
+  return 1;
+}
+#endif
 
-#ifdef CONFIG_THTTPD_AUTH_FILE
-  httpd_realloc_str(&dirname, &maxdirname, expnlen);
-  (void)strcpy(dirname, hc->expnfilename);
-  cp = strrchr(dirname, '/');
-  if (!cp)
+#ifdef CONFIG_DEBUG
+static int sockaddr_check(httpd_sockaddr *saP)
+{
+  switch (saP->sin_family)
     {
-      (void)strcpy(dirname, CONFIG_THTTPD_PATH);
+    case AF_INET:
+      return 1;
+
+#ifdef  CONFIG_NET_IPv6
+    case AF_INET6:
+      return 1;
+#endif
+
+    default:
+      return 0;
     }
-  else
+}
+#endif
+
+static size_t sockaddr_len(httpd_sockaddr *saP)
+{
+  switch (saP->sin_family)
     {
-      *cp = '\0';
+    case AF_INET:
+      return sizeof(struct sockaddr_in);
+
+#ifdef  CONFIG_NET_IPv6
+    case AF_INET6:
+      return sizeof(struct sockaddr_in6);
+#endif
+
+    default:
+      break;
     }
+  return 0;
+}
 
-  if (auth_check(hc, dirname) == -1)
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+FAR httpd_server *httpd_initialize(FAR httpd_sockaddr *sa)
+{
+  FAR httpd_server *hs;
+
+  /* Save the PID of the main thread */
+
+  main_thread = getpid();
+
+  /* Allocate the server structure */
+
+  hs = (FAR httpd_server *)zalloc(sizeof(httpd_server));
+  if (!hs)
     {
-      return -1;
+      ndbg("out of memory allocating an httpd_server\n");
+      return NULL;
     }
 
-  /* Check if the filename is the CONFIG_THTTPD_AUTH_FILE itself - that's verboten. */
+#ifdef CONFIG_THTTPD_HOSTNAME
+  hs->hostname = strdup(CONFIG_THTTPD_HOSTNAME);
+#else
+  hs->hostname = strdup(httpd_ntoa(sa));
+#endif
+  nvdbg("hostname: %s\n", hs->hostname);
 
-  if (expnlen == sizeof(CONFIG_THTTPD_AUTH_FILE) - 1)
+  if (!hs->hostname)
     {
-      if (strcmp(hc->expnfilename, CONFIG_THTTPD_AUTH_FILE) == 0)
-        {
-          ndbg("%s URL \"%s\" tried to retrieve an auth file\n",
-                 httpd_ntoa(&hc->client_addr), hc->encodedurl);
-          httpd_send_err(hc, 403, err403title, "",
-                         ERROR_FORM(err403form,
-                                    "The requested URL '%s' is an authorization file, retrieving it is not permitted.\n"),
-                         hc->encodedurl);
-          return -1;
-        }
+      ndbg("out of memory copying hostname\n");
+      return NULL;
     }
-  else if (expnlen >= sizeof(CONFIG_THTTPD_AUTH_FILE) &&
-           strcmp(&(hc->expnfilename[expnlen - sizeof(CONFIG_THTTPD_AUTH_FILE) + 1]),
-                  CONFIG_THTTPD_AUTH_FILE) == 0 &&
-           hc->expnfilename[expnlen - sizeof(CONFIG_THTTPD_AUTH_FILE)] == '/')
+
+  hs->cgi_count = 0;
+
+  /* Initialize listen sockets */
+
+  hs->listen_fd = initialize_listen_socket(sa);
+  if (hs->listen_fd == -1)
     {
-      ndbg("%s URL \"%s\" tried to retrieve an auth file\n",
-             httpd_ntoa(&hc->client_addr), hc->encodedurl);
-      httpd_send_err(hc, 403, err403title, "",
-                     ERROR_FORM(err403form,
-                                "The requested URL '%s' is an authorization file, retrieving it is not permitted.\n"),
-                     hc->encodedurl);
-      return -1;
+      ndbg("Failed to create listen socket\n");
+      free_httpd_server(hs);
+      return NULL;
     }
-#endif
 
-  /* Referer check. */
+  init_mime();
 
-  if (!check_referer(hc))
-    return -1;
+  /* Done initializing. */
 
-  /* Is it in the CGI area? */
+  ndbg("%s starting on port %d\n", CONFIG_THTTPD_SERVER_SOFTWARE, (int)CONFIG_THTTPD_PORT);
+  return hs;
+}
 
-#ifdef CONFIG_THTTPD_CGI_PATTERN
-  if (match(CONFIG_THTTPD_CGI_PATTERN, hc->expnfilename))
+void httpd_terminate(httpd_server * hs)
+{
+  httpd_unlisten(hs);
+  free_httpd_server(hs);
+}
+
+void httpd_unlisten(httpd_server * hs)
+{
+  if (hs->listen_fd != -1)
     {
-      return cgi(hc);
+      (void)close(hs->listen_fd);
+      hs->listen_fd = -1;
     }
-#endif
+}
 
-  /* It's not CGI.  If it's executable or there's pathinfo, someone's trying 
-   * to either serve or run a non-CGI file as CGI.  Either case is
-   * prohibited.
-   */
+/* Send the buffered response. */
 
-  if (hc->sb.st_mode & S_IXOTH)
+void httpd_write_response(httpd_conn *hc)
+{
+  /* If we are in a sub-process, turn off no-delay mode. */
+
+  if (main_thread != getpid())
     {
-      ndbg("%s URL \"%s\" is executable but isn't CGI\n",
-             httpd_ntoa(&hc->client_addr), hc->encodedurl);
-      httpd_send_err(hc, 403, err403title, "",
-                     ERROR_FORM(err403form,
-                                "The requested URL '%s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n"),
-                     hc->encodedurl);
-      return -1;
+       httpd_clear_ndelay(hc->conn_fd);
     }
 
-  if (hc->pathinfo[0] != '\0')
+  /* Send the response, if necessary. */
+
+  if (hc->buflen > 0)
     {
-      ndbg("%s URL \"%s\" has pathinfo but isn't CGI\n",
-             httpd_ntoa(&hc->client_addr), hc->encodedurl);
-      httpd_send_err(hc, 403, err403title, "",
-                     ERROR_FORM(err403form,
-                                "The requested URL '%s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n"),
-                     hc->encodedurl);
-      return -1;
+      (void)httpd_write(hc->conn_fd, hc->buffer, hc->buflen);
+      hc->buflen = 0;
     }
+}
 
-  /* Fill in range_end, if necessary. */
+/* Set no-delay / non-blocking mode on a socket. */
 
-  if (hc->got_range &&
-      (hc->range_end == -1 || hc->range_end >= hc->sb.st_size))
+void httpd_set_ndelay(int fd)
+{
+  int flags, newflags;
+
+  flags = fcntl(fd, F_GETFL, 0);
+  if (flags != -1)
     {
-      hc->range_end = hc->sb.st_size - 1;
+      newflags = flags | (int)O_NDELAY;
+      if (newflags != flags)
+        (void)fcntl(fd, F_SETFL, newflags);
     }
+}
 
-  figure_mime(hc);
+/* Clear no-delay / non-blocking mode on a socket. */
 
-  if (hc->method == METHOD_HEAD)
+void httpd_clear_ndelay(int fd)
+{
+  int flags, newflags;
+
+  flags = fcntl(fd, F_GETFL, 0);
+  if (flags != -1)
     {
-      send_mime(hc, 200, ok200title, hc->encodings, "", hc->type,
-                hc->sb.st_size, hc->sb.st_mtime);
+      newflags = flags & ~(int)O_NDELAY;
+      if (newflags != flags)
+        {
+          (void)fcntl(fd, F_SETFL, newflags);
+        }
     }
-  else if (hc->if_modified_since != (time_t) - 1 &&
-           hc->if_modified_since >= hc->sb.st_mtime)
+}
+
+void httpd_realloc_str(char **strP, size_t * maxsizeP, size_t size)
+{
+  if (*maxsizeP == 0)
     {
-      send_mime(hc, 304, err304title, hc->encodings, "", hc->type, (off_t) - 1,
-                hc->sb.st_mtime);
+      *maxsizeP       = MAX(CONFIG_THTTPD_IOBUFFERSIZE, size + CONFIG_THTTPD_REALLOCINCR);
+      *strP           = NEW(char, *maxsizeP + 1);
+      ++str_alloc_count;
+      str_alloc_size += *maxsizeP;
+    }
+  else if (size > *maxsizeP)
+    {
+      str_alloc_size -= *maxsizeP;
+      *maxsizeP       = MAX(*maxsizeP * 2, size * 5 / 4);
+      *strP = RENEW(*strP, char, *maxsizeP + 1);
+      str_alloc_size += *maxsizeP;
     }
   else
     {
-      hc->file_fd = open(hc->expnfilename, O_RDONLY);
-      if (!hc->file_fd < 0)
-        {
-          INTERNALERROR(hc->expnfilename);
-          httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
-          return -1;
-        }
-      send_mime(hc, 200, ok200title, hc->encodings, "", hc->type,
-                hc->sb.st_size, hc->sb.st_mtime);
+      return;
     }
 
-  return 0;
+  if (!*strP)
+    {
+      ndbg("out of memory reallocating a string to %d bytes\n", *maxsizeP);
+      exit(1);
+    }
 }
 
-/* Returns 1 if ok to serve the url, 0 if not. */
-
-static int check_referer(httpd_conn *hc)
+void httpd_send_err(httpd_conn *hc, int status, const char *title, const char *extraheads,
+                    const char *form, const char *arg)
 {
-  /* Are we doing referer checking at all? */
+#ifdef CONFIG_THTTPD_ERROR_DIRECTORY
+  char filename[1000];
 
-#ifdef CONFIG_THTTPD_URLPATTERN
-  int r;
-  char *cp;
+  /* Try virtual host error page. */
 
-  child = really_check_referer(hc);
+  ndbg("title: \"%s\" form: \"%s\"\n", title, form);
 
-  if (!r)
-    {
 #ifdef CONFIG_THTTPD_VHOST
-      if (hc->vhostname != NULL)
-        {
-          cp = hc->vhostname;
-        }
-      else
-#endif
-        {
-          cp = hc->hs->hostname;
-        }
-
-      if (cp == NULL)
-        {
-          cp = "";
-        }
-
-      ndbg("%s non-local referer \"%s%s\" \"%s\"\n",
-             httpd_ntoa(&hc->client_addr), cp, hc->encodedurl, hc->referer);
-      httpd_send_err(hc, 403, err403title, "",
-                     ERROR_FORM(err403form,
-                                "You must supply a local referer to get URL '%s' from this server.\n"),
-                     hc->encodedurl);
-    }
-  return r;
-#else
-  return 1;
-#endif
-}
-
-/* Returns 1 if ok to serve the url, 0 if not. */
-
-#ifdef CONFIG_THTTPD_URLPATTERN
-static int really_check_referer(httpd_conn *hc)
-{
-  httpd_server *hs;
-  char *cp1;
-  char *cp2;
-  char *cp3;
-  static char *refhost = (char *)0;
-  static size_t refhost_size = 0;
-  char *lp;
-
-  hs = hc->hs;
-
-  /* Check for an empty referer. */
-
-  if (hc->referer == NULL || hc->referer[0] == '\0' ||
-      (cp1 = strstr(hc->referer, "//")) == NULL)
+  if (hc->hostdir[0] != '\0')
     {
-      /* Disallow if the url matches. */
-
-      if (match(CONFIG_THTTPD_URLPATTERN, hc->origfilename))
+      (void)snprintf(filename, sizeof(filename),
+                     "%s/%s/err%d.html", hc->hostdir, CONFIG_THTTPD_ERROR_DIRECTORY, status);
+      if (send_err_file(hc, status, title, extraheads, filename))
         {
-          return 0;
+          nvdbg("Sent VHOST error file\n");
+          return;
         }
-
-      /* Otherwise ok. */
-
-      return 1;
     }
+#endif
 
-  /* Extract referer host. */
+  /* Try server-wide error page. */
 
-  cp1 += 2;
-  for (cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2)
+  (void)snprintf(filename, sizeof(filename), "%s/err%d.html", CONFIG_THTTPD_ERROR_DIRECTORY, status);
+  if (send_err_file(hc, status, title, extraheads, filename))
     {
-      continue;
+      nvdbg("Sent server-wide error page\n");
+      return;
     }
 
-  httpd_realloc_str(&refhost, &refhost_size, cp2 - cp1);
-  for (cp3 = refhost; cp1 < cp2; ++cp1, ++cp3)
-    if (isupper(*cp1))
-      {
-        *cp3 = tolower(*cp1);
-      }
-    else
-      {
-        *cp3 = *cp1;
-      }
-  *cp3 = '\0';
-
-  /* Local pattern? */
-
-#ifdef CONFIG_THTTPD_LOCALPATTERN
-  lp = CONFIG_THTTPD_LOCALPATTERN;
-#else
-
-  /* No local pattern.  What's our hostname? */
-
-#ifndef CONFIG_THTTPD_VHOST
-  /* Not vhosting, use the server name. */
-
-  lp = hs->hostname;
-  if (!lp)
-    {
-      /* Couldn't figure out local hostname - give up. */
+  /* Fall back on built-in error page. */
 
-     return 1;
-    }
+  send_response(hc, status, title, extraheads, form, arg);
 
 #else
-  /* We are vhosting, use the hostname on this connection. */
-
-  lp = hc->vhostname;
-  if (!lp)
-    {
-      /* Oops, no hostname.  Maybe it's an old browser that doesn't
-       * send a Host: header.  We could figure out the default
-       * hostname for this IP address, but it's not worth it for the
-       * few requests like this.
-       */
-
-      return 1;
-    }
-#endif
-#endif /* CONFIG_THTTPD_LOCALPATTERN */
 
-  /* If the referer host doesn't match the local host pattern, and the
-   * filename does match the url pattern, it's an illegal reference.
-   */
+  send_response(hc, status, title, extraheads, form, arg);
 
-#ifdef CONFIG_THTTPD_URLPATTERN
-  if (!match(lp, refhost) && match(CONFIG_THTTPD_URLPATTERN, hc->origfilename))
-    {
-      return 0;
-    }
 #endif
-
-  /* Otherwise ok. */
-
-  return 1;
 }
-#endif
 
-#ifdef CONFIG_DEBUG
-static int sockaddr_check(httpd_sockaddr *saP)
+const char *httpd_method_str(int method)
 {
-  switch (saP->sin_family)
+  switch (method)
     {
-    case AF_INET:
-      return 1;
-
-#ifdef  CONFIG_NET_IPv6
-    case AF_INET6:
-      return 1;
-#endif
-
-    default:
-      return 0;
-    }
-}
-#endif
+    case METHOD_GET:
+      return "GET";
 
-static size_t sockaddr_len(httpd_sockaddr *saP)
-{
-  switch (saP->sin_family)
-    {
-    case AF_INET:
-      return sizeof(struct sockaddr_in);
+    case METHOD_HEAD:
+      return "HEAD";
 
-#ifdef  CONFIG_NET_IPv6
-    case AF_INET6:
-      return sizeof(struct sockaddr_in6);
-#endif
+    case METHOD_POST:
+      return "POST";
 
     default:
-      break;
+      return "UNKNOWN";
     }
-  return 0;
 }
 
-/****************************************************************************
- * Public Functions
- ****************************************************************************/
-
-FAR httpd_server *httpd_initialize(FAR httpd_sockaddr *sa)
+int httpd_get_conn(httpd_server *hs, int listen_fd, httpd_conn *hc)
 {
-  FAR httpd_server *hs;
-
-  /* Save the PID of the main thread */
-
-  main_thread = getpid();
-
-  /* Allocate the server structure */
+  httpd_sockaddr sa;
+  socklen_t sz;
 
-  hs = (FAR httpd_server *)zalloc(sizeof(httpd_server));
-  if (!hs)
+  if (!hc->initialized)
     {
-      ndbg("out of memory allocating an httpd_server\n");
-      return NULL;
+      hc->read_size = 0;
+      httpd_realloc_str(&hc->read_buf, &hc->read_size, CONFIG_THTTPD_IOBUFFERSIZE);
+      hc->maxdecodedurl =
+        hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings =
+        hc->maxpathinfo = hc->maxquery = hc->maxaccept =
+        hc->maxaccepte = hc->maxreqhost = hc->maxhostdir =
+        hc->maxremoteuser = 0;
+#ifdef CONFIG_THTTPD_TILDE_MAP2
+      hc->maxaltdir = 0;
+#endif
+      httpd_realloc_str(&hc->decodedurl, &hc->maxdecodedurl, 1);
+      httpd_realloc_str(&hc->origfilename, &hc->maxorigfilename, 1);
+      httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, 0);
+      httpd_realloc_str(&hc->encodings, &hc->maxencodings, 0);
+      httpd_realloc_str(&hc->pathinfo, &hc->maxpathinfo, 0);
+      httpd_realloc_str(&hc->query, &hc->maxquery, 0);
+      httpd_realloc_str(&hc->accept, &hc->maxaccept, 0);
+      httpd_realloc_str(&hc->accepte, &hc->maxaccepte, 0);
+      httpd_realloc_str(&hc->reqhost, &hc->maxreqhost, 0);
+      httpd_realloc_str(&hc->hostdir, &hc->maxhostdir, 0);
+      httpd_realloc_str(&hc->remoteuser, &hc->maxremoteuser, 0);
+#ifdef CONFIG_THTTPD_TILDE_MAP2
+      httpd_realloc_str(&hc->altdir, &hc->maxaltdir, 0);
+#endif
+      hc->initialized = 1;
     }
 
-#ifdef CONFIG_THTTPD_HOSTNAME
-  hs->hostname = strdup(CONFIG_THTTPD_HOSTNAME);
-#else
-  hs->hostname = strdup(httpd_ntoa(sa));
-#endif
-  nvdbg("hostname: %s\n", hs->hostname);
+  /* Accept the new connection. */
 
-  if (!hs->hostname)
+  nvdbg("accept() new connection on listen_fd %d\n", listen_fd);
+  sz = sizeof(sa);
+  hc->conn_fd = accept(listen_fd, (struct sockaddr*)&sa, &sz);
+  if (hc->conn_fd < 0)
     {
-      ndbg("out of memory copying hostname\n");
-      return NULL;
-    }
-
-  hs->cgi_count = 0;
+      if (errno == EWOULDBLOCK)
+        {
+          return GC_NO_MORE;
+        }
 
-  /* Initialize listen sockets */
+      ndbg("accept failed: %d\n", errno);
+      return GC_FAIL;
+    }
 
-  hs->listen_fd = initialize_listen_socket(sa);
-  if (hs->listen_fd == -1)
+#ifdef CONFIG_DEBUG
+  if (!sockaddr_check(&sa))
     {
-      ndbg("Failed to create listen socket\n");
-      free_httpd_server(hs);
-      return NULL;
+      ndbg("unknown sockaddr family\n");
+      close(hc->conn_fd);
+      hc->conn_fd = -1;
+      return GC_FAIL;
     }
-
-  init_mime();
-
-  /* Done initializing. */
-
-  ndbg("%s starting on port %d\n", CONFIG_THTTPD_SERVER_SOFTWARE, (int)CONFIG_THTTPD_PORT);
-  return hs;
-}
-
-void httpd_terminate(httpd_server * hs)
-{
-  httpd_unlisten(hs);
-  free_httpd_server(hs);
-}
-
-void httpd_unlisten(httpd_server * hs)
-{
-  if (hs->listen_fd != -1)
-    {
-      (void)close(hs->listen_fd);
-      hs->listen_fd = -1;
-    }
-}
-
-/* Send the buffered response. */
-
-void httpd_write_response(httpd_conn *hc)
-{
-  /* If we are in a sub-process, turn off no-delay mode. */
-
-  if (main_thread != getpid())
-    {
-       httpd_clear_ndelay(hc->conn_fd);
-    }
-
-  /* Send the response, if necessary. */
-
-  if (hc->buflen > 0)
-    {
-      (void)httpd_write(hc->conn_fd, hc->buffer, hc->buflen);
-      hc->buflen = 0;
-    }
-}
-
-/* Set no-delay / non-blocking mode on a socket. */
-
-void httpd_set_ndelay(int fd)
-{
-  int flags, newflags;
-
-  flags = fcntl(fd, F_GETFL, 0);
-  if (flags != -1)
-    {
-      newflags = flags | (int)O_NDELAY;
-      if (newflags != flags)
-        (void)fcntl(fd, F_SETFL, newflags);
-    }
-}
-
-/* Clear no-delay / non-blocking mode on a socket. */
-
-void httpd_clear_ndelay(int fd)
-{
-  int flags, newflags;
-
-  flags = fcntl(fd, F_GETFL, 0);
-  if (flags != -1)
-    {
-      newflags = flags & ~(int)O_NDELAY;
-      if (newflags != flags)
-        {
-          (void)fcntl(fd, F_SETFL, newflags);
-        }
-    }
-}
-
-void httpd_realloc_str(char **strP, size_t * maxsizeP, size_t size)
-{
-  if (*maxsizeP == 0)
-    {
-      *maxsizeP       = MAX(CONFIG_THTTPD_IOBUFFERSIZE, size + CONFIG_THTTPD_REALLOCINCR);
-      *strP           = NEW(char, *maxsizeP + 1);
-      ++str_alloc_count;
-      str_alloc_size += *maxsizeP;
-    }
-  else if (size > *maxsizeP)
-    {
-      str_alloc_size -= *maxsizeP;
-      *maxsizeP       = MAX(*maxsizeP * 2, size * 5 / 4);
-      *strP = RENEW(*strP, char, *maxsizeP + 1);
-      str_alloc_size += *maxsizeP;
-    }
-  else
-    {
-      return;
-    }
-
-  if (!*strP)
-    {
-      ndbg("out of memory reallocating a string to %d bytes\n", *maxsizeP);
-      exit(1);
-    }
-}
-
-void httpd_send_err(httpd_conn *hc, int status, const char *title, const char *extraheads,
-                    const char *form, const char *arg)
-{
-#ifdef CONFIG_THTTPD_ERROR_DIRECTORY
-  char filename[1000];
-
-  /* Try virtual host error page. */
-
-  ndbg("title: \"%s\" form: \"%s\"\n", title, form);
-
-#ifdef CONFIG_THTTPD_VHOST
-  if (hc->hostdir[0] != '\0')
-    {
-      (void)snprintf(filename, sizeof(filename),
-                     "%s/%s/err%d.html", hc->hostdir, CONFIG_THTTPD_ERROR_DIRECTORY, status);
-      if (send_err_file(hc, status, title, extraheads, filename))
-        {
-          nvdbg("Sent VHOST error file\n");
-          return;
-        }
-    }
-#endif
-
-  /* Try server-wide error page. */
-
-  (void)snprintf(filename, sizeof(filename), "%s/err%d.html", CONFIG_THTTPD_ERROR_DIRECTORY, status);
-  if (send_err_file(hc, status, title, extraheads, filename))
-    {
-      nvdbg("Sent server-wide error page\n");
-      return;
-    }
-
-  /* Fall back on built-in error page. */
-
-  send_response(hc, status, title, extraheads, form, arg);
-
-#else
-
-  send_response(hc, status, title, extraheads, form, arg);
-
-#endif
-}
-
-const char *httpd_method_str(int method)
-{
-  switch (method)
-    {
-    case METHOD_GET:
-      return "GET";
-
-    case METHOD_HEAD:
-      return "HEAD";
-
-    case METHOD_POST:
-      return "POST";
-
-    default:
-      return "UNKNOWN";
-    }
-}
-
-int httpd_get_conn(httpd_server *hs, int listen_fd, httpd_conn *hc)
-{
-  httpd_sockaddr sa;
-  socklen_t sz;
-
-  if (!hc->initialized)
-    {
-      hc->read_size = 0;
-      httpd_realloc_str(&hc->read_buf, &hc->read_size, CONFIG_THTTPD_IOBUFFERSIZE);
-      hc->maxdecodedurl =
-        hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings =
-        hc->maxpathinfo = hc->maxquery = hc->maxaccept =
-        hc->maxaccepte = hc->maxreqhost = hc->maxhostdir =
-        hc->maxremoteuser = 0;
-#ifdef CONFIG_THTTPD_TILDE_MAP2
-      hc->maxaltdir = 0;
-#endif
-      httpd_realloc_str(&hc->decodedurl, &hc->maxdecodedurl, 1);
-      httpd_realloc_str(&hc->origfilename, &hc->maxorigfilename, 1);
-      httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, 0);
-      httpd_realloc_str(&hc->encodings, &hc->maxencodings, 0);
-      httpd_realloc_str(&hc->pathinfo, &hc->maxpathinfo, 0);
-      httpd_realloc_str(&hc->query, &hc->maxquery, 0);
-      httpd_realloc_str(&hc->accept, &hc->maxaccept, 0);
-      httpd_realloc_str(&hc->accepte, &hc->maxaccepte, 0);
-      httpd_realloc_str(&hc->reqhost, &hc->maxreqhost, 0);
-      httpd_realloc_str(&hc->hostdir, &hc->maxhostdir, 0);
-      httpd_realloc_str(&hc->remoteuser, &hc->maxremoteuser, 0);
-#ifdef CONFIG_THTTPD_TILDE_MAP2
-      httpd_realloc_str(&hc->altdir, &hc->maxaltdir, 0);
-#endif
-      hc->initialized = 1;
-    }
-
-  /* Accept the new connection. */
-
-  nvdbg("accept() new connection on listen_fd %d\n", listen_fd);
-  sz = sizeof(sa);
-  hc->conn_fd = accept(listen_fd, (struct sockaddr*)&sa, &sz);
-  if (hc->conn_fd < 0)
-    {
-      if (errno == EWOULDBLOCK)
-        {
-          return GC_NO_MORE;
-        }
-
-      ndbg("accept failed: %d\n", errno);
-      return GC_FAIL;
-    }
-
-#ifdef CONFIG_DEBUG
-  if (!sockaddr_check(&sa))
-    {
-      ndbg("unknown sockaddr family\n");
-      close(hc->conn_fd);
-      hc->conn_fd = -1;
-      return GC_FAIL;
-    }
-#endif
+#endif
 
   hc->hs = hs;
   (void)memset(&hc->client_addr, 0, sizeof(hc->client_addr));
@@ -4039,7 +3760,7 @@ int httpd_parse_request(httpd_conn *hc)
 
   if (hc->origfilename[0] == '\0')
     {
-      (void)strcpy(hc->origfilename, CONFIG_THTTPD_PATH);
+      (void)strcpy(hc->origfilename, ".");
     }
 
   /* Extract query string from encoded URL. */
@@ -4275,188 +3996,473 @@ int httpd_parse_request(httpd_conn *hc)
             }
 #endif
         }
-    }
-
-  if (hc->one_one)
-    {
-      /* Check that HTTP/1.1 requests specify a host, as required. */
+    }
+
+  if (hc->one_one)
+    {
+      /* Check that HTTP/1.1 requests specify a host, as required. */
+
+      if (hc->reqhost[0] == '\0' && hc->hdrhost[0] == '\0')
+        {
+          BADREQUEST("reqhost-3");
+          httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, "");
+          return -1;
+        }
+
+      /* If the client wants to do keep-alives, it might also be doing
+       * pipelining.  There's no way for us to tell.  Since we don't
+       * implement keep-alives yet, if we close such a connection there
+       * might be unread pipelined requests waiting.  So, we have to do a
+       * lingering close.
+       */
+
+      if (hc->keep_alive)
+        {
+          hc->should_linger = TRUE;
+        }
+    }
+
+  /* Ok, the request has been parsed.  Now we resolve stuff that may require 
+   * the entire request.
+   */
+
+  /* Copy original filename to expanded filename. */
+
+  httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename,
+                    strlen(hc->origfilename));
+  (void)strcpy(hc->expnfilename, hc->origfilename);
+
+  /* Tilde mapping. */
+
+  if (hc->expnfilename[0] == '~')
+    {
+#ifdef CONFIG_THTTPD_TILDE_MAP1
+      if (!tilde_map_1(hc))
+        {
+          httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl);
+          return -1;
+        }
+#endif                                 /*CONFIG_THTTPD_TILDE_MAP1 */
+#ifdef CONFIG_THTTPD_TILDE_MAP2
+      if (!tilde_map_2(hc))
+        {
+          httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl);
+          return -1;
+        }
+#endif                                 /*CONFIG_THTTPD_TILDE_MAP2 */
+    }
+
+  /* Virtual host mapping. */
+
+#ifdef CONFIG_THTTPD_VHOST
+    if (!vhost_map(hc))
+      {
+        INTERNALERROR("VHOST");
+        httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
+        return -1;
+      }
+#endif
+
+  /* Expand the filename */
+
+  cp = expand_filename(hc->expnfilename, &pi, hc->tildemapped);
+  if (!cp)
+    {
+      INTERNALERROR(hc->expnfilename);
+      httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
+      return -1;
+    }
+
+  httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, strlen(cp));
+  (void)strcpy(hc->expnfilename, cp);
+  httpd_realloc_str(&hc->pathinfo, &hc->maxpathinfo, strlen(pi));
+  (void)strcpy(hc->pathinfo, pi);
+  nvdbg("expnfilename: \"%s\" pathinfo: \"%s\"\n", hc->expnfilename, hc->pathinfo);
+
+  /* Remove pathinfo stuff from the original filename too. */
+
+  if (hc->pathinfo[0] != '\0')
+    {
+      int i;
+      i = strlen(hc->origfilename) - strlen(hc->pathinfo);
+      if (i > 0 && strcmp(&hc->origfilename[i], hc->pathinfo) == 0)
+        {
+          hc->origfilename[i - 1] = '\0';
+        }
+    }
+
+  /* If the expanded filename is an absolute path, check that it's still
+   * within the current directory or the alternate directory.
+   */
+
+  if (hc->expnfilename[0] == '/')
+    {
+      if (strncmp(hc->expnfilename, httpd_root, strlen(httpd_root)) == 0)
+        {
+        }
+#ifdef CONFIG_THTTPD_TILDE_MAP2
+      else if (hc->altdir[0] != '\0' &&
+               (strncmp(hc->expnfilename, hc->altdir, strlen(hc->altdir)) == 0 &&
+                (hc->expnfilename[strlen(hc->altdir)] == '\0' ||
+                 hc->expnfilename[strlen(hc->altdir)] == '/')))
+        {
+        }
+#endif
+      else
+        {
+          ndbg("%s URL \"%s\" goes outside the web tree\n",
+                 httpd_ntoa(&hc->client_addr), hc->encodedurl);
+          httpd_send_err(hc, 403, err403title, "",
+                         ERROR_FORM(err403form,
+                                    "The requested URL '%s' resolves to a file outside the permitted web server directory tree.\n"),
+                         hc->encodedurl);
+          return -1;
+        }
+    }
+
+  return 0;
+}
+
+void httpd_close_conn(httpd_conn *hc, struct timeval *nowP)
+{
+  if (hc->file_fd)
+    {
+      (void)close(hc->file_fd);
+      hc->file_fd = -1;
+    }
+
+  if (hc->conn_fd >= 0)
+    {
+      (void)close(hc->conn_fd);
+      hc->conn_fd = -1;
+    }
+}
+
+void httpd_destroy_conn(httpd_conn *hc)
+{
+  if (hc->initialized)
+    {
+      free((void *)hc->read_buf);
+      free((void *)hc->decodedurl);
+      free((void *)hc->origfilename);
+      free((void *)hc->expnfilename);
+      free((void *)hc->encodings);
+      free((void *)hc->pathinfo);
+      free((void *)hc->query);
+      free((void *)hc->accept);
+      free((void *)hc->accepte);
+      free((void *)hc->reqhost);
+      free((void *)hc->hostdir);
+      free((void *)hc->remoteuser);
+      free((void *)hc->buffer);
+#ifdef CONFIG_THTTPD_TILDE_MAP2
+      free((void *)hc->altdir);
+#endif                                 /*CONFIG_THTTPD_TILDE_MAP2 */
+      hc->initialized = 0;
+    }
+}
+
+int httpd_start_request(httpd_conn *hc, struct timeval *nowP)
+{
+  static char *indexname;
+  static size_t maxindexname = 0;
+  static const char *index_names[] = { CONFIG_THTTPD_INDEX_NAMES };
+  int i;
+#ifdef CONFIG_THTTPD_AUTH_FILE
+  static char *dirname;
+  static size_t maxdirname = 0;
+#endif                                 /* CONFIG_THTTPD_AUTH_FILE */
+  size_t expnlen, indxlen;
+  char *cp;
+  char *pi;
+
+  nvdbg("File: \"%s\"\n", hc->expnfilename);
+  expnlen = strlen(hc->expnfilename);
+
+  if (hc->method != METHOD_GET && hc->method != METHOD_HEAD &&
+      hc->method != METHOD_POST)
+    {
+      NOTIMPLEMENTED("start");
+      httpd_send_err(hc, 501, err501title, "", err501form,
+                     httpd_method_str(hc->method));
+      return -1;
+    }
+
+  /* Stat the file. */
+
+  if (stat(hc->expnfilename, &hc->sb) < 0)
+    {
+      INTERNALERROR(hc->expnfilename);
+      httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
+      return -1;
+    }
+
+  /* Is it world-readable or world-executable? We check explicitly instead
+   * of just trying to open it, so that no one ever gets surprised by a file 
+   * that's not set world-readable and yet somehow is readable by the HTTP
+   * server and therefore the *whole* world.
+   */
+
+  if (!(hc->sb.st_mode & (S_IROTH | S_IXOTH)))
+    {
+      ndbg("%s URL \"%s\" resolves to a non world-readable file\n",
+           httpd_ntoa(&hc->client_addr), hc->encodedurl);
+      httpd_send_err(hc, 403, err403title, "",
+                     ERROR_FORM(err403form,
+                                "The requested URL '%s' resolves to a file that is not world-readable.\n"),
+                     hc->encodedurl);
+      return -1;
+    }
+
+  /* Is it a directory? */
+
+  if (S_ISDIR(hc->sb.st_mode))
+    {
+      /* If there's pathinfo, it's just a non-existent file. */
+
+      if (hc->pathinfo[0] != '\0')
+        {
+          httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl);
+          return -1;
+        }
+
+      /* Special handling for directory URLs that don't end in a slash. We
+       * send back an explicit redirect with the slash, because otherwise
+       * many clients can't build relative URLs properly.
+       */
+
+      if (strcmp(hc->origfilename, "") != 0 &&
+          strcmp(hc->origfilename, ".") != 0 &&
+          hc->origfilename[strlen(hc->origfilename) - 1] != '/')
+        {
+          send_dirredirect(hc);
+          return -1;
+        }
+
+      /* Check for an index file. */
+
+      for (i = 0; i < sizeof(index_names) / sizeof(char *); ++i)
+        {
+          httpd_realloc_str(&indexname, &maxindexname,
+                            expnlen + 1 + strlen(index_names[i]));
+          (void)strcpy(indexname, hc->expnfilename);
+          indxlen = strlen(indexname);
+          if (indxlen == 0 || indexname[indxlen - 1] != '/')
+            {
+              (void)strcat(indexname, "/");
+            }
+
+          if (strcmp(indexname, "./") == 0)
+            {
+              indexname[0] = '\0';
+            }
+
+          (void)strcat(indexname, index_names[i]);
+          if (stat(indexname, &hc->sb) >= 0)
+            {
+              goto got_one;
+            }
+        }
+
+      /* Nope, no index file, so it's an actual directory request. */
+#ifdef CONFIG_THTTPD_GENERATE_INDICES
+      /* Directories must be readable for indexing. */
+      if (!(hc->sb.st_mode & S_IROTH))
+        {
+          ndbg("%s URL \"%s\" tried to index a directory with indexing disabled\n",
+                 httpd_ntoa(&hc->client_addr), hc->encodedurl);
+          httpd_send_err(hc, 403, err403title, "",
+                         ERROR_FORM(err403form,
+                                    "The requested URL '%s' resolves to a directory that has indexing disabled.\n"),
+                         hc->encodedurl);
+          return -1;
+        }
+#  ifdef CONFIG_THTTPD_AUTH_FILE
+      /* Check authorization for this directory. */
 
-      if (hc->reqhost[0] == '\0' && hc->hdrhost[0] == '\0')
+      if (auth_check(hc, hc->expnfilename) == -1)
         {
-          BADREQUEST("reqhost-3");
-          httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, "");
           return -1;
         }
+#  endif /* CONFIG_THTTPD_AUTH_FILE */
 
-      /* If the client wants to do keep-alives, it might also be doing
-       * pipelining.  There's no way for us to tell.  Since we don't
-       * implement keep-alives yet, if we close such a connection there
-       * might be unread pipelined requests waiting.  So, we have to do a
-       * lingering close.
-       */
+      /* Referer check. */
 
-      if (hc->keep_alive)
+      if (!check_referer(hc))
         {
-          hc->should_linger = TRUE;
+          return -1;
         }
-    }
-
-  /* Ok, the request has been parsed.  Now we resolve stuff that may require 
-   * the entire request.
-   */
 
-  /* Copy original filename to expanded filename. */
+      /* Ok, generate an index. */
+      return ls(hc);
+#else
+      ndbg("%s URL \"%s\" tried to index a directory\n",
+             httpd_ntoa(&hc->client_addr), hc->encodedurl);
+      httpd_send_err(hc, 403, err403title, "",
+                     ERROR_FORM(err403form,
+                                "The requested URL '%s' is a directory, and directory indexing is disabled on this server.\n"),
+                     hc->encodedurl);
+      return -1;
+#endif
 
-  httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename,
-                    strlen(hc->origfilename));
-  (void)strcpy(hc->expnfilename, hc->origfilename);
+    got_one:
 
-  /* Tilde mapping. */
+      /* Got an index file.  Expand again.  More pathinfo means
+       * something went wrong.
+       */
 
-  if (hc->expnfilename[0] == '~')
-    {
-#ifdef CONFIG_THTTPD_TILDE_MAP1
-      if (!tilde_map_1(hc))
+      cp = expand_filename(indexname, &pi, hc->tildemapped);
+      if (cp == (char *)0 || pi[0] != '\0')
         {
-          httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl);
+          INTERNALERROR(indexname);
+          httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
           return -1;
         }
-#endif                                 /*CONFIG_THTTPD_TILDE_MAP1 */
-#ifdef CONFIG_THTTPD_TILDE_MAP2
-      if (!tilde_map_2(hc))
+
+      expnlen = strlen(cp);
+      httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, expnlen);
+      (void)strcpy(hc->expnfilename, cp);
+
+      /* Now, is the index version world-readable or world-executable? */
+
+      if (!(hc->sb.st_mode & (S_IROTH | S_IXOTH)))
         {
-          httpd_send_err(hc, 404, err404title, "", err404form, hc->encodedurl);
+          ndbg("%s URL \"%s\" resolves to a non-world-readable index file\n",
+                 httpd_ntoa(&hc->client_addr), hc->encodedurl);
+          httpd_send_err(hc, 403, err403title, "",
+                         ERROR_FORM(err403form,
+                                    "The requested URL '%s' resolves to an index file that is not world-readable.\n"),
+                         hc->encodedurl);
           return -1;
         }
-#endif                                 /*CONFIG_THTTPD_TILDE_MAP2 */
     }
 
-  /* Virtual host mapping. */
-
-#ifdef CONFIG_THTTPD_VHOST
-    if (!vhost_map(hc))
-      {
-        INTERNALERROR("VHOST");
-        httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
-        return -1;
-      }
-#endif
-
-  /* Expand all symbolic links in the filename.  This also gives us any
-   * trailing non-existing components, for pathinfo.
-   */
+  /* Check authorization for this directory. */
 
-  cp = expand_filename(hc->expnfilename, &pi, hc->tildemapped);
+#ifdef CONFIG_THTTPD_AUTH_FILE
+  httpd_realloc_str(&dirname, &maxdirname, expnlen);
+  (void)strcpy(dirname, hc->expnfilename);
+  cp = strrchr(dirname, '/');
   if (!cp)
     {
-      INTERNALERROR(hc->expnfilename);
-      httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
-      return -1;
+      (void)strcpy(dirname, httpd_root);
+    }
+  else
+    {
+      *cp = '\0';
     }
 
-  httpd_realloc_str(&hc->expnfilename, &hc->maxexpnfilename, strlen(cp));
-  (void)strcpy(hc->expnfilename, cp);
-  httpd_realloc_str(&hc->pathinfo, &hc->maxpathinfo, strlen(pi));
-  (void)strcpy(hc->pathinfo, pi);
-
-  /* Remove pathinfo stuff from the original filename too. */
-
-  if (hc->pathinfo[0] != '\0')
+  if (auth_check(hc, dirname) == -1)
     {
-      int i;
-      i = strlen(hc->origfilename) - strlen(hc->pathinfo);
-      if (i > 0 && strcmp(&hc->origfilename[i], hc->pathinfo) == 0)
-        {
-          hc->origfilename[i - 1] = '\0';
-        }
+      return -1;
     }
 
-  /* If the expanded filename is an absolute path, check that it's still
-   * within the current directory or the alternate directory.
-   */
+  /* Check if the filename is the CONFIG_THTTPD_AUTH_FILE itself - that's verboten. */
 
-  if (hc->expnfilename[0] == '/')
+  if (expnlen == sizeof(CONFIG_THTTPD_AUTH_FILE) - 1)
     {
-      if (strncmp(hc->expnfilename, CONFIG_THTTPD_PATH, strlen(CONFIG_THTTPD_PATH)) == 0)
-        {
-          /* Elide the current directory. */
-
-          (void)strcpy(hc->expnfilename, &hc->expnfilename[strlen(CONFIG_THTTPD_PATH)]);
-        }
-#ifdef CONFIG_THTTPD_TILDE_MAP2
-      else if (hc->altdir[0] != '\0' &&
-               (strncmp(hc->expnfilename, hc->altdir,
-                        strlen(hc->altdir)) == 0 &&
-                (hc->expnfilename[strlen(hc->altdir)] == '\0' ||
-                 hc->expnfilename[strlen(hc->altdir)] == '/')))
-        {
-        }
-#endif
-      else
+      if (strcmp(hc->expnfilename, CONFIG_THTTPD_AUTH_FILE) == 0)
         {
-          ndbg("%s URL \"%s\" goes outside the web tree\n",
+          ndbg("%s URL \"%s\" tried to retrieve an auth file\n",
                  httpd_ntoa(&hc->client_addr), hc->encodedurl);
           httpd_send_err(hc, 403, err403title, "",
                          ERROR_FORM(err403form,
-                                    "The requested URL '%s' resolves to a file outside the permitted web server directory tree.\n"),
+                                    "The requested URL '%s' is an authorization file, retrieving it is not permitted.\n"),
                          hc->encodedurl);
           return -1;
         }
     }
+  else if (expnlen >= sizeof(CONFIG_THTTPD_AUTH_FILE) &&
+           strcmp(&(hc->expnfilename[expnlen - sizeof(CONFIG_THTTPD_AUTH_FILE) + 1]),
+                  CONFIG_THTTPD_AUTH_FILE) == 0 &&
+           hc->expnfilename[expnlen - sizeof(CONFIG_THTTPD_AUTH_FILE)] == '/')
+    {
+      ndbg("%s URL \"%s\" tried to retrieve an auth file\n",
+             httpd_ntoa(&hc->client_addr), hc->encodedurl);
+      httpd_send_err(hc, 403, err403title, "",
+                     ERROR_FORM(err403form,
+                                "The requested URL '%s' is an authorization file, retrieving it is not permitted.\n"),
+                     hc->encodedurl);
+      return -1;
+    }
+#endif
 
-  return 0;
-}
+  /* Referer check. */
 
-void httpd_close_conn(httpd_conn *hc, struct timeval *nowP)
-{
-  if (hc->file_fd)
+  if (!check_referer(hc))
+    return -1;
+
+  /* Is it in the CGI area? */
+
+#ifdef CONFIG_THTTPD_CGI_PATTERN
+  if (match(CONFIG_THTTPD_CGI_PATTERN, hc->expnfilename))
     {
-      (void)close(hc->file_fd);
-      hc->file_fd = -1;
+      return cgi(hc);
     }
+#endif
 
-  if (hc->conn_fd >= 0)
+  /* It's not CGI.  If it's executable or there's pathinfo, someone's trying 
+   * to either serve or run a non-CGI file as CGI.  Either case is
+   * prohibited.
+   */
+
+  if (hc->sb.st_mode & S_IXOTH)
     {
-      (void)close(hc->conn_fd);
-      hc->conn_fd = -1;
+      ndbg("%s URL \"%s\" is executable but isn't CGI\n",
+             httpd_ntoa(&hc->client_addr), hc->encodedurl);
+      httpd_send_err(hc, 403, err403title, "",
+                     ERROR_FORM(err403form,
+                                "The requested URL '%s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n"),
+                     hc->encodedurl);
+      return -1;
     }
-}
 
-void httpd_destroy_conn(httpd_conn *hc)
-{
-  if (hc->initialized)
+  if (hc->pathinfo[0] != '\0')
     {
-      free((void *)hc->read_buf);
-      free((void *)hc->decodedurl);
-      free((void *)hc->origfilename);
-      free((void *)hc->expnfilename);
-      free((void *)hc->encodings);
-      free((void *)hc->pathinfo);
-      free((void *)hc->query);
-      free((void *)hc->accept);
-      free((void *)hc->accepte);
-      free((void *)hc->reqhost);
-      free((void *)hc->hostdir);
-      free((void *)hc->remoteuser);
-      free((void *)hc->buffer);
-#ifdef CONFIG_THTTPD_TILDE_MAP2
-      free((void *)hc->altdir);
-#endif                                 /*CONFIG_THTTPD_TILDE_MAP2 */
-      hc->initialized = 0;
+      ndbg("%s URL \"%s\" has pathinfo but isn't CGI\n",
+             httpd_ntoa(&hc->client_addr), hc->encodedurl);
+      httpd_send_err(hc, 403, err403title, "",
+                     ERROR_FORM(err403form,
+                                "The requested URL '%s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n"),
+                     hc->encodedurl);
+      return -1;
     }
-}
 
-int httpd_start_request(httpd_conn *hc, struct timeval *nowP)
-{
-  int r;
+  /* Fill in range_end, if necessary. */
 
-  /* Really start the request. */
+  if (hc->got_range &&
+      (hc->range_end == -1 || hc->range_end >= hc->sb.st_size))
+    {
+      hc->range_end = hc->sb.st_size - 1;
+    }
 
-  r = really_start_request(hc, nowP);
+  figure_mime(hc);
 
-  /* And return the status. */
+  if (hc->method == METHOD_HEAD)
+    {
+      send_mime(hc, 200, ok200title, hc->encodings, "", hc->type,
+                hc->sb.st_size, hc->sb.st_mtime);
+    }
+  else if (hc->if_modified_since != (time_t) - 1 &&
+           hc->if_modified_since >= hc->sb.st_mtime)
+    {
+      send_mime(hc, 304, err304title, hc->encodings, "", hc->type, (off_t) - 1,
+                hc->sb.st_mtime);
+    }
+  else
+    {
+      hc->file_fd = open(hc->expnfilename, O_RDONLY);
+      if (!hc->file_fd < 0)
+        {
+          INTERNALERROR(hc->expnfilename);
+          httpd_send_err(hc, 500, err500title, "", err500form, hc->encodedurl);
+          return -1;
+        }
+      send_mime(hc, 200, ok200title, hc->encodings, "", hc->type,
+                hc->sb.st_size, hc->sb.st_mtime);
+    }
 
-  return r;
+  return 0;
 }
 
 char *httpd_ntoa(httpd_sockaddr *saP)
diff --git a/netutils/thttpd/tdate_parse.c b/netutils/thttpd/tdate_parse.c
index f1e657029a..2403ea6ca6 100644
--- a/netutils/thttpd/tdate_parse.c
+++ b/netutils/thttpd/tdate_parse.c
@@ -45,6 +45,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <time.h>
+#include <debug.h>
 
 #include "tdate_parse.h"
 
@@ -170,6 +171,7 @@ static int scan_mon(char *str_mon, long *tm_monP)
 
 time_t tdate_parse(char *str)
 {
+#if 0 // REVISIT -- doesn't work
   struct tm tm;
   char *cp;
   char str_mon[32];
@@ -184,7 +186,10 @@ time_t tdate_parse(char *str)
   long tm_wday;
 #endif
 
+  nvdbg("str: \"%s\"\n", str);
+
   /* Initialize. */
+
   (void)memset((char *)&tm, 0, sizeof(struct tm));
 
   /* Skip initial whitespace ourselves - sscanf is clumsy at this. */
@@ -315,5 +320,8 @@ time_t tdate_parse(char *str)
     }
 
   return mktime(&tm);
+#else
+  return 0; // for now
+#endif
 }
 
-- 
GitLab