Browse Source

uhttpd: - fix a compile warning - support custom index file names - support custom error pages (or cgi handler) - add option to disable directory listings - add REDIRECT_STATUS for CGI requests, fixes php-cgi

SVN-Revision: 22366
Jo-Philipp Wich 15 years ago
parent
commit
b688fcaa5c

+ 1 - 1
package/uhttpd/Makefile

@@ -8,7 +8,7 @@
 include $(TOPDIR)/rules.mk
 include $(TOPDIR)/rules.mk
 
 
 PKG_NAME:=uhttpd
 PKG_NAME:=uhttpd
-PKG_RELEASE:=11
+PKG_RELEASE:=12
 
 
 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
 
 

+ 16 - 0
package/uhttpd/files/uhttpd.init

@@ -17,6 +17,17 @@ append_arg() {
 	[ -n "$val" -o -n "$def" ] && append UHTTPD_ARGS "$opt ${val:-$def}"
 	[ -n "$val" -o -n "$def" ] && append UHTTPD_ARGS "$opt ${val:-$def}"
 }
 }
 
 
+append_bool() {
+	local cfg="$1"
+	local var="$2"
+	local opt="$3"
+	local def="$4"
+	local val
+
+	config_get_bool val "$cfg" "$var" "$def"
+	[ "$val" = 1 ] && append UHTTPD_ARGS "$opt"
+}
+
 generate_keys() {
 generate_keys() {
 	local cfg="$1"
 	local cfg="$1"
 	local key="$2"
 	local key="$2"
@@ -59,6 +70,11 @@ start_instance()
 	append_arg "$cfg" lua_handler "-L"
 	append_arg "$cfg" lua_handler "-L"
 	append_arg "$cfg" script_timeout "-t"
 	append_arg "$cfg" script_timeout "-t"
 	append_arg "$cfg" network_timeout "-T"
 	append_arg "$cfg" network_timeout "-T"
+	append_arg "$cfg" error_page "-E"
+	append_arg "$cfg" index_page "-I"
+
+	append_bool "$cfg" no_symlinks "-S" 0
+	append_bool "$cfg" no_dirlists "-D" 0
 
 
 	config_get http "$cfg" listen_http
 	config_get http "$cfg" listen_http
 	for listen in $http; do
 	for listen in $http; do

+ 11 - 0
package/uhttpd/src/uhttpd-cgi.c

@@ -234,6 +234,17 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf
 				if( pi->info )
 				if( pi->info )
 					setenv("PATH_INFO", pi->info, 1);
 					setenv("PATH_INFO", pi->info, 1);
 
 
+				/* REDIRECT_STATUS, php-cgi wants it */
+				switch( req->redirect_status )
+				{
+					case 404:
+						setenv("REDIRECT_STATUS", "404", 1);
+						break;
+
+					default:
+						setenv("REDIRECT_STATUS", "200", 1);
+						break;
+				}
 
 
 				/* http version */
 				/* http version */
 				if( req->version > 1.0 )
 				if( req->version > 1.0 )

+ 8 - 4
package/uhttpd/src/uhttpd-file.c

@@ -29,7 +29,7 @@
 static const char * uh_file_mime_lookup(const char *path)
 static const char * uh_file_mime_lookup(const char *path)
 {
 {
 	struct mimetype *m = &uh_mime_types[0];
 	struct mimetype *m = &uh_mime_types[0];
-	char *e;
+	const char *e;
 
 
 	while( m->extn )
 	while( m->extn )
 	{
 	{
@@ -275,7 +275,9 @@ static void uh_file_dirlist(struct client *cl, struct http_request *req, struct
 			strncat(filename, files[i]->d_name,
 			strncat(filename, files[i]->d_name,
 				sizeof(filename) - strlen(files[i]->d_name));
 				sizeof(filename) - strlen(files[i]->d_name));
 
 
-			if( !stat(filename, &s) && (s.st_mode & S_IFDIR) )
+			if( !stat(filename, &s) &&
+			    (s.st_mode & S_IFDIR) && (s.st_mode & S_IXOTH)
+			)
 				uh_http_sendf(cl, req,
 				uh_http_sendf(cl, req,
 					"<li><strong><a href='%s%s'>%s</a>/</strong><br />"
 					"<li><strong><a href='%s%s'>%s</a>/</strong><br />"
 					"<small>modified: %s<br />directory - %.02f kbyte"
 					"<small>modified: %s<br />directory - %.02f kbyte"
@@ -293,7 +295,9 @@ static void uh_file_dirlist(struct client *cl, struct http_request *req, struct
 			strncat(filename, files[i]->d_name,
 			strncat(filename, files[i]->d_name,
 				sizeof(filename) - strlen(files[i]->d_name));
 				sizeof(filename) - strlen(files[i]->d_name));
 
 
-			if( !stat(filename, &s) && !(s.st_mode & S_IFDIR) )
+			if( !stat(filename, &s) &&
+			    !(s.st_mode & S_IFDIR) && (s.st_mode & S_IROTH)
+			)
 				uh_http_sendf(cl, req,
 				uh_http_sendf(cl, req,
 					"<li><strong><a href='%s%s'>%s</a></strong><br />"
 					"<li><strong><a href='%s%s'>%s</a></strong><br />"
 					"<small>modified: %s<br />%s - %.02f kbyte<br />"
 					"<small>modified: %s<br />%s - %.02f kbyte<br />"
@@ -369,7 +373,7 @@ void uh_file_request(struct client *cl, struct http_request *req, struct path_in
 	}
 	}
 
 
 	/* directory */
 	/* directory */
-	else if( pi->stat.st_mode & S_IFDIR )
+	else if( (pi->stat.st_mode & S_IFDIR) && !cl->server->conf->no_dirlists )
 	{
 	{
 		/* write status */
 		/* write status */
 		uh_file_response_200(cl, req, NULL);
 		uh_file_response_200(cl, req, NULL);

+ 20 - 4
package/uhttpd/src/uhttpd-utils.c

@@ -464,6 +464,9 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url)
 	int i = 0;
 	int i = 0;
 	struct stat s;
 	struct stat s;
 
 
+	/* back out early if url is undefined */
+	if ( url == NULL )
+		return NULL;
 
 
 	memset(path_phys, 0, sizeof(path_phys));
 	memset(path_phys, 0, sizeof(path_phys));
 	memset(path_info, 0, sizeof(path_info));
 	memset(path_info, 0, sizeof(path_info));
@@ -550,18 +553,31 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url)
 			memcpy(buffer, path_phys, sizeof(buffer));
 			memcpy(buffer, path_phys, sizeof(buffer));
 			pathptr = &buffer[strlen(buffer)];
 			pathptr = &buffer[strlen(buffer)];
 
 
-			for( i = 0; i < array_size(uh_index_files); i++ )
+			if( cl->server->conf->index_file )
 			{
 			{
-				strncat(buffer, uh_index_files[i], sizeof(buffer));
+				strncat(buffer, cl->server->conf->index_file, sizeof(buffer));
 
 
 				if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
 				if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
 				{
 				{
 					memcpy(path_phys, buffer, sizeof(path_phys));
 					memcpy(path_phys, buffer, sizeof(path_phys));
 					memcpy(&p.stat, &s, sizeof(p.stat));
 					memcpy(&p.stat, &s, sizeof(p.stat));
-					break;
 				}
 				}
+			}
+			else
+			{
+				for( i = 0; i < array_size(uh_index_files); i++ )
+				{
+					strncat(buffer, uh_index_files[i], sizeof(buffer));
 
 
-				*pathptr = 0;
+					if( !stat(buffer, &s) && (s.st_mode & S_IFREG) )
+					{
+						memcpy(path_phys, buffer, sizeof(path_phys));
+						memcpy(&p.stat, &s, sizeof(p.stat));
+						break;
+					}
+
+					*pathptr = 0;
+				}
 			}
 			}
 
 
 			p.root = docroot;
 			p.root = docroot;

+ 79 - 9
package/uhttpd/src/uhttpd.c

@@ -47,7 +47,7 @@ static void uh_sigchld(int sig)
 	while( waitpid(-1, NULL, WNOHANG) > 0 ) { }
 	while( waitpid(-1, NULL, WNOHANG) > 0 ) { }
 }
 }
 
 
-static void uh_config_parse(const char *path)
+static void uh_config_parse(struct config *conf)
 {
 {
 	FILE *c;
 	FILE *c;
 	char line[512];
 	char line[512];
@@ -55,7 +55,10 @@ static void uh_config_parse(const char *path)
 	char *pass = NULL;
 	char *pass = NULL;
 	char *eol  = NULL;
 	char *eol  = NULL;
 
 
-	if( (c = fopen(path ? path : "/etc/httpd.conf", "r")) != NULL )
+	const char *path = conf->file ? conf->file : "/etc/httpd.conf";
+
+
+	if( (c = fopen(path, "r")) != NULL )
 	{
 	{
 		memset(line, 0, sizeof(line));
 		memset(line, 0, sizeof(line));
 
 
@@ -74,9 +77,23 @@ static void uh_config_parse(const char *path)
 						"Notice: No password set for user %s, ignoring "
 						"Notice: No password set for user %s, ignoring "
 						"authentication on %s\n", user, line
 						"authentication on %s\n", user, line
 					);
 					);
+				}
+			}
+			else if( !strncmp(line, "I:", 2) )
+			{
+				if( !(user = strchr(line, ':')) || (*user++ = 0) ||
+				    !(eol = strchr(user, '\n')) || (*eol++  = 0) )
+				    	continue;
 
 
-					break;
-				} 
+				conf->index_file = strdup(user);
+			}
+			else if( !strncmp(line, "E404:", 5) )
+			{
+				if( !(user = strchr(line, ':')) || (*user++ = 0) ||
+				    !(eol = strchr(user, '\n')) || (*eol++  = 0) )
+						continue;
+
+				conf->error_handler = strdup(user);
 			}
 			}
 		}
 		}
 
 
@@ -302,6 +319,7 @@ static struct http_request * uh_http_header_parse(struct client *cl, char *buffe
 		}
 		}
 
 
 		/* valid enough */
 		/* valid enough */
+		req.redirect_status = 200;
 		return &req;
 		return &req;
 	}
 	}
 
 
@@ -505,8 +523,9 @@ int main (int argc, char **argv)
 	}
 	}
 #endif
 #endif
 
 
