last_patch84 by Vodz.
authorGlenn L McGrath <bug1@ihug.co.nz>
Tue, 13 May 2003 16:20:11 +0000 (16:20 -0000)
committerGlenn L McGrath <bug1@ihug.co.nz>
Tue, 13 May 2003 16:20:11 +0000 (16:20 -0000)
networking/httpd.c

index e62168d389a5c17f240a4828575650aad18ba859..9411117eaa75d180b3aeb138aae771c091404888 100644 (file)
@@ -42,7 +42,7 @@
  * The server can also be invoked as a url arg decoder and html text encoder
  * as follows:
  *  foo=`httpd -d $foo`             # decode "Hello%20World" as "Hello World"
- *  bar=`httpd -e "<Hello World>"`  # encode as "%3CHello%20World%3E"
+ *  bar=`httpd -e "<Hello World>"`  # encode as "&#60Hello&#32World&#62"
  *
  * httpd.conf has the following format:
 
@@ -56,51 +56,17 @@ D:*               # Deny from other IP connections
 /adm:toor:PaSsWd  # or user toor, pwd PaSsWd on urls starting with /adm/
 .au:audio/basic   # additional mime type for audio.au files
 
-A shortes path and D:from[^*] automaticaly sorting to top.
-All longest path can`t reset user:password if shorted protect setted.
-
 A/D may be as a/d or allow/deny - first char case unsensitive parsed only.
 
 Each subdir can have config file.
+You can set less IP allow from subdir config.
+Password protection from subdir config can rewriten previous sets for
+current or/and next subpathes.
 For protect as user:pass current subdir and subpathes set from subdir config:
 /:user:pass
-if not, other subpathes for give effect must have path from httpd root
-/current_subdir_path_from_httpd_root/subpath:user:pass
-
-The Deny/Allow IP logic:
-
-       1. Allow all:
-The config don`t set D: lines
-
-       2. Allow from setted only:
-see the begin format example
-
-       3. Set deny, allow from other:
-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:*             # allow from other, this line not strongly require
-
-  A global and subdirs config merging logic:
-allow rules reducing, deny lines strongled.
-  The algorithm combinations:
-
-       4. If current config have
-A:from
-D:*
-  subdir config A: lines skiping, D:from - moving top
-
-       5. If current config have
-D:from
-A:*
-  result config:
-D:from current
-D:from subdir
-A:from subdir
- and seting D:* if subdir config have this
+/subpath:user2:pass2
 
  If -c don`t setted, used httpd root config, else httpd root config skiped.
- Exited with fault if can`t open start config.
- For set wide open server, use -c /dev/null ;=)
  */
 
 #include <stdio.h>
@@ -120,30 +86,29 @@ A:from subdir
 #include "busybox.h"
 
 
-static const char httpdVersion[] = "busybox httpd/1.20 31-Jan-2003";
-static const char default_patch_httpd_conf[] = "/etc";
+static const char httpdVersion[] = "busybox httpd/1.25 10-May-2003";
+static const char default_path_httpd_conf[] = "/etc";
 static const char httpd_conf[] = "httpd.conf";
 static const char home[] = "/www";
 
-// Note: xfuncs are not used because we want the server to keep running
+// Note: bussybox 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
 
 //#define DEBUG 1
 
-/* Configure options, disabled by default as nonstandart httpd feature */
+/* Configure options, disabled by default as custom httpd feature */
+
+/* disabled as optional features */
 //#define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
 //#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-//#define CONFIG_FEATURE_HTTPD_DECODE_URL_STR
-
-/* disabled as not necessary feature */
 //#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
 //#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
 //#define CONFIG_FEATURE_HTTPD_SETUID
 //#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
 
-/* If seted this you can use this server from internet superserver only */
+/* If set, use this server from internet superserver only */
 //#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
 
 /* You can use this server as standalone, require libbb.a for linking */
