Pārlūkot izejas kodu

Merge branch 'cgi' of https://github.com/civetweb/civetweb

- 4 gadi atpakaļ
vecāks
revīzija
77329cd0a6
3 mainītis faili ar 179 papildinājumiem un 67 dzēšanām
  1. 14 7
      docs/UserManual.md
  2. 137 58
      src/civetweb.c
  3. 28 2
      src/main.c

+ 14 - 7
docs/UserManual.md

@@ -250,6 +250,13 @@ allows CGI files be anywhere. To restrict CGIs to a certain directory,
 use `/path/to/cgi-bin/**.cgi` as the pattern. Note that the full file path is
 matched against the pattern, not the URI.
 
+Depending on the build configuration, additional patterns `cgi2_pattern`,
+`cgi3_pattern` and `cgi4_pattern` may be available.
+This allows to use different cgi interpreter programs (`cgi2_interpreter`,
+...), environments (`cgi2_environment` ...) and interpreter arguments
+(`cgi2_interpreter_argument`, ...). The default for all additional CGI file
+patterns is empty - they are not used unless they are configured explicitly.
+
 ### cgi\_timeout\_ms
 Maximum allowed runtime for CGI scripts.  CGI processes are terminated by
 the server after this time.  The default is "no timeout", so scripts may
@@ -441,12 +448,12 @@ communicate the port number to clients via other means, for example mDNS
 In case the server has been built with the `USE_X_DOM_SOCKET` option set,
 it can listen to unix domain sockets as well. They are specified by a
 lower case `x` followed by the domain socket path, e.g. `x/tmp/sockname`.
-Domain sockets do not require a port number, always use HTTP (not HTTPS) 
-and never redirect. Thus `:` is not allowed, while `r` or `s` at the end 
+Domain sockets do not require a port number, always use HTTP (not HTTPS)
+and never redirect. Thus `:` is not allowed, while `r` or `s` at the end
 of the configuration is interpreted as part of the domain socket path.
-The domain sochet path must be a valid path to a non-existing file on a 
+The domain sochet path must be a valid path to a non-existing file on a
 Unix/Linux system. The CivetWeb process needs write/create access rights
-to create the domain socket in the Unix/Linux file system. 
+to create the domain socket in the Unix/Linux file system.
 Use only alphanumerical characters, underscore and `/` in a domain socket
 path (in particular, `,;:` must be avoided).
 
@@ -463,8 +470,8 @@ files, ...), check for external resources, remove old log files, etc.
 
 The Lua state remains open until the server is stopped.
 
-For a detailed descriotion of available Lua callbacks see section 
-"Lua background script" below. 
+For a detailed descriotion of available Lua callbacks see section
+"Lua background script" below.
 
 ### lua\_background\_script\_params
 Can add dynamic parameters to background script.
@@ -1037,7 +1044,7 @@ before any client is able to connect. It can be used for preparation and
 maintenance tasks, e.g., for preparing the web contents, cleaning log files,
 etc.
 
-The Name of the script file including path is configured as `lua_background_script`. 
+The Name of the script file including path is configured as `lua_background_script`.
 Additional parameters can be supplied using `lua_background_script_params`.
 
 The background script is loaded before the server is ready to start.

+ 137 - 58
src/civetweb.c

