- use RESERVE_CONFIG_BUFFER. For defconfig this gives:
[oweals/busybox.git] / networking / httpd.c
index ba65e13611c5521451e3859d6347f55c7ec03307..6e80fd9bed9440b2556b8928f0108ed82386891c 100644 (file)
@@ -2,7 +2,7 @@
  * httpd implementation for busybox
  *
  * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
- * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru>
  *
  * simplify patch stolen from libbb without using strdup
  *
  *  foo=`httpd -d $foo`           # decode "Hello%20World" as "Hello World"
  *  bar=`httpd -e "<Hello World>"`  # encode as "&#60Hello&#32World&#62"
  * Note that url encoding for arguments is not the same as html encoding for
- * presenation.  -d decodes a url-encoded argument while -e encodes in html
+ * presentation.  -d decodes a url-encoded argument while -e encodes in html
  * for page display.
  *
  * httpd.conf has the following format:
- * 
+ *
  * A:172.20.         # Allow address from 172.20.0.0/16
  * A:10.0.0.0/25     # Allow any address from 10.0.0.0-10.0.0.127
  * A:10.0.0.0/255.255.255.128  # Allow any address that previous set
  * /adm:admin:setup  # Require user admin, pwd setup on urls starting with /adm/
  * /adm:toor:PaSsWd  # or user toor, pwd PaSsWd on urls starting with /adm/
  * .au:audio/basic   # additional mime type for audio.au files
- * 
- * A/D may be as a/d or allow/deny - first char case unsensitive
+ * *.php:/path/php   # running cgi.php scripts through an interpreter
+ *
+ * A/D may be as a/d or allow/deny - first char case insensitive
  * Deny IP rules take precedence over allow rules.
- * 
- * 
+ *
+ *
  * The Deny/Allow IP logic:
- * 
+ *
  *  - Default is to allow all.  No addresses are denied unless
- *        denied with a D: rule.
+ *         denied with a D: rule.
  *  - Order of Deny/Allow rules is significant
  *  - Deny rules take precedence over allow rules.
  *  - If a deny all rule (D:*) is used it acts as a catch-all for unmatched
- *      addresses.
+ *       addresses.
  *  - Specification of Allow all (A:*) is a no-op
- * 
+ *
  * Example:
  *   1. Allow only specified addresses
  *     A:172.20          # Allow any address that begins with 172.20.
  *     A:10.10.          # Allow any address that begins with 10.10.
  *     A:127.0.0.1       # Allow local loopback connections
  *     D:*               # Deny from other IP connections
- * 
+ *
  *   2. Only deny specified addresses
  *     D:1.2.3.        # deny from 1.2.3.0 - 1.2.3.255
  *     D:2.3.4.        # deny from 2.3.4.0 - 2.3.4.255
  *     A:*             # (optional line added for clarity)
- * 
+ *
  * If a sub directory contains a config file it is parsed and merged with
  * any existing settings as if it was appended to the original configuration.
  *
  * subdir http request, any merge is discarded when the process exits.  As a
  * result, the subdir settings only have a lifetime of a single request.
  *
- * 
- * If -c is not set, an attempt will be made to open the default 
+ *
+ * If -c is not set, an attempt will be made to open the default
  * root configuration file.  If -c is set and the file is not found, the
  * server exits with an error.
- * 
+ *
 */
 
 
 #include "busybox.h"
 
 
-static const char httpdVersion[] = "busybox httpd/1.34 2-Oct-2003";
+static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004";
 static const char default_path_httpd_conf[] = "/etc";
 static const char httpd_conf[] = "httpd.conf";
 static const char home[] = "./";
 
 #ifdef CONFIG_LFS
 # define cont_l_fmt "%lld"
+# define cont_l_type (long long)
 #else
 # define cont_l_fmt "%ld"
+# define cont_l_type (long)
 #endif
 
-// Note: bussybox xfuncs are not used because we want the server to keep running
+#define TIMEOUT 60
+
+// Note: busybox xfuncs are not used because we want the server to keep running
 //       if something bad happens due to a malformed user request.
 //       As a result, all memory allocation after daemonize
 //       is checked rigorously
@@ -166,6 +171,15 @@ static const char home[] = "./";
 #undef CONFIG_FEATURE_HTTPD_CGI
 #undef CONFIG_FEATURE_HTTPD_SETUID
 #undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+#undef ENABLE_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+#undef ENABLE_FEATURE_HTTPD_BASIC_AUTH
+#undef ENABLE_FEATURE_HTTPD_AUTH_MD5
+#undef ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+#undef ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+#undef ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+#undef ENABLE_FEATURE_HTTPD_CGI
+#undef ENABLE_FEATURE_HTTPD_SETUID
+#undef ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
 /* enable all features now */
 #define CONFIG_FEATURE_HTTPD_BASIC_AUTH
 #define CONFIG_FEATURE_HTTPD_AUTH_MD5
@@ -175,6 +189,15 @@ static const char home[] = "./";
 #define CONFIG_FEATURE_HTTPD_CGI
 #define CONFIG_FEATURE_HTTPD_SETUID
 #define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
+#define ENABLE_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY  0
+#define ENABLE_FEATURE_HTTPD_BASIC_AUTH             1
+#define ENABLE_FEATURE_HTTPD_AUTH_MD5               1
+#define ENABLE_FEATURE_HTTPD_ENCODE_URL_STR         1
+#define ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV 1
+#define ENABLE_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES 1
+#define ENABLE_FEATURE_HTTPD_CGI                    1
+#define ENABLE_FEATURE_HTTPD_SETUID                 1
+#define ENABLE_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP   1
 
 /* require from libbb.a for linking */
 const char *bb_applet_name = "httpd";
@@ -194,6 +217,10 @@ void bb_show_usage(void)
 #undef DEBUG
 #endif
 