@@ -160,7 +125,6 @@ static const char home[] = "/www";
 #undef CONFIG_FEATURE_HTTPD_BASIC_AUTH
 #undef CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
 #undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-#undef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
 #undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
 #undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
 #undef CONFIG_FEATURE_HTTPD_CGI
@@ -170,7 +134,6 @@ static const char home[] = "/www";
 #define CONFIG_FEATURE_HTTPD_BASIC_AUTH
 #define CONFIG_FEATURE_HTTPD_SET_CGI_VARS_TO_ENV
 #define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
-#define CONFIG_FEATURE_HTTPD_DECODE_URL_STR
 #define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
 #define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
 #define CONFIG_FEATURE_HTTPD_CGI
@@ -183,7 +146,7 @@ const char *bb_applet_name = "httpd";
 void bb_show_usage(void)
 {
   fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] "
-                 "[-r realm] [-u user]\n", bb_applet_name);
+                 "[-r realm] [-u user] [-h homedir]\n", bb_applet_name);
   exit(1);
 }
 #endif
@@ -232,6 +195,15 @@ typedef struct
   const char *found_mime_type;
   off_t ContentLength;          /* -1 - unknown */
   time_t last_mod;
+
+  Htaccess *ip_a_d;             /* config allow/deny lines */
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+  Htaccess *auth;               /* config user:password lines */
+#endif
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+  Htaccess *mime_a;             /* config mime types */
+#endif
+
 #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
   int accepted_socket;
 #define a_c_r config->accepted_socket
@@ -241,7 +213,6 @@ typedef struct
 #define a_c_r 0
 #define a_c_w 1
 #endif
-  Htaccess *Httpd_conf_parsed;
 } HttpdConfig;
 
 static HttpdConfig *config;
