uhttpd: make Lua handler more CGI like and fork child away
[project/luci.git] / contrib / package / uhttpd / src / uhttpd.c
index a338d29..ea4ca00 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * uhttpd - Tiny single-threaded httpd - Main component
+ *
+ *   Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#define _XOPEN_SOURCE 500      /* crypt() */
+
 #include "uhttpd.h"
 #include "uhttpd-utils.h"
 #include "uhttpd-file.h"
@@ -22,6 +42,43 @@ static void uh_sigterm(int sig)
        run = 0;
 }
 
+static void uh_config_parse(const char *path)
+{
+       FILE *c;
+       char line[512];
+       char *user = NULL;
+       char *pass = NULL;
+       char *eol  = NULL;
+
+       if( (c = fopen(path ? path : "/etc/httpd.conf", "r")) != NULL )
+       {
+               memset(line, 0, sizeof(line));
+
+               while( fgets(line, sizeof(line) - 1, c) )
+               {
+                       if( (line[0] == '/') && (strchr(line, ':') != NULL) )
+                       {
+                               if( !(user = strchr(line, ':')) || (*user++ = 0) ||
+                                   !(pass = strchr(user, ':')) || (*pass++ = 0) ||
+                                       !(eol = strchr(pass, '\n')) || (*eol++  = 0) )
+                                               continue;
+
+                               if( !uh_auth_add(line, user, pass) )
+                               {
+                                       fprintf(stderr,
+                                               "Can not manage more than %i basic auth realms, "
+                                               "will skip the rest\n", UH_LIMIT_AUTHREALMS
+                                       );
+
+                                       break;
+                               } 
+                       }
+               }
+
+               fclose(c);
+       }
+}
+
 static int uh_socket_bind(
        fd_set *serv_fds, int *max_fd, const char *host, const char *port,
        struct addrinfo *hints, int do_tls, struct config *conf
@@ -250,7 +307,7 @@ static struct http_request * uh_http_header_parse(struct client *cl, char *buffe
 
 static struct http_request * uh_http_header_recv(struct client *cl)
 {
-       char buffer[UH_LIMIT_MSGHEAD];
+       static char buffer[UH_LIMIT_MSGHEAD];
        char *bufptr = &buffer[0];
        char *idxptr = NULL;
 
@@ -315,32 +372,17 @@ static struct http_request * uh_http_header_recv(struct client *cl)
        return NULL;
 }
 
-static int uh_docroot_resolve(const char *path, char *buf)
+static int uh_path_match(const char *prefix, const char *url)
 {
-       char curpath[PATH_MAX];
-
-       if( ! getcwd(curpath, sizeof(curpath)) )
-       {
-               perror("getcwd()");
-               return 0;
-       }
-
-       if( chdir(path) || !getcwd(buf, PATH_MAX) )
-       {
-               return 0;
-       }
-       else
-       {
-               buf[strlen(buf)] = '/';
-       }
-
-       if( chdir(curpath) )
-       {
-               perror("chdir()");
-               return 0;
+       if( (strstr(url, prefix) == url) &&
+           ((prefix[strlen(prefix)-1] == '/') ||
+                (strlen(url) == strlen(prefix))   ||
+                (url[strlen(prefix)] == '/'))
+       ) {
+               return 1;
        }
 
-       return 1;
+       return 0;
 }
 
 
@@ -357,6 +399,7 @@ int main (int argc, char **argv)
        /* working structs */
        struct addrinfo hints;
        struct http_request *req;
+       struct path_info *pin;
        struct client *cl;
        struct sigaction sa;
        struct config conf;
@@ -407,7 +450,7 @@ int main (int argc, char **argv)
        }
 #endif
 
-       while( (opt = getopt(argc, argv, "fC:K:p:s:h:c:l:L:d:")) > 0 )
+       while( (opt = getopt(argc, argv, "fC:K:p:s:h:c:l:L:d:r:m:x:")) > 0 )
        {
                switch(opt)
                {
@@ -467,16 +510,16 @@ int main (int argc, char **argv)
 
                        /* docroot */
                        case 'h':
-                               if( ! uh_docroot_resolve(optarg, conf.docroot) )
+                               if( ! realpath(optarg, conf.docroot) )
                                {
-                                       fprintf(stderr, "Invalid directory: %s\n", optarg);
+                                       fprintf(stderr, "Invalid directory %s: %s\n", optarg, strerror(errno));
                                        exit(1);
                                }
                                break;
 
 #ifdef HAVE_CGI
                        /* cgi prefix */
-                       case 'c':
+                       case 'x':
                                conf.cgi_prefix = optarg;
                                break;
 #endif
@@ -510,25 +553,44 @@ int main (int argc, char **argv)
                                }
                                break;
 
+                       /* basic auth realm */
+                       case 'r':
+                               conf.realm = optarg;
+                               break;
+
+                       /* md5 crypt */
+                       case 'm':
+                               printf("%s\n", crypt(optarg, "$1$"));
+                               exit(0);
+                               break;
+
+                       /* config file */
+                       case 'c':
+                               conf.file = optarg;
+                               break;
+
                        default:
                                fprintf(stderr,
                                        "Usage: %s -p [addr:]port [-h docroot]\n"
-                                       "       -p      Bind to specified address and port, multiple allowed\n"
+                                       "       -f              Do not fork to background\n"
+                                       "       -c file         Configuration file, default is '/etc/httpd.conf'\n"
+                                       "       -p [addr:]port  Bind to specified address and port, multiple allowed\n"
 #ifdef HAVE_TLS
-                                       "       -s      Like -p but provide HTTPS on this port\n"
-                                       "       -C      ASN.1 server certificate file\n"
-                                       "       -K      ASN.1 server private key file\n"
+                                       "       -s [addr:]port  Like -p but provide HTTPS on this port\n"
+                                       "       -C file         ASN.1 server certificate file\n"
+                                       "       -K file         ASN.1 server private key file\n"
 #endif
-                                       "       -h      Specify the document root, default is '.'\n"
-                                       "       -f      Do not fork to background\n"
+                                       "       -h directory    Specify the document root, default is '.'\n"
 #ifdef HAVE_LUA
-                                       "       -l      URL prefix for Lua handler, default is '/lua'\n"
-                                       "       -L      Lua handler script, default is './lua/handler.lua'\n"
+                                       "       -l string       URL prefix for Lua handler, default is '/lua'\n"
+                                       "       -L file         Lua handler script, omit to disable Lua\n"
 #endif
 #ifdef HAVE_CGI
-                                       "       -c      URL prefix for CGI handler, default is '/cgi-bin'\n"
+                                       "       -x string       URL prefix for CGI handler, default is '/cgi-bin'\n"
 #endif
-                                       "       -d      URL decode given string\n"
+                                       "       -d string       URL decode given string\n"
+                                       "       -r string       Specify basic auth realm\n"
+                                       "       -m string       MD5 crypt given string\n"
                                        "\n", argv[0]
                                );
 
@@ -551,12 +613,20 @@ int main (int argc, char **argv)
        }
 
        /* default docroot */
-       if( !conf.docroot[0] && !uh_docroot_resolve(".", conf.docroot) )
+       if( !conf.docroot[0] && !realpath(".", conf.docroot) )
        {
-               fprintf(stderr, "Can not determine default document root\n");
+               fprintf(stderr, "Can not determine default document root: %s\n",
+                       strerror(errno));
                exit(1);
        }
 
+       /* default realm */
+       if( ! conf.realm )
+               conf.realm = "Protected Area";
+
+       /* config file */
+       uh_config_parse(conf.file);
+
 #ifdef HAVE_CGI
        /* default cgi prefix */
        if( ! conf.cgi_prefix )
@@ -673,31 +743,50 @@ int main (int argc, char **argv)
                                                goto cleanup;
                                        }
 
-                                       /* parse message header and dispatch request */
+                                       /* parse message header */
                                        if( (req = uh_http_header_recv(cl)) != NULL )
                                        {
-#ifdef HAVE_CGI
-                                               if( strstr(req->url, conf.cgi_prefix) == req->url )
-                                               {
-                                                       uh_cgi_request(cl, req);
-                                               }
-                                               else
-#endif
-
 #ifdef HAVE_LUA
-                                               if( (L != NULL) &&
-                                                   (strstr(req->url, conf.lua_prefix) == req->url)
-                                               {
+                                               /* Lua request? */
+                                               if( L && uh_path_match(conf.lua_prefix, req->url) )
+                                               {
                                                        uh_lua_request(cl, req, L);
                                                }
                                                else
 #endif
+                                               /* dispatch request */
+                                               if( (pin = uh_path_lookup(cl, req->url)) != NULL )
+                                               {
+                                                       /* auth ok? */
+                                                       if( uh_auth_check(cl, req, pin) )
+                                                       {
+#ifdef HAVE_CGI
+                                                               if( uh_path_match(conf.cgi_prefix, pin->name) )
+                                                               {
+                                                                       uh_cgi_request(cl, req, pin);
+                                                               }
+                                                               else
+#endif
+                                                               {
+                                                                       uh_file_request(cl, req, pin);
+                                                               }
+                                                       }
+                                               }
 
+                                               /* 404 */
+                                               else
                                                {
-                                                       uh_file_request(cl, req);
+                                                       uh_http_sendhf(cl, 404, "Not Found",
+                                                               "No such file or directory");
                                                }
                                        }
 
+                                       /* 400 */
+                                       else
+                                       {
+                                               uh_http_sendhf(cl, 400, "Bad Request",
+                                                       "Malformed request received");
+                                       }
 
 #ifdef HAVE_TLS
                                        /* free client tls context */