+#ifndef DEBUG
+# define DEBUG 0
+#endif
+
 #define MAX_MEMORY_BUFF 8192    /* IO buffer */
 
 typedef struct HT_ACCESS {
@@ -213,25 +240,26 @@ typedef struct
 {
   char buf[MAX_MEMORY_BUFF];
 
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-  const char *realm;
-  char *remoteuser;
-#endif
+  USE_FEATURE_HTTPD_BASIC_AUTH(const char *realm;)
+  USE_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
 
-#ifdef CONFIG_FEATURE_HTTPD_CGI
-  char *referer;
-#endif
+  const char *query;
+
+  USE_FEATURE_HTTPD_CGI(char *referer;)
 
   const char *configFile;
 
   unsigned int rmt_ip;
-#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
   char rmt_ip_str[16];     /* for set env REMOTE_ADDR */
 #endif
   unsigned port;           /* server initial port and for
                              set env REMOTE_PORT */
+  union HTTPD_FOUND {
+       const char *found_mime_type;
+       const char *found_moved_temporarily;
+  } httpd_found;
 
-  const char *found_mime_type;
   off_t ContentLength;          /* -1 - unknown */
   time_t last_mod;
 
@@ -246,12 +274,16 @@ typedef struct
 
 #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
   int accepted_socket;
-#define a_c_r config->accepted_socket
-#define a_c_w config->accepted_socket
-  int debugHttpd;          /* if seted, don`t stay daemon */
+# define a_c_r config->accepted_socket
+# define a_c_w config->accepted_socket
 #else
-#define a_c_r 0
-#define a_c_w 1
+# define a_c_r 0
+# define a_c_w 1
+#endif
+  volatile int alarm_signaled;
+
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+  Htaccess *script_i;           /* config script interpreters */
 #endif
 } HttpdConfig;
 
@@ -260,7 +292,7 @@ static HttpdConfig *config;
 static const char request_GET[] = "GET";    /* size algorithic optimize */
 
 static const char* const suffixTable [] = {
-/* Warning: shorted equalent suffix in one line must be first */
+/* Warning: shorted equivalent suffix in one line must be first */
   ".htm.html", "text/html",
   ".jpg.jpeg", "image/jpeg",
   ".gif", "image/gif",
@@ -284,11 +316,13 @@ static const char* const suffixTable [] = {
 typedef enum
 {
   HTTP_OK = 200,
+  HTTP_MOVED_TEMPORARILY = 302,
+  HTTP_BAD_REQUEST = 400,       /* malformed syntax */
   HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
   HTTP_NOT_FOUND = 404,
-  HTTP_NOT_IMPLEMENTED = 501,   /* used for unrecognized requests */
-  HTTP_BAD_REQUEST = 400,       /* malformed syntax */
   HTTP_FORBIDDEN = 403,
+  HTTP_REQUEST_TIMEOUT = 408,
+  HTTP_NOT_IMPLEMENTED = 501,   /* used for unrecognized requests */
   HTTP_INTERNAL_SERVER_ERROR = 500,
 #if 0 /* future use */
   HTTP_CONTINUE = 100,
@@ -299,7 +333,6 @@ typedef enum
   HTTP_NO_CONTENT = 204,
   HTTP_MULTIPLE_CHOICES = 300,
   HTTP_MOVED_PERMANENTLY = 301,
-  HTTP_MOVED_TEMPORARILY = 302,
   HTTP_NOT_MODIFIED = 304,
   HTTP_PAYMENT_REQUIRED = 402,
   HTTP_BAD_GATEWAY = 502,
@@ -316,7 +349,10 @@ typedef struct
 } HttpEnumString;
 
 static const HttpEnumString httpResponseNames[] = {
-  { HTTP_OK, "OK" },
+  { HTTP_OK, "OK", NULL },
+  { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
+  { HTTP_REQUEST_TIMEOUT, "Request Timeout",
+    "No request appeared within a reasonable time period." },
   { HTTP_NOT_IMPLEMENTED, "Not Implemented",
     "The requested method is not recognized by this server." },
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
@@ -334,7 +370,6 @@ static const HttpEnumString httpResponseNames[] = {
   { HTTP_NO_CONTENT, "No Content" },
   { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
   { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
-  { HTTP_MOVED_TEMPORARILY, "Moved Temporarily" },
   { HTTP_NOT_MODIFIED, "Not Modified" },
   { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
   { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
@@ -447,7 +482,7 @@ static void free_config_lines(Htaccess **pprev)
  > $Function: parse_conf()
  *
  * $Description: parse configuration file into in-memory linked list.
- * 
+ *
  * The first non-white character is examined to determine if the config line
  * is one of the following:
  *    .ext:mime/type   # new mime type not compiled into httpd
@@ -464,7 +499,7 @@ static void free_config_lines(Htaccess **pprev)
  *                              checks.
  *      (int) flag  . . . . . . the source of the parse request.
  *
- * $Return: (None) 
+ * $Return: (None)
  *
  ****************************************************************************/
 static void parse_conf(const char *path, int flag)
@@ -494,7 +529,7 @@ static void parse_conf(const char *path, int flag)
 
     config->flg_deny_all = 0;
 
-#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
+#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR)
     /* retain previous auth and mime config only for subdir parse */
     if(flag != SUBDIR_PARSE) {
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
@@ -502,6 +537,9 @@ static void parse_conf(const char *path, int flag)
 #endif
 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
        free_config_lines(&config->mime_a);
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+       free_config_lines(&config->script_i);
 #endif
     }
 #endif
@@ -565,6 +603,9 @@ static void parse_conf(const char *path, int flag)
 #endif
 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
           && *p0 != '.'
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+          && *p0 != '*'
 #endif
          )
               continue;
@@ -637,7 +678,7 @@ static void parse_conf(const char *path, int flag)
        }
 #endif
 
-#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
+#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR)
        /* storing current config line */
        cur = calloc(1, sizeof(Htaccess) + strlen(p0));
        if(cur) {
@@ -653,6 +694,14 @@ static void parse_conf(const char *path, int flag)
                continue;
            }
 #endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+           if(*cf == '*' && cf[1] == '.') {
+               /* config script interpreter line move top for overwrite previous */
+               cur->next = config->script_i;
+               config->script_i = cur;
+               continue;
+           }
+#endif
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
            free(p0);
            if(prev == NULL) {
@@ -661,7 +710,7 @@ static void parse_conf(const char *path, int flag)
            } else {
                /* sort path, if current lenght eq or bigger then move up */
                Htaccess *prev_hti = config->auth;
-               int l = strlen(cf);
+               size_t l = strlen(cf);
                Htaccess *hti;
 
                for(hti = prev_hti; hti; hti = hti->next) {
@@ -714,7 +763,7 @@ static char *encodeString(const char *string)
   /* take the simple route and encode everything */
   /* could possibly scan once to get length.     */
   int len = strlen(string);
-  char *out = malloc(len*5 +1);
+  char *out = malloc(len * 6 + 1);
   char *p=out;
   char ch;
 
@@ -722,7 +771,7 @@ static char *encodeString(const char *string)
   while ((ch = *string++)) {
     // very simple check for what to encode
     if (isalnum(ch)) *p++ = ch;
-    else p += sprintf(p, "&#%d", (unsigned char) ch);
+    else p += sprintf(p, "&#%d;", (unsigned char) ch);
   }
   *p=0;
   return out;
@@ -759,10 +808,21 @@ static char *decodeString(char *orig, int flag_plus_to_space)
     if (*ptr == '+' && flag_plus_to_space)    { *string++ = ' '; ptr++; }
     else if (*ptr != '%') *string++ = *ptr++;
     else  {
-      unsigned int value;
-      sscanf(ptr+1, "%2X", &value);
-      *string++ = value;
-      ptr += 3;
+      unsigned int value1, value2;
+
+      ptr++;
+      if(sscanf(ptr, "%1X", &value1) != 1 ||
+                               sscanf(ptr+1, "%1X", &value2) != 1) {
+       if(!flag_plus_to_space)
+               return NULL;
+       *string++ = '%';
+      } else {
+       value1 = value1 * 16 + value2;
+       if(value1 == '/' || value1 == 0)
+               return orig+1;
+       *string++ = value1;
+       ptr += 2;
+      }
     }
   }
   *string = '\0';
@@ -775,7 +835,7 @@ static char *decodeString(char *orig, int flag_plus_to_space)
  *
  > $Function: addEnv()
  *
- * $Description: Add an enviornment variable setting to the global list.
+ * $Description: Add an environment variable setting to the global list.
  *    A NAME=VALUE string is allocated, filled, and added to the list of
  *    environment settings passed to the cgi execution script.
  *
@@ -840,7 +900,7 @@ static void addEnvPort(const char *port_name)
 static void decodeBase64(char *Data)
 {
 
-  const unsigned char *in = Data;
+  const unsigned char *in = (const unsigned char *)Data;
   // The decoded size will be at most 3/4 the size of the encoded
   unsigned long ch = 0;
   int i = 0;
@@ -943,6 +1003,7 @@ static int sendHeaders(HttpResponseNum responseNum)
   char *buf = config->buf;
   const char *responseString = "";
   const char *infoString = 0;
+  const char *mime_type;
   unsigned int i;
   time_t timer = time(0);
   char timeStr[80];
@@ -956,16 +1017,16 @@ static int sendHeaders(HttpResponseNum responseNum)
                        break;
                }
   }
-  if (responseNum != HTTP_OK) {
-       config->found_mime_type = "text/html";  // error message is HTML
-  }
+  /* error message is HTML */
+  mime_type = responseNum == HTTP_OK ?
+               config->httpd_found.found_mime_type : "text/html";
 
   /* emit the current date */
   strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
   len = sprintf(buf,
-       "HTTP/1.0 %d %s\nContent-type: %s\r\n"
+       "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
        "Date: %s\r\nConnection: close\r\n",
-         responseNum, responseString, config->found_mime_type, timeStr);
+         responseNum, responseString, mime_type, timeStr);
 
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
   if (responseNum == HTTP_UNAUTHORIZED) {
@@ -973,10 +1034,17 @@ static int sendHeaders(HttpResponseNum responseNum)
                                                            config->realm);
   }
 #endif
+  if(responseNum == HTTP_MOVED_TEMPORARILY) {
+       len += sprintf(buf+len, "Location: %s/%s%s\r\n",
+               config->httpd_found.found_moved_temporarily,
+               (config->query ? "?" : ""),
+               (config->query ? config->query : ""));
+  }
+
   if (config->ContentLength != -1) {    /* file */
     strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
     len += sprintf(buf+len, "Last-Modified: %s\r\n%s " cont_l_fmt "\r\n",
-                             timeStr, Content_length, config->ContentLength);
+                             timeStr, Content_length, cont_l_type config->ContentLength);
   }
   strcat(buf, "\r\n");
   len += 2;
@@ -987,8 +1055,8 @@ static int sendHeaders(HttpResponseNum responseNum)
            responseNum, responseString,
            responseNum, responseString, infoString);
   }
-#ifdef DEBUG
-  if (config->debugHttpd) fprintf(stderr, "Headers: '%s'", buf);
+#if DEBUG
+  fprintf(stderr, "Headers: '%s'", buf);
 #endif
   return bb_full_write(a_c_w, buf, len);
 }
@@ -1035,7 +1103,6 @@ static int getLine(void)
  *
  * $Parameters:
  *      (const char *) url . . . . . . The requested URL (with leading /).
- *      (const char *urlArgs). . . . . Any URL arguments.
  *      (int bodyLen)  . . . . . . . . Length of the post body.
  *      (const char *cookie) . . . . . For set HTTP_COOKIE.
  *      (const char *content_type) . . For set CONTENT_TYPE.
@@ -1047,8 +1114,7 @@ static int getLine(void)
  *
  ****************************************************************************/
 static int sendCgi(const char *url,
-                  const char *request, const char *urlArgs,
-                  int bodyLen, const char *cookie,
+                  const char *request, int bodyLen, const char *cookie,
                   const char *content_type)
 {
   int fromCgi[2];  /* pipe for reading data from CGI */
@@ -1088,10 +1154,7 @@ static int sendCgi(const char *url,
 
       dup2(inFd, 0);  // replace stdin with the pipe
       dup2(outFd, 1);  // replace stdout with the pipe
-
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-      if (!config->debugHttpd)
-#endif
+      if(!DEBUG)
        dup2(outFd, 2);  // replace stderr with the pipe
 
       close(toCgi[0]);
@@ -1118,19 +1181,24 @@ static int sendCgi(const char *url,
       addEnv("PATH", "INFO", script);   /* set /PATH_INFO or NULL */
       addEnv("PATH",           "",         getenv("PATH"));
       addEnv("REQUEST",        "METHOD",   request);
-      if(urlArgs) {
-       char *uri = alloca(strlen(purl) + 2 + strlen(urlArgs));
+      if(config->query) {
+       char *uri = alloca(strlen(purl) + 2 + strlen(config->query));
        if(uri)
-           sprintf(uri, "%s?%s", purl, urlArgs);
+           sprintf(uri, "%s?%s", purl, config->query);
        addEnv("REQUEST",        "URI",   uri);
       } else {
        addEnv("REQUEST",        "URI",   purl);
       }
       if(script != NULL)
        *script = '\0';         /* reduce /PATH_INFO */
+       /* SCRIPT_FILENAME required by PHP in CGI mode */
+       if(realpath(purl + 1, realpath_buff))
+        addEnv("SCRIPT", "FILENAME", realpath_buff);
+       else
+        *realpath_buff = 0;
       /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
       addEnv("SCRIPT_NAME",    "",         purl);
-      addEnv("QUERY_STRING",   "",         urlArgs);
+      addEnv("QUERY_STRING",   "",         config->query);
       addEnv("SERVER",         "SOFTWARE", httpdVersion);
       addEnv("SERVER",         "PROTOCOL", "HTTP/1.0");
       addEnv("GATEWAY_INTERFACE", "",      "CGI/1.1");
@@ -1148,26 +1216,46 @@ static int sendCgi(const char *url,
        addEnv("HTTP", "COOKIE", cookie);
       if(content_type)
        addEnv("CONTENT", "TYPE", content_type);
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
       if(config->remoteuser) {
        addEnv("REMOTE", "USER", config->remoteuser);
        addEnv("AUTH_TYPE", "", "Basic");
       }
+#endif
       if(config->referer)
        addEnv("HTTP", "REFERER", config->referer);
 
        /* set execve argp[0] without path */
       argp[0] = strrchr( purl, '/' ) + 1;
        /* but script argp[0] must have absolute path and chdiring to this */
-      if(realpath(purl + 1, realpath_buff) != NULL) {
+      if(*realpath_buff) {
            script = strrchr(realpath_buff, '/');
            if(script) {
                *script = '\0';
                if(chdir(realpath_buff) == 0) {
-                 *script = '/';
                  // now run the program.  If it fails,
                  // use _exit() so no destructors
                  // get called and make a mess.
-                 execv(realpath_buff, argp);
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+                 char *interpr = NULL;
+                 char *suffix = strrchr(purl, '.');
+
+                 if(suffix) {
+                       Htaccess * cur;
+                       for (cur = config->script_i; cur; cur = cur->next)
+                               if(strcmp(cur->before_colon + 1, suffix) == 0) {
+                                       interpr = cur->after_colon;
+                                       break;
+                               }
+                 }
+#endif
+                 *script = '/';
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+                 if (interpr)
+                       execv(interpr, argp);
+                 else
+#endif
+                       execv(realpath_buff, argp);
                }
            }
       }
@@ -1222,13 +1310,11 @@ static int sendCgi(const char *url,
       if (nfound <= 0) {
        if (waitpid(pid, &status, WNOHANG) > 0) {
          close(inFd);
-#ifdef DEBUG
-         if (config->debugHttpd) {
-           if (WIFEXITED(status))
+#if DEBUG
+         if (WIFEXITED(status))
              bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
-           if (WIFSIGNALED(status))
+         if (WIFSIGNALED(status))
              bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
-         }
 #endif
          break;
        }
@@ -1239,40 +1325,53 @@ static int sendCgi(const char *url,
                        post_readed_idx += count;
                        if(post_readed_size == 0)
                                post_readed_idx = 0;
+               } else {
+                       post_readed_size = post_readed_idx = bodyLen = 0; /* broken pipe to CGI */
                }
       } else if(bodyLen > 0 && post_readed_size == 0 && FD_ISSET(a_c_r, &readSet)) {
-               count = bodyLen > sizeof(wbuf) ? sizeof(wbuf) : bodyLen;
-               count = bb_full_read(a_c_r, wbuf, count);
+               count = bodyLen > (int)sizeof(wbuf) ? (int)sizeof(wbuf) : bodyLen;
+               count = safe_read(a_c_r, wbuf, count);
                if(count > 0) {
                        post_readed_size += count;
                        bodyLen -= count;
-      } else {
+               } else {
                        bodyLen = 0;    /* closed */
                }
-      } else if(FD_ISSET(inFd, &readSet)) {
+      }
+      if(FD_ISSET(inFd, &readSet)) {
        int s = a_c_w;
        char *rbuf = config->buf;
 
+#ifndef PIPE_BUF
+# define PIPESIZE 4096          /* amount of buffering in a pipe */
+#else
+# define PIPESIZE PIPE_BUF
+#endif
+#if PIPESIZE >= MAX_MEMORY_BUFF
+# error "PIPESIZE >= MAX_MEMORY_BUFF"
+#endif
+
        // There is something to read
-       count = bb_full_read(inFd, rbuf, MAX_MEMORY_BUFF-1);
+       count = safe_read(inFd, rbuf, PIPESIZE);
        if (count == 0)
                break;  /* closed */
        if (count > 0) {
          if (firstLine) {
            rbuf[count] = 0;
            /* check to see if the user script added headers */
-           if(strncmp(rbuf, "HTTP/1.0 200 OK\n", 4) != 0) {
-             bb_full_write(s, "HTTP/1.0 200 OK\n", 16);
+           if(strncmp(rbuf, "HTTP/1.0 200 OK\r\n", 4) != 0) {
+             bb_full_write(s, "HTTP/1.0 200 OK\r\n", 17);
            }
            if (strstr(rbuf, "ontent-") == 0) {
-             bb_full_write(s, "Content-type: text/plain\n\n", 26);
+             bb_full_write(s, "Content-type: text/plain\r\n\r\n", 28);
            }
            firstLine = 0;
          }
-         bb_full_write(s, rbuf, count);
-#ifdef DEBUG
-         if (config->debugHttpd)
-               fprintf(stderr, "cgi read %d bytes\n", count);
+         if (bb_full_write(s, rbuf, count) != count)
+             break;
+
+#if DEBUG
+         fprintf(stderr, "cgi read %d bytes\n", count);
 #endif
        }
       }
@@ -1310,24 +1409,23 @@ static int sendFile(const char *url)
                        break;
        }
   /* also, if not found, set default as "application/octet-stream";  */
-  config->found_mime_type = *(table+1);
+  config->httpd_found.found_mime_type = *(table+1);
 #ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
   if (suffix) {
     Htaccess * cur;
 
     for (cur = config->mime_a; cur; cur = cur->next) {
        if(strcmp(cur->before_colon, suffix) == 0) {
-               config->found_mime_type = cur->after_colon;
+               config->httpd_found.found_mime_type = cur->after_colon;
                break;
        }
     }
   }
 #endif  /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
 
-#ifdef DEBUG
-    if (config->debugHttpd)
-       fprintf(stderr, "Sending file '%s' Content-type: %s\n",
-                                       url, config->found_mime_type);
+#if DEBUG
+  fprintf(stderr, "Sending file '%s' Content-type: %s\n",
+                       url, config->httpd_found.found_mime_type);
 #endif
 
   f = open(url, O_RDONLY);
@@ -1337,13 +1435,13 @@ static int sendFile(const char *url)
 
        sendHeaders(HTTP_OK);
        while ((count = bb_full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
-               bb_full_write(a_c_w, buf, count);
+               if (bb_full_write(a_c_w, buf, count) != count)
+                       break;
        }
        close(f);
   } else {
-#ifdef DEBUG
-       if (config->debugHttpd)
-               bb_perror_msg("Unable to open '%s'", url);
+#if DEBUG
+       bb_perror_msg("Unable to open '%s'", url);
 #endif
        sendHeaders(HTTP_NOT_FOUND);
   }
@@ -1357,10 +1455,9 @@ static int checkPermIP(void)
 
     /* This could stand some work */
     for (cur = config->ip_a_d; cur; cur = cur->next) {
-#ifdef DEBUG
-       if (config->debugHttpd) {
-           fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
-           fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
+#if DEBUG
+       fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
+       fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
                (unsigned char)(cur->ip >> 24),
                (unsigned char)(cur->ip >> 16),
                (unsigned char)(cur->ip >> 8),
@@ -1369,13 +1466,12 @@ static int checkPermIP(void)
                (unsigned char)(cur->mask >> 16),
                (unsigned char)(cur->mask >> 8),
                                cur->mask & 0xff);
-       }
 #endif
        if((config->rmt_ip & cur->mask) == cur->ip)
            return cur->allow_deny == 'A';   /* Allow/Deny */
     }
 
-    /* if uncofigured, return 1 - access from all */
+    /* if unconfigured, return 1 - access from all */
     return !config->flg_deny_all;
 }
 
@@ -1411,12 +1507,11 @@ static int checkPerm(const char *path, const char *request)
        if(prev != NULL && strcmp(prev, p0) != 0)
            continue;       /* find next identical */
        p = cur->after_colon;
-#ifdef DEBUG
-       if (config->debugHttpd)
-           fprintf(stderr,"checkPerm: '%s' ? '%s'\n", p0, request);
+#if DEBUG
+       fprintf(stderr,"checkPerm: '%s' ? '%s'\n", p0, request);
 #endif
        {
-           int l = strlen(p0);
+           size_t l = strlen(p0);
 
            if(strncmp(p0, path, l) == 0 &&
                            (l == 1 || path[l] == '/' || path[l] == 0)) {
@@ -1470,6 +1565,20 @@ set_remoteuser_var:
 
 #endif  /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
 
+/****************************************************************************
+ *
+ > $Function: handle_sigalrm()
+ *
+ * $Description: Handle timeouts
+ *
+ ****************************************************************************/
+
+static void
+handle_sigalrm( int sig )
+{
+    sendHeaders(HTTP_REQUEST_TIMEOUT);
+    config->alarm_signaled = sig;
+}
 
 /****************************************************************************
  *
@@ -1484,24 +1593,35 @@ static void handleIncoming(void)
   char *url;
   char *purl;
   int  blank = -1;
-  char *urlArgs;
+  char *test;
+  struct stat sb;
+  int ip_allowed;
 #ifdef CONFIG_FEATURE_HTTPD_CGI
   const char *prequest = request_GET;
   long length=0;
   char *cookie = 0;
   char *content_type = 0;
 #endif
-  char *test;
-  struct stat sb;
-  int ip_allowed;
+#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+  fd_set s_fd;
+  struct timeval tv;
+  int retval;
+#endif
+  struct sigaction sa;
 
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
   int credentials = -1;  /* if not requred this is Ok */
 #endif
 
+  sa.sa_handler = handle_sigalrm;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0; /* no SA_RESTART */
+  sigaction(SIGALRM, &sa, NULL);
+
   do {
     int  count;
 
+    (void) alarm( TIMEOUT );
     if (getLine() <= 0)
        break;  /* closed */
 
@@ -1529,7 +1649,6 @@ BAD_REQUEST:
     *purl = ' ';
     count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
 
-    decodeString(buf, 0);
     if (count < 1 || buf[0] != '/') {
       /* Garbled request/URL */
       goto BAD_REQUEST;
@@ -1541,10 +1660,19 @@ BAD_REQUEST:
     }
     strcpy(url, buf);
     /* extract url args if present */
-    urlArgs = strchr(url, '?');
-    if (urlArgs)
-      *urlArgs++ = 0;
+    test = strchr(url, '?');
+    if (test) {
+      *test++ = 0;
+      config->query = test;
+    }
 
+    test = decodeString(url, 0);
+    if(test == NULL)
+       goto BAD_REQUEST;
+    if(test == (buf+1)) {
+       sendHeaders(HTTP_NOT_FOUND);
+       break;
+    }
     /* algorithm stolen from libbb bb_simplify_path(),
        but don`t strdup and reducing trailing slash and protect out root */
     purl = test = url;
@@ -1576,14 +1704,11 @@ BAD_REQUEST:
     /* If URL is directory, adding '/' */
     if(test[-1] != '/') {
            if ( is_directory(url + 1, 1, &sb) ) {
-                   *test++ = '/';
-                   *test = 0;
-                   purl = test;    /* end ptr */
+                   config->httpd_found.found_moved_temporarily = url;
            }
     }
-#ifdef DEBUG
-    if (config->debugHttpd)
-       fprintf(stderr, "url='%s', args=%s\n", url, urlArgs);
+#if DEBUG
+    fprintf(stderr, "url='%s', args=%s\n", url, config->query);
 #endif
 
     test = url;
@@ -1598,53 +1723,60 @@ BAD_REQUEST:
        }
        *test = '/';
     }
+    if(blank >= 0) {
+      // read until blank line for HTTP version specified, else parse immediate
+      while(1) {
+       alarm(TIMEOUT);
+       count = getLine();
+       if(count <= 0)
+               break;
 
-    // read until blank line for HTTP version specified, else parse immediate
-    while (blank >= 0 && (count = getLine()) > 0) {
-
-#ifdef DEBUG
-      if (config->debugHttpd) fprintf(stderr, "Header: '%s'\n", buf);
+#if DEBUG
+       fprintf(stderr, "Header: '%s'\n", buf);
 #endif
 
 #ifdef CONFIG_FEATURE_HTTPD_CGI
-      /* try and do our best to parse more lines */
-      if ((strncasecmp(buf, Content_length, 15) == 0)) {
-       if(prequest != request_GET)
-               length = strtol(buf + 15, 0, 0); // extra read only for POST
-      } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) {
-               for(test = buf + 7; isspace(*test); test++)
-                       ;
-               cookie = strdup(test);
-      } else if ((strncasecmp(buf, "Content-Type:", 13) == 0)) {
-               for(test = buf + 13; isspace(*test); test++)
-                       ;
-               content_type = strdup(test);
-      } else if ((strncasecmp(buf, "Referer:", 8) == 0)) {
-               for(test = buf + 8; isspace(*test); test++)
-                       ;
-               config->referer = strdup(test);
-      }
+       /* try and do our best to parse more lines */
+       if ((strncasecmp(buf, Content_length, 15) == 0)) {
+         if(prequest != request_GET)
+                 length = strtol(buf + 15, 0, 0); // extra read only for POST
+       } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) {
+                 for(test = buf + 7; isspace(*test); test++)
+                         ;
+                 cookie = strdup(test);
+       } else if ((strncasecmp(buf, "Content-Type:", 13) == 0)) {
+                 for(test = buf + 13; isspace(*test); test++)
+                         ;
+                 content_type = strdup(test);
+       } else if ((strncasecmp(buf, "Referer:", 8) == 0)) {
+                 for(test = buf + 8; isspace(*test); test++)
+                         ;
+                 config->referer = strdup(test);
+       }
 #endif
 
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-      if (strncasecmp(buf, "Authorization:", 14) == 0) {
-       /* We only allow Basic credentials.
-        * It shows up as "Authorization: Basic <userid:password>" where
-        * the userid:password is base64 encoded.
-        */
-       for(test = buf + 14; isspace(*test); test++)
-               ;
-       if (strncasecmp(test, "Basic", 5) != 0)
-               continue;
-
-       test += 5;  /* decodeBase64() skiping space self */
-       decodeBase64(test);
-       credentials = checkPerm(url, test);
-      }
+       if (strncasecmp(buf, "Authorization:", 14) == 0) {
+         /* We only allow Basic credentials.
+          * It shows up as "Authorization: Basic <userid:password>" where
+          * the userid:password is base64 encoded.
+          */
+         for(test = buf + 14; isspace(*test); test++)
+                 ;
+         if (strncasecmp(test, "Basic", 5) != 0)
+                 continue;
+
+         test += 5;  /* decodeBase64() skiping space self */
+         decodeBase64(test);
+         credentials = checkPerm(url, test);
+       }
 #endif          /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
 
-    }   /* while extra header reading */
-
+      }   /* while extra header reading */
+    }
+    (void) alarm( 0 );
+    if(config->alarm_signaled)
+       break;
 
     if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
                /* protect listing [/path]/httpd_conf or IP deny */
@@ -1662,6 +1794,15 @@ FORBIDDEN:      /* protect listing /cgi-bin */
     }
 #endif
 
+    if(config->httpd_found.found_moved_temporarily) {
+       sendHeaders(HTTP_MOVED_TEMPORARILY);
+#if DEBUG
+       /* clear unforked memory flag */
+       config->httpd_found.found_moved_temporarily = NULL;
+#endif
+       break;
+    }
+
     test = url + 1;      /* skip first '/' */
 
 #ifdef CONFIG_FEATURE_HTTPD_CGI
@@ -1672,7 +1813,7 @@ FORBIDDEN:      /* protect listing /cgi-bin */
     if (strncmp(test, "cgi-bin", 7) == 0) {
                if(test[7] == '/' && test[8] == 0)
                        goto FORBIDDEN;     // protect listing cgi-bin/
-               sendCgi(url, prequest, urlArgs, length, cookie, content_type);
+               sendCgi(url, prequest, length, cookie, content_type);
     } else {
        if (prequest != request_GET)
                sendHeaders(HTTP_NOT_IMPLEMENTED);
@@ -1700,16 +1841,29 @@ FORBIDDEN:      /* protect listing /cgi-bin */
 
 #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
 /* from inetd don`t looping: freeing, closing automatic from exit always */
-# ifdef DEBUG
-  if (config->debugHttpd) fprintf(stderr, "closing socket\n");
+# if DEBUG
+  fprintf(stderr, "closing socket\n");
 # endif
 # ifdef CONFIG_FEATURE_HTTPD_CGI
   free(cookie);
   free(content_type);
-  free(config->remoteuser);
   free(config->referer);
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+  free(config->remoteuser);
+#endif
 # endif
   shutdown(a_c_w, SHUT_WR);
+
+  /* Properly wait for remote to closed */
+  FD_ZERO (&s_fd) ;
+  FD_SET (a_c_w, &s_fd) ;
+
+  do {
+    tv.tv_sec = 2 ;
+    tv.tv_usec = 0 ;
+    retval = select (a_c_w + 1, &s_fd, NULL, NULL, &tv);
+  } while (retval > 0 && (read (a_c_w, buf, sizeof (config->buf)) > 0));
+
   shutdown(a_c_r, SHUT_RD);
   close(config->accepted_socket);
 #endif  /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
@@ -1742,7 +1896,7 @@ static int miniHttpd(int server)
   while (1) {
     readfd = portfd;
 
-    /* Now wait INDEFINATELY on the set of sockets! */
+    /* Now wait INDEFINITELY on the set of sockets! */
     if (select(server + 1, &readfd, 0, 0, 0) > 0) {
       if (FD_ISSET(server, &readfd)) {
        int on;
@@ -1757,18 +1911,16 @@ static int miniHttpd(int server)
        }
        config->accepted_socket = s;
        config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr);
-#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
+#if ENABLE_FEATURE_HTTPD_CGI || DEBUG
        sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
                (unsigned char)(config->rmt_ip >> 24),
                (unsigned char)(config->rmt_ip >> 16),
                (unsigned char)(config->rmt_ip >> 8),
                                config->rmt_ip & 0xff);
        config->port = ntohs(fromAddr.sin_port);
-#ifdef DEBUG
-       if (config->debugHttpd) {
-           bb_error_msg("connection from IP=%s, port %u\n",
+#if DEBUG
+       bb_error_msg("connection from IP=%s, port %u\n",
                                        config->rmt_ip_str, config->port);
-       }
 #endif
 #endif /* CONFIG_FEATURE_HTTPD_CGI */
 
@@ -1776,15 +1928,19 @@ static int miniHttpd(int server)
        on = 1;
        setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on));
 
-       if (config->debugHttpd || fork() == 0) {
+#if !DEBUG
+       if (fork() == 0)
+#endif
+       {
            /* This is the spawned thread */
 #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
            /* protect reload config, may be confuse checking */
            signal(SIGHUP, SIG_IGN);
 #endif
            handleIncoming();
-           if(!config->debugHttpd)
-               exit(0);
+#if !DEBUG
+           exit(0);
+#endif
        }
        close(s);
       }
@@ -1803,7 +1959,7 @@ static int miniHttpd(void)
 
   getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen);
   config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr);
-#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
+#if ENABLE_FEATURE_HTTPD_CGI
   sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
                (unsigned char)(config->rmt_ip >> 24),
                (unsigned char)(config->rmt_ip >> 16),
@@ -1831,42 +1987,42 @@ static void sighup_handler(int sig)
 }
 #endif
 
+enum httpd_opts_nums {
+       c_opt_config_file = 0,
+       d_opt_decode_url,
+       h_opt_home_httpd,
+       USE_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
+       USE_FEATURE_HTTPD_BASIC_AUTH(r_opt_realm,)
+       USE_FEATURE_HTTPD_AUTH_MD5(m_opt_md5,)
+       USE_FEATURE_HTTPD_SETUID(u_opt_setuid,)
+       SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY(p_opt_port,)
+};
 
 static const char httpd_opts[]="c:d:h:"
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-                               "e:"
-#define OPT_INC_1 1
-#else
-#define OPT_INC_1 0
-#endif
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-                               "r:"
-# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
-                               "m:"
-# define OPT_INC_2 2
-# else
-# define OPT_INC_2 1
-#endif
-#else
-#define OPT_INC_2 0
-#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-                               "p:v"
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
-                               "u:"
-#endif
-#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
-                                       ;
+       USE_FEATURE_HTTPD_ENCODE_URL_STR("e:")
+       USE_FEATURE_HTTPD_BASIC_AUTH("r:")
+       USE_FEATURE_HTTPD_AUTH_MD5("m:")
+       USE_FEATURE_HTTPD_SETUID("u:")
+       SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY("p:");
+
+#define OPT_CONFIG_FILE (1<<c_opt_config_file)
+#define OPT_DECODE_URL  (1<<d_opt_decode_url)
+#define OPT_HOME_HTTPD  (1<<h_opt_home_httpd)
 
-#define OPT_CONFIG_FILE (1<<0)
-#define OPT_DECODE_URL  (1<<1)
-#define OPT_HOME_HTTPD  (1<<2)
-#define OPT_ENCODE_URL  (1<<(2+OPT_INC_1))
-#define OPT_REALM       (1<<(3+OPT_INC_1))
-#define OPT_MD5         (1<<(4+OPT_INC_1))
-#define OPT_PORT        (1<<(3+OPT_INC_1+OPT_INC_2))
-#define OPT_DEBUG       (1<<(4+OPT_INC_1+OPT_INC_2))
-#define OPT_SETUID      (1<<(5+OPT_INC_1+OPT_INC_2))
+#define OPT_ENCODE_URL  USE_FEATURE_HTTPD_ENCODE_URL_STR((1<<e_opt_encode_url)) \
+                       SKIP_FEATURE_HTTPD_ENCODE_URL_STR(0)
+
+#define OPT_REALM       USE_FEATURE_HTTPD_BASIC_AUTH((1<<r_opt_realm)) \
+                       SKIP_FEATURE_HTTPD_BASIC_AUTH(0)
+
+#define OPT_MD5         USE_FEATURE_HTTPD_AUTH_MD5((1<<m_opt_md5)) \
+                       SKIP_FEATURE_HTTPD_AUTH_MD5(0)
+
+#define OPT_SETUID      USE_FEATURE_HTTPD_SETUID((1<<u_opt_setuid)) \
+                       SKIP_FEATURE_HTTPD_SETUID(0)
+
+#define OPT_PORT        SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY((1<<p_opt_port)) \
+                       USE_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY(0)
 
 
 #ifdef HTTPD_STANDALONE
@@ -1878,25 +2034,14 @@ int httpd_main(int argc, char *argv[])
   unsigned long opt;
   const char *home_httpd = home;
   char *url_for_decode;
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-  const char *url_for_encode;
-#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-  const char *s_port;
-#endif
-
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-  int server;
-#endif
+  USE_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
+  SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY(const char *s_port;)
+  SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY(int server;)
 
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
-  const char *s_uid;
-  long uid = -1;
-#endif
+  USE_FEATURE_HTTPD_SETUID(const char *s_uid;)
+  USE_FEATURE_HTTPD_SETUID(long uid = -1;)
 
-#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
-  const char *pass;
-#endif
+  USE_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
 
   config = xcalloc(1, sizeof(*config));
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
@@ -1911,22 +2056,12 @@ int httpd_main(int argc, char *argv[])
 
   opt = bb_getopt_ulflags(argc, argv, httpd_opts,
                        &(config->configFile), &url_for_decode, &home_httpd
-#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-                       , &url_for_encode
-#endif
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-                       , &(config->realm)
-# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
-                       , &pass
-# endif
-#endif
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-                       , &s_port
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
-                       , &s_uid
-#endif
-#endif
-    );
+                       USE_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
+                       USE_FEATURE_HTTPD_BASIC_AUTH(, &(config->realm))
+                       USE_FEATURE_HTTPD_AUTH_MD5(, &pass)
+                       USE_FEATURE_HTTPD_SETUID(, &s_uid)
+                       SKIP_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY(, &s_port)
+       );
 
   if(opt & OPT_DECODE_URL) {
       printf("%s", decodeString(url_for_decode, 1));
@@ -1947,7 +2082,6 @@ int httpd_main(int argc, char *argv[])
 #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
     if(opt & OPT_PORT)
        config->port = bb_xgetlarg(s_port, 10, 1, 0xffff);
-    config->debugHttpd = opt & OPT_DEBUG;
 #ifdef CONFIG_FEATURE_HTTPD_SETUID
     if(opt & OPT_SETUID) {
        char *e;
@@ -1955,7 +2089,7 @@ int httpd_main(int argc, char *argv[])
        uid = strtol(s_uid, &e, 0);
        if(*e != '\0') {
                /* not integer */
-               uid = my_getpwnam(s_uid);
+               uid = bb_xgetpwnam(s_uid);
        }
       }
 #endif
@@ -1967,7 +2101,7 @@ int httpd_main(int argc, char *argv[])
 #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
   server = openServer();
 # ifdef CONFIG_FEATURE_HTTPD_SETUID
-  /* drop privilegies */
+  /* drop privileges */
   if(uid > 0)
        setuid(uid);
 # endif
@@ -1994,11 +2128,11 @@ int httpd_main(int argc, char *argv[])
   parse_conf(default_path_httpd_conf, FIRST_PARSE);
 #endif
 
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-  if (!config->debugHttpd) {
-    if (daemon(1, 0) < 0)     /* don`t change curent directory */
+#if !ENABLE_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
+# if !DEBUG
+  if (daemon(1, 0) < 0)     /* don`t change curent directory */
        bb_perror_msg_and_die("daemon");
-  }
+# endif
   return miniHttpd(server);
 #else
   return miniHttpd();