@@ -308,15 +279,16 @@ static const HttpEnumString httpResponseNames[] = {
   { HTTP_OK, "OK" },
   { HTTP_NOT_IMPLEMENTED, "Not Implemented",
     "The requested method is not recognized by this server." },
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
   { HTTP_UNAUTHORIZED, "Unauthorized", "" },
+#endif
   { HTTP_NOT_FOUND, "Not Found",
     "The requested URL was not found on this server." },
-  { HTTP_BAD_REQUEST, "Bad Request" ,
-    "Unsupported method." },
+  { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
   { HTTP_FORBIDDEN, "Forbidden", "" },
-  { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error"
+  { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
     "Internal Server Error" },
-#if 0
+#if 0                               /* not implemented */
   { HTTP_CREATED, "Created" },
   { HTTP_ACCEPTED, "Accepted" },
   { HTTP_NO_CONTENT, "No Content" },
@@ -334,157 +306,106 @@ static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
 static const char Content_length[] = "Content-length:";
 
 
-/*
- * sotring to:
- * .ext:mime/type
- * /path:user:pass
- * /path/subdir:user:pass
- * D:from
- * A:from
- * D:*
- */
-static int conf_sort(const void *p1, const void *p2)
+
+static void free_config_lines(Htaccess **pprev)
 {
-    const Htaccess *cl1 = *(const Htaccess **)p1;
-    const Htaccess *cl2 = *(const Htaccess **)p2;
-    char c1 = cl1->before_colon[0];
-    char c2 = cl2->before_colon[0];
-    int test;
+    Htaccess *prev = *pprev;
 
-#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
-    /* .ext line up before other lines for simlify algorithm */
-    test = c2 == '.';
-    if(c1 == '.')
-       return -(!test);
-    if(test)
-       return test;
-#endif
+    while( prev ) {
+       Htaccess *cur = prev;
 
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-    test = c1 == '/';
-    /* /path line up before A/D lines for simlify algorithm */
-    if(test) {
-       if(c2 != '/')
-           return -test;
-       /* a shortes path with user:pass must be first */
-       return strlen(cl1->before_colon) - strlen(cl2->before_colon);
-    } else if(c2 == '/')
-           return !test;
-#endif
-
-    /* D:from must move top */
-    test = c2 == 'D' && cl2->after_colon[0] != 0;
-    if(c1 == 'D' && cl1->after_colon[0] != 0) {
-       return -(!test);
+       prev = cur->next;
+       free(cur);
     }
-    if(test)
-       return test;
+    *pprev = NULL;
+}
 
-    /* next lines -  A:from */
-    test = c2 == 'A' && cl2->after_colon[0] != 0;
-    if(c1 == 'A' && cl1->after_colon[0] != 0) {
-       return -(!test);
-    }
-    if(test)
-       return test;
+static void add_config_line(Htaccess **pprev, Htaccess *cur)
+{
+    if(*pprev == NULL) {
+       *pprev = cur;
+    } else {
+       Htaccess *prev;
 
-    /* end lines - D:* */
-    test = c2 == 'D' && cl2->after_colon[0] == 0;
-    if(c1 == 'D' && cl1->after_colon[0] == 0) {
-       return -(!test);
+       for(prev = *pprev; prev->next; prev = prev->next)
+               ;
+       prev->next = cur;
     }
-#ifdef DEBUG
-    if(!test)
-       bb_error_msg_and_die("sort: can`t found compares!");
-#endif
-    return test;
 }
 
-
 /* flag */
-#define FIRST_PARSE    0
-#define SUBDIR_PARSE   1
-#define SIGNALED_PARSE 2
+#define FIRST_PARSE          0
+#define SUBDIR_PARSE         1
+#define SIGNALED_PARSE       2
+#define FIND_FROM_HTTPD_ROOT 3
 
 static void parse_conf(const char *path, int flag)
 {
-#define bc cur->before_colon[0]
-#define ac cur->after_colon[0]
     FILE *f;
-    Htaccess *prev;
     Htaccess *cur;
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+    Htaccess *prev;
+#endif
+
     const char *cf = config->configFile;
     char buf[80];
     char *p0 = NULL;
-    int deny_all = 0;   /* default A:* */
-    int n = 0;          /* count config lines */
+    char *c, *p;
+
+    /* free previous setuped */
+    free_config_lines(&config->ip_a_d);
+    if(flag != SUBDIR_PARSE) {
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+       free_config_lines(&config->auth)
+#endif
+       ;   /* syntax confuse */
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+       free_config_lines(&config->mime_a);
+#endif
+    }
 
     if(flag == SUBDIR_PARSE || cf == NULL) {
-       cf = p0 = alloca(strlen(path) + sizeof(httpd_conf) + 2);
-       if(p0 == NULL) {
+       cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
+       if(cf == NULL) {
            if(flag == FIRST_PARSE)
                bb_error_msg_and_die(bb_msg_memory_exhausted);
            return;
        }
-       sprintf(p0, "%s/%s", path, httpd_conf);
+       sprintf((char *)cf, "%s/%s", path, httpd_conf);
     }
 
     while((f = fopen(cf, "r")) == NULL) {
-       if(flag != FIRST_PARSE)
-           return;                 /* subdir config not found */
-       if(p0 == NULL)              /* if -c option gived */
-               bb_perror_msg_and_die("%s", cf);
-       p0 = NULL;
-       cf = httpd_conf;            /* set -c ./httpd_conf */
-    }
-
-    prev = config->Httpd_conf_parsed;
-    if(flag != SUBDIR_PARSE) {
-       /* free previous setuped */
-       while( prev ) {
-               cur = prev;
-               prev = cur->next;
-               free(cur);
-       }
-       config->Httpd_conf_parsed = prev; /* eq NULL */
-    } else {
-       /* parse previous IP logic for merge */
-       for(cur = prev; cur; cur = cur->next) {
-           if(bc == 'D' && ac == 0)
-               deny_all++;
-           n++;
-           /* find last for set prev->next of merging */
-           if(cur != prev)
-               prev = prev->next;
+       if(flag != FIRST_PARSE) {
+           /* config file not found */
+           return;
        }
+       if(config->configFile)      /* if -c option given */
+           bb_perror_msg_and_die("%s", cf);
+       flag = FIND_FROM_HTTPD_ROOT;
+       cf = httpd_conf;
     }
 
-       /* This could stand some work */
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+    prev = config->auth;
+#endif
+    /* This could stand some work */
     while ( (p0 = fgets(buf, 80, f)) != NULL) {
-       char *p;
-       char *colon;
-
-       for(p = colon = p0; *p; p++) {
-               if(*p == '#') {
-                       *p = 0;
-                       break;
+       c = NULL;
+       for(p = p0; *p0 != 0 && *p0 != '#'; p0++) {
+               if(!isspace(*p0)) {
+                   *p++ = *p0;
+                   if(*p0 == ':' && c == NULL)
+                       c = p;
                }
-               if(isspace(*p)) {
-                       if(p != p0) {
-                               *p = 0;
-                               break;
-                       }
-                       p0++;
-               }
-               else if(*p == ':' && colon <= p0)
-                       colon = p;
        }
+       *p = 0;
 
        /* test for empty or strange line */
-       if (colon <= p0 || colon[1] == 0)
+       if (c == NULL || *c == 0)
            continue;
-       if(colon[1] == '*')
-           colon[1] = 0;   /* Allow all */
+       if(*c == '*')
+           *c = 0;   /* Allow all */
+       p0 = buf;
        if(*p0 == 'a')
            *p0 = 'A';
        if(*p0 == 'd')
@@ -498,70 +419,109 @@ static void parse_conf(const char *path, int flag)
 #endif
                                                       )
               continue;
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-       if(*p0 == '/' && colon[1] == 0) {
-           /* skip /path:* */
+
+       if(*p0 == 'A' && *c == 0) {
+           /* skip default A:* */
            continue;
        }
-#endif
+       p0 = buf;
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+       if(*p0 == '/') {
+           if(*c == 0) {
+               /* skip /path:* */
+               continue;
+           }
+           /* make full path from httpd root / curent_path / config_line_path */
+           cf = flag == SUBDIR_PARSE ? path : "";
+           p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
+           if(p0 == NULL)
+               continue;
+           c[-1] = 0;
+           sprintf(p0, "/%s%s", cf, buf);
+
+           /* another call bb_simplify_path */
+           cf = p = p0;
+
+           do {
+                   if (*p == '/') {
+                       if (*cf == '/') {    /* skip duplicate (or initial) slash */
+                           continue;
+                       } else if (*cf == '.') {
+                           if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
+                               continue;
+                           } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
+                               ++cf;
+                               if (p > p0) {
+                                   while (*--p != '/');    /* omit previous dir */
+                               }
+                               continue;
+                           }
+                       }
+                   }
+                   *++p = *cf;
+           } while (*++cf);
 
-       if(*p0 == 'A' || *p0 == 'D') {
-           if(colon[1] == 0) {
-               if(*p0 == 'A' || deny_all != 0)
-                   continue;   /* skip default A:* or double D:* */
+           if ((p == p0) || (*p != '/')) {      /* not a trailing slash */
+               ++p;                             /* so keep last character */
            }
-           if(deny_all != 0 && *p0 == 'A')
-               continue;   // if previous setted rule D:* skip all subdir A:
+           *p = 0;
+           sprintf(p0, "%s:%s", p0, c);
        }
-
+#endif
        /* storing current config line */
-       cur = calloc(1, sizeof(Htaccess) + (p-p0));
+
+       cur = calloc(1, sizeof(Htaccess) + strlen(p0));
        if(cur) {
-           if(*(colon-1) == '/' && (colon-1) != p0)
-               colon[-1] = 0;      // remove last / from /path/
-           cur->after_colon = strcpy(cur->before_colon, p0);
-           cur->after_colon += (colon-p0);
-           *cur->after_colon++ = 0;
+           cf = strcpy(cur->before_colon, p0);
+           c = strchr(cf, ':');
+           *c++ = 0;
+           cur->after_colon = c;
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+           if(*cf == '/')
+               free(p0);
+#endif
+           if(*cf == 'A' || *cf == 'D')
+               add_config_line(&config->ip_a_d, cur);
+#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
+           else if(*cf == '.')
+               add_config_line(&config->mime_a, cur);
+#endif
 
-           if(prev == NULL) {
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
+           else if(prev == NULL) {
                /* first line */
-               config->Httpd_conf_parsed = prev = cur;
+               config->auth = prev = cur;
            } else {
-               prev->next = cur;
-               prev = cur;
+               /* sort path, if current lenght eq or bigger then move up */
+               Htaccess *prev_hti = config->auth;
+               int l = strlen(cf);
+               Htaccess *hti;
+
+               for(hti = prev_hti; hti; hti = hti->next) {
+                   if(l >= strlen(hti->before_colon)) {
+                       /* insert before hti */
+                       cur->next = hti;
+                       if(prev_hti != hti) {
+                           prev_hti->next = cur;
+                           break;
+                       } else {
+                           /* insert as top */
+                           config->auth = cur;
+                           break;
+                       }
+                   }
+                   if(prev_hti != hti)
+                           prev_hti = prev_hti->next;
+               }
+               if(!hti)  {       /* not inserted, add to bottom */
+                   prev->next = cur;
+                   prev = cur;
+               }
            }
-           n++;
+#endif
        }
    }
    fclose(f);
-
-   if(n > 1) {
-       /* sorting conf lines */
-       Htaccess ** pcur;     /* array for qsort */
-
-       prev = config->Httpd_conf_parsed;
-       pcur = alloca((n + 1) * sizeof(Htaccess *));
-       if(pcur == NULL) {
-           if(flag == FIRST_PARSE)
-               bb_error_msg_and_die(bb_msg_memory_exhausted);
-           return;
-       }
-       n = 0;
-       for(cur = prev; cur; cur = cur->next)
-               pcur[n++] = cur;
-       pcur[n] = NULL;
-
-       qsort(pcur, n, sizeof(Htaccess *), conf_sort);
-
-       /* storing sorted config */
-       config->Httpd_conf_parsed = *pcur;
-       for(cur = *pcur; cur; cur = cur->next) {
-#ifdef DEBUG
-           bb_error_msg("%s: %s:%s", cf, cur->before_colon, cur->after_colon);
-#endif
-           cur->next = *++pcur;
-       }
-    }
 }
 
 #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
@@ -602,7 +562,6 @@ static char *encodeString(const char *string)
 }
 #endif          /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */
 
-#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
 /****************************************************************************
  *
  > $Function: decodeString()
@@ -615,20 +574,21 @@ static char *encodeString(const char *string)
  *
  * $Parameters:
  *      (char *) string . . . The first string to decode.
+ *      (int)    flag   . . . 1 if require decode '+' as ' ' for CGI
  *
  * $Return: (char *)  . . . . A pointer to the decoded string (same as input).
  *
  * $Errors: None
  *
  ****************************************************************************/
-static char *decodeString(char *string)
+static char *decodeString(char *string, int flag_plus_to_space)
 {
   /* note that decoded string is always shorter than original */
   char *orig = string;
   char *ptr = string;
   while (*ptr)
   {
-    if (*ptr == '+')    { *string++ = ' '; ptr++; }
+    if (*ptr == '+' && flag_plus_to_space)    { *string++ = ' '; ptr++; }
     else if (*ptr != '%') *string++ = *ptr++;
     else  {
       unsigned int value;
@@ -640,7 +600,6 @@ static char *decodeString(char *string)
   *string = '\0';
   return orig;
 }
-#endif          /* CONFIG_FEATURE_HTTPD_DECODE_URL_STR */
 
 
 #ifdef CONFIG_FEATURE_HTTPD_CGI
@@ -730,7 +689,7 @@ static void addEnvCgi(const char *pargs)
     args = strchr(value, '&');
     if (args)
        *args++ = 0;
-    addEnv("CGI", name, decodeString(value));
+    addEnv("CGI", name, decodeString(value, 1));
   }
   free(memargs);
 }
@@ -758,33 +717,44 @@ static void addEnvCgi(const char *pargs)
  ****************************************************************************/
 static void decodeBase64(char *Data)
 {
-  int i = 0;
-  static const char base64ToBin[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
   const unsigned char *in = Data;
   // The decoded size will be at most 3/4 the size of the encoded
   unsigned long ch = 0;
+  int i = 0;
 
   while (*in) {
-    unsigned char conv = 0;
-
-    while (*in) {
-      const char *p64;
+    int t = *in++;
 
-      p64 = strchr(base64ToBin, *in++);
-      if(p64 == NULL)
-       continue;
-      conv = (p64 - base64ToBin);
-      break;
+    switch(t) {
+       case '+':
+               t = 62;
+               break;
+       case '/':
+               t = 63;
+               break;
+       case '=':
+               t = 0;
+               break;
+       case 'A' ... 'Z':
+               t = t - 'A';
+               break;
+       case 'a' ... 'z':
+               t = t - 'a' + 26;
+               break;
+       case '0' ... '9':
+               t = t - '0' + 52;
+               break;
+       default:
+               continue;
     }
-    ch = (ch << 6) | conv;
+    ch = (ch << 6) | t;
     i++;
-    if (i== 4) {
-      *Data++ = (char) (ch >> 16);
-      *Data++ = (char) (ch >> 8);
-      *Data++ = (char) ch;
-      i = 0;
+    if (i == 4) {
+       *Data++ = (char) (ch >> 16);
+       *Data++ = (char) (ch >> 8);
+       *Data++ = (char) ch;
+       i = 0;
     }
   }
   *Data = 0;
@@ -1203,7 +1173,7 @@ static int sendFile(const char *url, char *buf)
   if (suffix) {
     Htaccess * cur;
 
-    for (cur = config->Httpd_conf_parsed; cur; cur = cur->next) {
+    for (cur = config->mime_a; cur; cur = cur->next) {
        if(strcmp(cur->before_colon, suffix) == 0) {
                config->found_mime_type = cur->after_colon;
                break;
@@ -1255,74 +1225,71 @@ static int sendFile(const char *url, char *buf)
  *
  ****************************************************************************/
 
-
+#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
 static int checkPerm(const char *path, const char *request)
 {
     Htaccess * cur;
     const char *p;
+    const char *p0;
 
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
     int ipaddr = path == NULL;
     const char *prev = NULL;
-#else
-#   define ipaddr 1
-#endif
 
     /* This could stand some work */
-    for (cur = config->Httpd_conf_parsed; cur; cur = cur->next) {
-       const char *p0 = cur->before_colon;
-
-       if(*p0 == 'A' || *p0 == 'D') {
-               if(!ipaddr)
-                       continue;
-       } else {
-               if(ipaddr)
-                       continue;
-#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
-               if(*p0 == '.')
-                       continue;
-#endif
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-               if(prev != NULL && strcmp(prev, p0) != 0)
-                       continue;       /* find next identical */
-#endif
-       }
-
+    for (cur = ipaddr ? config->ip_a_d : config->auth; cur; cur = cur->next) {
+       p0 = cur->before_colon;
+       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",
-                               (ipaddr ? p : p0), request);
+           fprintf(stderr,"checkPerm: '%s' ? '%s'\n",
+                               (ipaddr ? (*p ? p : "*") : p0), request);
 #endif
        if(ipaddr) {
-               if(strncmp(p, request, strlen(p)) != 0)
-                       continue;
-               return *p0 == 'A';   /* Allow/Deny */
-
+           if(strncmp(p, request, strlen(p)) != 0)
+               continue;
+           return *p0 == 'A';   /* Allow/Deny */
+       } else {
+           int l = strlen(p0);
+
+           if(strncmp(p0, path, l) == 0 &&
+                           (l == 1 || path[l] == '/' || path[l] == 0)) {
+               /* path match found.  Check request */
+               if (strcmp(p, request) == 0)
+                   return 1;   /* Ok */
+               /* unauthorized, but check next /path:user:password */
+               prev = p0;
+           }
        }
-#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-         else {
-               int l = strlen(p0);
-
-               if(strncmp(p0, path, l) == 0 &&
-                               (l == 1 || path[l] == '/' || path[l] == 0)) {
-                       /* path match found.  Check request */
-                       if (strcmp(p, request) == 0)
-                               return 1;   /* Ok */
-                       /* unauthorized, but check next /path:user:password */
-                       prev = p0;
-               }
-         }
-#endif
     }   /* for */
 
-#ifndef CONFIG_FEATURE_HTTPD_BASIC_AUTH
-       /* if uncofigured, return 1 - access from all */
-    return 1;
-#else
     return prev == NULL;
+}
+
+#else /* ifndef CONFIG_FEATURE_HTTPD_BASIC_AUTH */
+static int checkPermIP(const char *request)
+{
+    Htaccess * cur;
+    const char *p;
+
+    /* This could stand some work */
+    for (cur = config->ip_a_d; cur; cur = cur->next) {
+       p = cur->after_colon;
+#ifdef DEBUG
+       if (config->debugHttpd)
+           fprintf(stderr, "checkPerm: '%s' ? '%s'\n",
+                                       (*p ? p : "*"), request);
 #endif
+       if(strncmp(p, request, strlen(p)) == 0)
+           return *cur->before_colon == 'A';   /* Allow/Deny */
+    }
+
+    /* if uncofigured, return 1 - access from all */
+    return 1;
 }
+#define checkPerm(null, request) checkPermIP(request)
+#endif  /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
 
 
 /****************************************************************************
@@ -1347,6 +1314,7 @@ static void handleIncoming(void)
 #endif
   char *test;
   struct stat sb;
+  int ip_allowed;
 
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
   int credentials = -1;  /* if not requred this is Ok */
@@ -1382,6 +1350,7 @@ 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;
@@ -1394,13 +1363,11 @@ BAD_REQUEST:
     strcpy(url, buf);
     /* extract url args if present */
     urlArgs = strchr(url, '?');
-    if (urlArgs) {
-      *urlArgs++ = 0;   /* next code can set '/' to this pointer,
-                          but CGI script can`t be a directory */
-    }
+    if (urlArgs)
+      *urlArgs++ = 0;
 
     /* algorithm stolen from libbb bb_simplify_path(),
-       but don`t strdup and reducing trailing slash */
+       but don`t strdup and reducing trailing slash and protect out root */
     purl = test = url;
 
     do {
@@ -1441,12 +1408,14 @@ BAD_REQUEST:
 #endif
 
     test = url;
-    while((test = strchr( test + 1, '/' )) != NULL) {
+    ip_allowed = checkPerm(NULL, config->rmt_ip);
+    while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) {
        /* have path1/path2 */
        *test = '\0';
        if( is_directory(url + 1, 1, &sb) ) {
                /* may be having subdir config */
                parse_conf(url + 1, SUBDIR_PARSE);
+               ip_allowed = checkPerm(NULL, config->rmt_ip);
        }
        *test = '/';
     }
@@ -1490,8 +1459,7 @@ BAD_REQUEST:
     }   /* while extra header reading */
 
 
-    if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 ||
-                       checkPerm(NULL, config->rmt_ip) == 0) {
+    if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
                /* protect listing [/path]/httpd_conf or IP deny */
 #ifdef CONFIG_FEATURE_HTTPD_CGI
 FORBIDDEN:      /* protect listing /cgi-bin */
@@ -1525,8 +1493,8 @@ FORBIDDEN:      /* protect listing /cgi-bin */
     }
 
     if (strncmp(test, "cgi-bin", 7) == 0) {
-               if(test[7] == 0 || (test[7] == '/' && test[8] == 0))
-                       goto FORBIDDEN;     // protect listing cgi-bin
+               if(test[7] == '/' && test[8] == 0)
+                       goto FORBIDDEN;     // protect listing cgi-bin/
                sendCgi(url, prequest, urlArgs, body, length, cookie);
     } else {
        if (prequest != request_GET)
@@ -1587,7 +1555,6 @@ FORBIDDEN:      /* protect listing /cgi-bin */
 static int miniHttpd(int server)
 {
   fd_set readfd, portfd;
-  int nfound;
 
   FD_ZERO(&portfd);
   FD_SET(server, &portfd);
@@ -1597,16 +1564,7 @@ static int miniHttpd(int server)
     readfd = portfd;
 
     /* Now wait INDEFINATELY on the set of sockets! */
-    nfound = select(server + 1, &readfd, 0, 0, 0);
-
-    switch (nfound) {
-    case 0:
-      /* select timeout error! */
-      break ;
-    case -1:
-      /* select error */
-      break;
-    default:
+    if (select(server + 1, &readfd, 0, 0, 0) > 0) {
       if (FD_ISSET(server, &readfd)) {
        int on;
        struct sockaddr_in fromAddr;
@@ -1617,7 +1575,7 @@ static int miniHttpd(int server)
                       (struct sockaddr *)&fromAddr, &fromAddrLen);
 
        if (s < 0) {
-         continue;
+           continue;
        }
        config->accepted_socket = s;
        addr = ntohl(fromAddr.sin_addr.s_addr);
@@ -1629,7 +1587,7 @@ static int miniHttpd(int server)
        config->port = ntohs(fromAddr.sin_port);
 #ifdef DEBUG
        if (config->debugHttpd) {
-               bb_error_msg("connection from IP=%s, port %u\n",
+           bb_error_msg("connection from IP=%s, port %u\n",
                                        config->rmt_ip, config->port);
        }
 #endif
@@ -1638,9 +1596,13 @@ static int miniHttpd(int server)
        setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on));
 
        if (config->debugHttpd || fork() == 0) {
-         /* This is the spawned thread */
-         handleIncoming();
-         if(!config->debugHttpd)
+           /* 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);
        }
        close(s);
@@ -1682,7 +1644,7 @@ static void sighup_handler(int sig)
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART;
        sigaction(SIGHUP, &sa, NULL);
-       parse_conf(default_patch_httpd_conf,
+       parse_conf(default_path_httpd_conf,
                    sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
 }
 #endif
@@ -1716,16 +1678,13 @@ int httpd_main(int argc, char *argv[])
 
   /* check if user supplied a port number */
   for (;;) {
-    int c = getopt( argc, argv, "c:h:"
+    int c = getopt( argc, argv, "c:d:h:"
 #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
                                "p:v"
 #endif
 #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
                "e:"
 #endif
-#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
-               "d:"
-#endif
 #ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
                "r:"
 #endif
@@ -1751,11 +1710,9 @@ int httpd_main(int argc, char *argv[])
        bb_error_msg_and_die("invalid %s for -p", optarg);
       break;
 #endif
-#ifdef CONFIG_FEATURE_HTTPD_DECODE_URL_STR
     case 'd':
-      printf("%s", decodeString(optarg));
+      printf("%s", decodeString(optarg, 1));
       return 0;
-#endif
 #ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
     case 'e':
       printf("%s", encodeString(optarg));
@@ -1803,7 +1760,7 @@ int httpd_main(int argc, char *argv[])
 #ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
   sighup_handler(0);
 #else
-  parse_conf(default_patch_httpd_conf, FIRST_PARSE);
+  parse_conf(default_path_httpd_conf, FIRST_PARSE);
 #endif
 
 #ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY