- use RESERVE_CONFIG_BUFFER. For defconfig this gives:
[oweals/busybox.git] / networking / httpd.c
index 11e89f64a5c4e3ac9d0aec1ba2ae94634b6080e7..6e80fd9bed9440b2556b8928f0108ed82386891c 100644 (file)
@@ -2,7 +2,7 @@
  * httpd implementation for busybox
  *
  * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
- * Copyright (C) 2003,2004 Vladimir Oleynik <dzo@simtreas.ru>
+ * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru>
  *
  * simplify patch stolen from libbb without using strdup
  *
@@ -54,6 +54,7 @@
  * /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
+ * *.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.
@@ -123,8 +124,10 @@ 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
 
 #define TIMEOUT 60
@@ -168,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
@@ -177,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";
@@ -196,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 {
@@ -215,21 +240,17 @@ 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;)
 
   const char *query;
 
-#ifdef CONFIG_FEATURE_HTTPD_CGI
-  char *referer;
-#endif
+  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
@@ -253,15 +274,17 @@ 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;
 
 static HttpdConfig *config;
@@ -506,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
@@ -514,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
@@ -577,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;
@@ -649,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) {
@@ -665,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) {
@@ -726,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;
 
@@ -771,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';
@@ -976,7 +1024,7 @@ static int sendHeaders(HttpResponseNum responseNum)
   /* 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, mime_type, timeStr);
 
@@ -996,7 +1044,7 @@ static int sendHeaders(HttpResponseNum responseNum)
   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;
@@ -1007,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);
 }
@@ -1106,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]);
@@ -1188,11 +1233,29 @@ static int sendCgi(const char *url,
            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);
                }
            }
       }
@@ -1247,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;
        }
@@ -1298,20 +1359,19 @@ static int sendCgi(const char *url,
          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;
          }
          if (bb_full_write(s, rbuf, count) != count)
              break;
 
-#ifdef DEBUG
-         if (config->debugHttpd)
-               fprintf(stderr, "cgi read %d bytes\n", count);
+#if DEBUG
+         fprintf(stderr, "cgi read %d bytes\n", count);
 #endif
        }
       }
@@ -1363,10 +1423,9 @@ static int sendFile(const char *url)
   }
 #endif  /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
 
-#ifdef DEBUG
-    if (config->debugHttpd)
-       fprintf(stderr, "Sending file '%s' Content-type: %s\n",
-                                       url, config->httpd_found.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);
@@ -1381,9 +1440,8 @@ static int sendFile(const char *url)
        }
        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);
   }
@@ -1397,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),
@@ -1409,7 +1466,6 @@ 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 */
@@ -1451,9 +1507,8 @@ 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
        {
            size_t l = strlen(p0);
@@ -1512,9 +1567,9 @@ set_remoteuser_var:
 
 /****************************************************************************
  *
- > $Function: handleIncoming()
+ > $Function: handle_sigalrm()
  *
- * $Description: Handle an incoming http request.
+ * $Description: Handle timeouts
  *
  ****************************************************************************/
 
@@ -1594,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;
@@ -1612,6 +1666,13 @@ BAD_REQUEST:
       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;
@@ -1640,16 +1701,14 @@ BAD_REQUEST:
     *++purl = 0;        /* so keep last character */
     test = purl;        /* end ptr */
 
-    /* If URL is directory, adding '/' */
     /* If URL is directory, adding '/' */
     if(test[-1] != '/') {
            if ( is_directory(url + 1, 1, &sb) ) {
                    config->httpd_found.found_moved_temporarily = url;
            }
     }
-#ifdef DEBUG
-    if (config->debugHttpd)
-       fprintf(stderr, "url='%s', args=%s\n", url, config->query);
+#if DEBUG
+    fprintf(stderr, "url='%s', args=%s\n", url, config->query);
 #endif
 
     test = url;
@@ -1672,8 +1731,8 @@ BAD_REQUEST:
        if(count <= 0)
                break;
 
-#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
@@ -1737,10 +1796,9 @@ FORBIDDEN:      /* protect listing /cgi-bin */
 
     if(config->httpd_found.found_moved_temporarily) {
        sendHeaders(HTTP_MOVED_TEMPORARILY);
-#ifdef DEBUG
+#if DEBUG
        /* clear unforked memory flag */
-       if(config->debugHttpd)
-               config->httpd_found.found_moved_temporarily = NULL;
+       config->httpd_found.found_moved_temporarily = NULL;
 #endif
        break;
     }
@@ -1783,8 +1841,8 @@ 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);
@@ -1853,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 */
 
@@ -1872,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);
       }
@@ -1899,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),
@@ -1927,40 +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:"
-#endif
-#define OPT_INC_1 ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+       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:");
 
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-                               "r:"
-#endif
-#define OPT_INC_2 ENABLE_FEATURE_HTTPD_BASIC_AUTH
+#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)
 
-#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
-                               "m:"
-#endif
-#define OPT_INC_3 ENABLE_FEATURE_HTTPD_AUTH_MD5
+#define OPT_ENCODE_URL  USE_FEATURE_HTTPD_ENCODE_URL_STR((1<<e_opt_encode_url)) \
+                       SKIP_FEATURE_HTTPD_ENCODE_URL_STR(0)
 
-#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
-                               "p:v"
-#endif
-#ifdef CONFIG_FEATURE_HTTPD_SETUID
-                               "u:"
-#endif
-                                       ;
+#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_CONFIG_FILE (1<<0)                                    /* c */
-#define OPT_DECODE_URL  (1<<1)                                    /* d */
-#define OPT_HOME_HTTPD  (1<<2)                                    /* h */
-#define OPT_ENCODE_URL  (1<<(2+OPT_INC_1))                        /* e */
-#define OPT_REALM       (1<<(2+OPT_INC_1+OPT_INC_2))              /* r */
-#define OPT_MD5         (1<<(2+OPT_INC_1+OPT_INC_2+OPT_INC_3))    /* m */
-#define OPT_PORT        (1<<(3+OPT_INC_1+OPT_INC_2+OPT_INC_3))    /* p */
-#define OPT_DEBUG       (1<<(4+OPT_INC_1+OPT_INC_2+OPT_INC_3))    /* v */
-#define OPT_SETUID      (1<<(5+OPT_INC_1+OPT_INC_2+OPT_INC_3))    /* u */
+#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
@@ -1972,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
@@ -2005,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));
@@ -2041,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;
@@ -2088,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();