@@ -2099,19 +2099,40 @@ enum {
 	DOCUMENT_ROOT,
 
 	CGI_EXTENSIONS,
-	CGI2_EXTENSIONS,
 	CGI_ENVIRONMENT,
-	CGI2_ENVIRONMENT,
 	CGI_INTERPRETER,
-	CGI2_INTERPRETER,
 	CGI_INTERPRETER_ARGS,
-	CGI2_INTERPRETER_ARGS,
 #if defined(USE_TIMERS)
 	CGI_TIMEOUT,
+#endif
+
+	CGI2_EXTENSIONS,
+	CGI2_ENVIRONMENT,
+	CGI2_INTERPRETER,
+	CGI2_INTERPRETER_ARGS,
+#if defined(USE_TIMERS)
 	CGI2_TIMEOUT,
 #endif
 
-	PUT_DELETE_PASSWORDS_FILE,
+#if defined(USE_4_CGI)
+	CGI3_EXTENSIONS,
+	CGI3_ENVIRONMENT,
+	CGI3_INTERPRETER,
+	CGI3_INTERPRETER_ARGS,
+#if defined(USE_TIMERS)
+	CGI3_TIMEOUT,
+#endif
+
+	CGI4_EXTENSIONS,
+	CGI4_ENVIRONMENT,
+	CGI4_INTERPRETER,
+	CGI4_INTERPRETER_ARGS,
+#if defined(USE_TIMERS)
+	CGI4_TIMEOUT,
+#endif
+#endif
+
+	PUT_DELETE_PASSWORDS_FILE, /* must follow CGI_* */
 	PROTECT_URI,
 	AUTHENTICATION_DOMAIN,
 	ENABLE_AUTH_DOMAIN_CHECK,
@@ -2218,18 +2239,39 @@ static const struct mg_option config_options[] = {
     {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
 
     {"cgi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
-    {"cgi2_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
     {"cgi_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
-    {"cgi2_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
     {"cgi_interpreter", MG_CONFIG_TYPE_FILE, NULL},
-    {"cgi2_interpreter", MG_CONFIG_TYPE_FILE, NULL},
     {"cgi_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
-    {"cgi2_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
 #if defined(USE_TIMERS)
     {"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
+#endif
+
+    {"cgi2_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
+    {"cgi2_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
+    {"cgi2_interpreter", MG_CONFIG_TYPE_FILE, NULL},
+    {"cgi2_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
+#if defined(USE_TIMERS)
     {"cgi2_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
 #endif
 
+#if defined(USE_4_CGI)
+    {"cgi3_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
+    {"cgi3_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
+    {"cgi3_interpreter", MG_CONFIG_TYPE_FILE, NULL},
+    {"cgi3_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
+#if defined(USE_TIMERS)
+    {"cgi3_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
+#endif
+
+    {"cgi2_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
+    {"cgi4_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
+    {"cgi4_interpreter", MG_CONFIG_TYPE_FILE, NULL},
+    {"cgi4_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
+#if defined(USE_TIMERS)
+    {"cgi4_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
+#endif
+#endif
+
     {"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL},
     {"protect_uri", MG_CONFIG_TYPE_STRING_LIST, NULL},
     {"authentication_domain", MG_CONFIG_TYPE_STRING, "mydomain.com"},
@@ -5548,7 +5590,8 @@ spawn_process(struct mg_connection *conn,
               int fdin[2],
               int fdout[2],
               int fderr[2],
-              const char *dir)
+              const char *dir,
+              unsigned char cgi_config_idx)
 {
 	HANDLE me;
 	char *interp;
@@ -5605,11 +5648,12 @@ spawn_process(struct mg_connection *conn,
 
 	/* First check, if there is a CGI interpreter configured for all CGI
 	 * scripts. */
-	interp = conn->dom_ctx->config[CGI_INTERPRETER];
+	interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx];
 	if (interp != NULL) {
 		/* If there is a configured interpreter, check for additional arguments
 		 */
-		interp_arg = conn->dom_ctx->config[CGI_INTERPRETER_ARGS];
+		interp_arg =
+		    conn->dom_ctx->config[CGI_INTERPRETER_ARGS + cgi_config_idx];
 	} else {
 		/* Otherwise, the interpreter must be stated in the first line of the
 		 * CGI script file, after a #! (shebang) mark. */
@@ -5858,7 +5902,8 @@ spawn_process(struct mg_connection *conn,
               int fdin[2],
               int fdout[2],
               int fderr[2],
-              const char *dir)
+              const char *dir,
+              unsigned char cgi_config_idx)
 {
 	pid_t pid;
 	const char *interp;
@@ -5921,7 +5966,7 @@ spawn_process(struct mg_connection *conn,
 			sa.sa_handler = SIG_DFL;
 			sigaction(SIGCHLD, &sa, NULL);
 
-			interp = conn->dom_ctx->config[CGI_INTERPRETER];
+			interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx];
 			if (interp == NULL) {
 				/* no interpreter configured, call the programm directly */
 				(void)execle(prog, prog, NULL, envp);
@@ -5933,7 +5978,8 @@ spawn_process(struct mg_connection *conn,
 			} else {
 				/* call the configured interpreter */
 				const char *interp_args =
-				    conn->dom_ctx->config[CGI_INTERPRETER_ARGS];
+				    conn->dom_ctx
+				        ->config[CGI_INTERPRETER_ARGS + cgi_config_idx];
 
 				if ((interp_args != NULL) && (interp_args[0] != 0)) {
 					(void)execle(interp, interp, interp_args, prog, NULL, envp);
@@ -7391,15 +7437,9 @@ extention_matches_script(
 )
 {
 #if !defined(NO_CGI)
-	if (match_prefix_strlen(conn->dom_ctx->config[CGI_EXTENSIONS], filename)
-	    > 0) {
-		return 1;
-	}
-	if (match_prefix_strlen(conn->dom_ctx->config[CGI2_EXTENSIONS], filename)
-	    > 0) {
-		return 1;
-	}
+	unsigned char cgi_config_idx, inc, max;
 #endif
+
 #if defined(USE_LUA)
 	if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
 	                        filename)
@@ -7414,6 +7454,19 @@ extention_matches_script(
 		return 1;
 	}
 #endif
+#if !defined(NO_CGI)
+	inc = CGI2_EXTENSIONS - CGI_EXTENSIONS;
+	max = PUT_DELETE_PASSWORDS_FILE - CGI_EXTENSIONS;
+	for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) {
+		if ((conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL)
+		    && (match_prefix_strlen(
+		            conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx],
+		            filename)
+		        > 0)) {
+			return 1;
+		}
+	}
+#endif
 	/* filename and conn could be unused, if all preocessor conditions
 	 * are false (no script language supported). */
 	(void)filename;
@@ -11023,7 +11076,8 @@ addenv(struct cgi_environment *env, const char *fmt, ...)
 static int
 prepare_cgi_environment(struct mg_connection *conn,
                         const char *prog,
-                        struct cgi_environment *env)
+                        struct cgi_environment *env,
+                        unsigned char cgi_config_idx)
 {
 	const char *s;
 	struct vec var_vec;
@@ -11198,7 +11252,7 @@ prepare_cgi_environment(struct mg_connection *conn,
 	}
 
 	/* Add user-specified variables */
-	s = conn->dom_ctx->config[CGI_ENVIRONMENT];
+	s = conn->dom_ctx->config[CGI_ENVIRONMENT + cgi_config_idx];
 	while ((s = next_option(s, &var_vec, NULL)) != NULL) {
 		addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr);
 	}
@@ -11252,7 +11306,9 @@ abort_cgi_process(void *data)
 
 /* Local (static) function assumes all arguments are valid. */
 static void
-handle_cgi_request(struct mg_connection *conn, const char *prog)
+handle_cgi_request(struct mg_connection *conn,
+                   const char *prog,
+                   unsigned char cgi_config_idx)
 {
 	char *buf;
 	size_t buflen;
@@ -11269,9 +11325,10 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
 
 #if defined(USE_TIMERS)
 	double cgi_timeout;
-	if (conn->dom_ctx->config[CGI_TIMEOUT]) {
+	if (conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) {
 		/* Get timeout in seconds */
-		cgi_timeout = atof(conn->dom_ctx->config[CGI_TIMEOUT]) * 0.001;
+		cgi_timeout =
+		    atof(conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) * 0.001;
 	} else {
 		cgi_timeout =
 		    atof(config_options[REQUEST_TIMEOUT].default_value) * 0.001;
@@ -11281,7 +11338,7 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
 
 	buf = NULL;
 	buflen = conn->phys_ctx->max_request_size;
-	i = prepare_cgi_environment(conn, prog, &blk);
+	i = prepare_cgi_environment(conn, prog, &blk, cgi_config_idx);
 	if (i != 0) {
 		blk.buf = NULL;
 		blk.var = NULL;
@@ -11330,7 +11387,8 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
 	}
 
 	DEBUG_TRACE("CGI: spawn %s %s\n", dir, p);
-	pid = spawn_process(conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir);
+	pid = spawn_process(
+	    conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir, cgi_config_idx);
 
 	if (pid == (pid_t)-1) {
 		status = strerror(ERRNO);
@@ -14502,15 +14560,18 @@ handle_file_based_request(struct mg_connection *conn,
                           const char *path,
                           struct mg_file *file)
 {
+#if !defined(NO_CGI)
+	unsigned char cgi_config_idx, inc, max;
+#endif
+
 	if (!conn || !conn->dom_ctx) {
 		return;
 	}
 
-	if (0) {
 #if defined(USE_LUA)
-	} else if (match_prefix_strlen(
-	               conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS], path)
-	           > 0) {
+	if (match_prefix_strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
+	                        path)
+	    > 0) {
 		if (is_in_script_path(conn, path)) {
 			/* Lua server page: an SSI like page containing mostly plain
 			 * html code plus some tags with server generated contents. */
@@ -14519,25 +14580,27 @@ handle_file_based_request(struct mg_connection *conn,
 			/* Script was in an illegal path */
 			mg_send_http_error(conn, 403, "%s", "Forbidden");
 		}
+		return;
+	}
 
-	} else if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
-	                               path)
-	           > 0) {
+	if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS], path)
+	    > 0) {
 		if (is_in_script_path(conn, path)) {
 			/* Lua in-server module script: a CGI like script used to
-			 * generate
-			 * the
-			 * entire reply. */
+			 * generate the entire reply. */
 			mg_exec_lua_script(conn, path, NULL);
 		} else {
 			/* Script was in an illegal path */
 			mg_send_http_error(conn, 403, "%s", "Forbidden");
 		}
+		return;
+	}
 #endif
+
 #if defined(USE_DUKTAPE)
-	} else if (match_prefix_strlen(
-	               conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS], path)
-	           > 0) {
+	if (match_prefix_strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
+	                        path)
+	    > 0) {
 		if (is_in_script_path(conn, path)) {
 			/* Call duktape to generate the page */
 			mg_exec_duktape_script(conn, path);
@@ -14545,35 +14608,51 @@ handle_file_based_request(struct mg_connection *conn,
 			/* Script was in an illegal path */
 			mg_send_http_error(conn, 403, "%s", "Forbidden");
 		}
+		return;
+	}
 #endif
+
 #if !defined(NO_CGI)
-	} else if (match_prefix_strlen(conn->dom_ctx->config[CGI_EXTENSIONS], path)
-	           > 0) {
-		if (is_in_script_path(conn, path)) {
-			/* CGI scripts may support all HTTP methods */
-			handle_cgi_request(conn, path);
-		} else {
-			/* Script was in an illegal path */
-			mg_send_http_error(conn, 403, "%s", "Forbidden");
+	inc = CGI2_EXTENSIONS - CGI_EXTENSIONS;
+	max = PUT_DELETE_PASSWORDS_FILE - CGI_EXTENSIONS;
+	for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) {
+		if (conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL) {
+			if (match_prefix_strlen(
+			        conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx],
+			        path)
+			    > 0) {
+				if (is_in_script_path(conn, path)) {
+					/* CGI scripts may support all HTTP methods */
+					handle_cgi_request(conn, path, 0);
+				} else {
+					/* Script was in an illegal path */
+					mg_send_http_error(conn, 403, "%s", "Forbidden");
+				}
+				return;
+			}
 		}
+	}
 #endif /* !NO_CGI */
-	} else if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path)
-	           > 0) {
+
+	if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path) > 0) {
 		if (is_in_script_path(conn, path)) {
 			handle_ssi_file_request(conn, path, file);
 		} else {
 			/* Script was in an illegal path */
 			mg_send_http_error(conn, 403, "%s", "Forbidden");
 		}
+		return;
+	}
+
 #if !defined(NO_CACHING)
-	} else if ((!conn->in_error_handler)
-	           && is_not_modified(conn, &file->stat)) {
+	if ((!conn->in_error_handler) && is_not_modified(conn, &file->stat)) {
 		/* Send 304 "Not Modified" - this must not send any body data */
 		handle_not_modified_static_file_request(conn, file);
-#endif /* !NO_CACHING */
-	} else {
-		handle_static_file_request(conn, path, file, NULL, NULL);
+		return;
 	}
+#endif /* !NO_CACHING */
+
+	handle_static_file_request(conn, path, file, NULL, NULL);
 }
 #endif /* NO_FILESYSTEMS */
 

+ 28 - 2
src/main.c

@@ -2276,6 +2276,32 @@ add_control(struct dlg_complete *dlg,
 }
 
 
+static int
+optioncmp(const char *o1, const char *o2)
+{
+	/* string compare for option names */
+	while (*o1 || *o2) {
+		int c1 = 256 * (int)*o1;
+		int c2 = 256 * (int)*o2;
+		if (isalpha(*o1))
+			c1 = toupper(*o1);
+		else if (*o1 == '_')
+			c1 = 1;
+		if (isalpha(*o2))
+			c2 = toupper(*o2);
+		else if (*o2 == '_')
+			c2 = 1;
+		if (c1 < c2)
+			return -1;
+		if (c1 > c2)
+			return +1;
+		o1++;
+		o2++;
+	}
+	return 0;
+}
+
+
 static void
 show_settings_dialog()
 {
@@ -2352,8 +2378,8 @@ show_settings_dialog()
 	for (;;) {
 		int swapped = 0;
 		for (i = 1; i < NO_OF_OPTIONS; i++) {
-			if (strcmp(cv_options[option_index[i - 1]].name,
-			           cv_options[option_index[i]].name)
+			if (optioncmp(cv_options[option_index[i - 1]].name,
+			              cv_options[option_index[i]].name)
 			    > 0) {
 				short swap = option_index[i];
 				option_index[i] = option_index[i - 1];