-	while( (opt = getopt(argc, argv, "fSC:K:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0 )
-	{
+	while( (opt = getopt(argc, argv,
+		"fSDC:K:E:I:p:s:h:c:l:L:d:r:m:x:t:T:")) > 0
+	) {
 		switch(opt)
 		switch(opt)
 		{
 		{
 			/* [addr:]port */
 			/* [addr:]port */
@@ -597,11 +616,38 @@ int main (int argc, char **argv)
 				}
 				}
 				break;
 				break;
 
 
+			/* error handler */
+			case 'E':
+				if( (strlen(optarg) == 0) || (optarg[0] != '/') )
+				{
+					fprintf(stderr, "Error: Invalid error handler: %s\n",
+						optarg);
+					exit(1);
+				}
+				conf.error_handler = optarg;
+				break;
+
+			/* index file */
+			case 'I':
+				if( (strlen(optarg) == 0) || (optarg[0] == '/') )
+				{
+					fprintf(stderr, "Error: Invalid index page: %s\n",
+						optarg);
+					exit(1);
+				}
+				conf.index_file = optarg;
+				break;
+
 			/* don't follow symlinks */
 			/* don't follow symlinks */
 			case 'S':
 			case 'S':
 				conf.no_symlinks = 1;
 				conf.no_symlinks = 1;
 				break;
 				break;
 
 
+			/* don't list directories */
+			case 'D':
+				conf.no_dirlists = 1;
+				break;
+
 #ifdef HAVE_CGI
 #ifdef HAVE_CGI
 			/* cgi prefix */
 			/* cgi prefix */
 			case 'x':
 			case 'x':
@@ -678,7 +724,10 @@ int main (int argc, char **argv)
 					"	-K file         ASN.1 server private key file\n"
 					"	-K file         ASN.1 server private key file\n"
 #endif
 #endif
 					"	-h directory    Specify the document root, default is '.'\n"
 					"	-h directory    Specify the document root, default is '.'\n"
+					"	-E string       Use given virtual URL as 404 error handler\n"
+					"	-I string       Use given filename as index page for directories\n"
 					"	-S              Do not follow symbolic links outside of the docroot\n"
 					"	-S              Do not follow symbolic links outside of the docroot\n"
+					"	-D              Do not allow directory listings, send 403 instead\n"
 #ifdef HAVE_LUA
 #ifdef HAVE_LUA
 					"	-l string       URL prefix for Lua handler, default is '/lua'\n"
 					"	-l string       URL prefix for Lua handler, default is '/lua'\n"
 					"	-L file         Lua handler script, omit to disable Lua\n"
 					"	-L file         Lua handler script, omit to disable Lua\n"
@@ -727,7 +776,7 @@ int main (int argc, char **argv)
 		conf.realm = "Protected Area";
 		conf.realm = "Protected Area";
 
 
 	/* config file */
 	/* config file */
-	uh_config_parse(conf.file);
+	uh_config_parse(&conf);
 
 
 	/* default network timeout */
 	/* default network timeout */
 	if( conf.network_timeout <= 0 )
 	if( conf.network_timeout <= 0 )
@@ -913,8 +962,29 @@ int main (int argc, char **argv)
 						/* 404 */
 						/* 404 */
 						else
 						else
 						{
 						{
-							uh_http_sendhf(cl, 404, "Not Found",
-								"No such file or directory");
+							/* Try to invoke an error handler */
+							pin = uh_path_lookup(cl, conf.error_handler);
+
+							if( pin && uh_auth_check(cl, req, pin) )
+							{
+								req->redirect_status = 404;
+
+#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);
+								}
+							}
+							else
+							{
+								uh_http_sendhf(cl, 404, "Not Found",
+									"No such file or directory");
+							}
 						}
 						}
 					}
 					}
 
 

+ 4 - 0
package/uhttpd/src/uhttpd.h

@@ -64,7 +64,10 @@ struct config {
 	char docroot[PATH_MAX];
 	char docroot[PATH_MAX];
 	char *realm;
 	char *realm;
 	char *file;
 	char *file;
+	char *index_file;
+	char *error_handler;
 	int no_symlinks;
 	int no_symlinks;
+	int no_dirlists;
 	int network_timeout;
 	int network_timeout;
 #ifdef HAVE_CGI
 #ifdef HAVE_CGI
 	char *cgi_prefix;
 	char *cgi_prefix;
@@ -124,6 +127,7 @@ struct auth_realm {
 struct http_request {
 struct http_request {
 	int	method;
 	int	method;
 	float version;
 	float version;
+	int redirect_status;
 	char *url;
 	char *url;
 	char *headers[UH_LIMIT_HEADERS];
 	char *headers[UH_LIMIT_HEADERS];
 	struct auth_realm *realm;
 	struct auth_realm *realm;