瀏覽代碼

Merge branch 'master' of https://github.com/civetweb/civetweb into cipher_list

Mateusz Gralka 9 年之前
父節點
當前提交
423aa19dd1
共有 1 個文件被更改,包括 204 次插入186 次删除
  1. 204 186
      src/civetweb.c

+ 204 - 186
src/civetweb.c

@@ -1849,19 +1849,19 @@ skip_quoted(char **buf,
 	if (end_word > begin_word) {
 		p = end_word - 1;
 		while (*p == quotechar) {
-			/* TODO (bel, low): it seems this code is never reached, so
-			 * quotechar is actually not needed - check if this code may be
-			 * droped */
+			/* While the delimiter is quoted, look for the next delimiter. */
+			/* This happens, e.g., in calls from parse_auth_header,
+			 * if the user name contains a " character. */
 
-			/* If there is anything beyond end_word, copy it */
-			if (*end_word == '\0') {
-				*p = '\0';
-				break;
-			} else {
+			/* If there is anything beyond end_word, copy it. */
+			if (*end_word != '\0') {
 				size_t end_off = strcspn(end_word + 1, delimiters);
 				memmove(p, end_word, end_off + 1);
 				p += end_off; /* p must correspond to end_word - 1 */
 				end_word += end_off + 1;
+			} else {
+				*p = '\0';
+				break;
 			}
 		}
 		for (p++; p < end_word; p++) {
@@ -2692,7 +2692,7 @@ mg_stat(struct mg_connection *conn, const char *path, struct file *filep)
 	if (conn && is_file_in_memory(conn, path, filep)) {
 		/* filep->is_directory = 0; filep->gzipped = 0; .. already done by
 		 * memset */
-		last_modified = time(NULL);
+		filep->last_modified = time(NULL);
 		/* last_modified = now ... assumes the file may change during runtime,
 		 * so every mg_fopen call may return different data */
 		/* last_modified = conn->ctx.start_time;
@@ -4199,7 +4199,7 @@ is_put_or_delete_method(const struct mg_connection *conn)
 }
 
 static void
-interpret_uri(struct mg_connection *conn,   /* in: request */
+interpret_uri(struct mg_connection *conn,   /* in: request (must be valid) */
               char *filename,               /* out: filename */
               size_t filename_buf_len,      /* in: size of filename buffer */
               struct file *filep,           /* out: file structure */
@@ -4209,202 +4209,188 @@ interpret_uri(struct mg_connection *conn,   /* in: request */
               int *is_put_or_delete_request /* out: put/delete a file? */
               )
 {
-	/* TODO (high): Restructure this function */
-	if (conn && conn->ctx) {
+/* TODO (high): Restructure this function */
 
 #if !defined(NO_FILES)
-		const char *uri = conn->request_info.local_uri;
-		const char *root = conn->ctx->config[DOCUMENT_ROOT];
-		const char *rewrite;
-		struct vec a, b;
-		int match_len;
-		char gz_path[PATH_MAX];
-		char const *accept_encoding;
-		int truncated;
+	const char *uri = conn->request_info.local_uri;
+	const char *root = conn->ctx->config[DOCUMENT_ROOT];
+	const char *rewrite;
+	struct vec a, b;
+	int match_len;
+	char gz_path[PATH_MAX];
+	char const *accept_encoding;
+	int truncated;
 #if !defined(NO_CGI) || defined(USE_LUA)
-		char *p;
+	char *p;
 #endif
 #else
-		(void)filename_buf_len; /* unused if NO_FILES is defined */
+	(void)filename_buf_len; /* unused if NO_FILES is defined */
 #endif
 
-		memset(filep, 0, sizeof(*filep));
-		*filename = 0;
-		*is_found = 0;
-		*is_script_resource = 0;
-		*is_put_or_delete_request = is_put_or_delete_method(conn);
+	memset(filep, 0, sizeof(*filep));
+	*filename = 0;
+	*is_found = 0;
+	*is_script_resource = 0;
+	*is_put_or_delete_request = is_put_or_delete_method(conn);
 
 #if defined(USE_WEBSOCKET)
-		*is_websocket_request = is_websocket_protocol(conn);
+	*is_websocket_request = is_websocket_protocol(conn);
 #if !defined(NO_FILES)
-		if (*is_websocket_request && conn->ctx->config[WEBSOCKET_ROOT]) {
-			root = conn->ctx->config[WEBSOCKET_ROOT];
-		}
+	if (*is_websocket_request && conn->ctx->config[WEBSOCKET_ROOT]) {
+		root = conn->ctx->config[WEBSOCKET_ROOT];
+	}
 #endif /* !NO_FILES */
 #else  /* USE_WEBSOCKET */
-		*is_websocket_request = 0;
+	*is_websocket_request = 0;
 #endif /* USE_WEBSOCKET */
 
 #if !defined(NO_FILES)
-		/* Note that root == NULL is a regular use case here. This occurs,
-		 * if all requests are handled by callbacks, so the WEBSOCKET_ROOT
-		 * config is not required. */
-		if (root == NULL) {
-			/* all file related outputs have already been set to 0, just return
-			 */
-			return;
-		}
+	/* Note that root == NULL is a regular use case here. This occurs,
+	 * if all requests are handled by callbacks, so the WEBSOCKET_ROOT
+	 * config is not required. */
+	if (root == NULL) {
+		/* all file related outputs have already been set to 0, just return
+		 */
+		return;
+	}
 
-		/* Using buf_len - 1 because memmove() for PATH_INFO may shift part
-		 * of the path one byte on the right.
-		 * If document_root is NULL, leave the file empty. */
-		mg_snprintf(conn,
-		            &truncated,
-		            filename,
-		            filename_buf_len - 1,
-		            "%s%s",
-		            root,
-		            uri);
+	/* Using buf_len - 1 because memmove() for PATH_INFO may shift part
+	 * of the path one byte on the right.
+	 * If document_root is NULL, leave the file empty. */
+	mg_snprintf(
+	    conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri);
 
-		if (truncated) {
-			goto interpret_cleanup;
-		}
+	if (truncated) {
+		goto interpret_cleanup;
+	}
 
-		rewrite = conn->ctx->config[REWRITE];
-		while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
-			if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
-				mg_snprintf(conn,
-				            &truncated,
-				            filename,
-				            filename_buf_len - 1,
-				            "%.*s%s",
-				            (int)b.len,
-				            b.ptr,
-				            uri + match_len);
-				break;
-			}
+	rewrite = conn->ctx->config[REWRITE];
+	while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
+		if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
+			mg_snprintf(conn,
+			            &truncated,
+			            filename,
+			            filename_buf_len - 1,
+			            "%.*s%s",
+			            (int)b.len,
+			            b.ptr,
+			            uri + match_len);
+			break;
 		}
+	}
 
-		if (truncated) {
-			goto interpret_cleanup;
-		}
+	if (truncated) {
+		goto interpret_cleanup;
+	}
 
-		/* Local file path and name, corresponding to requested URI
-		 * is now stored in "filename" variable. */
-		if (mg_stat(conn, filename, filep)) {
+	/* Local file path and name, corresponding to requested URI
+	 * is now stored in "filename" variable. */
+	if (mg_stat(conn, filename, filep)) {
 #if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
-			/* File exists. Check if it is a script type. */
-			if (0
+		/* File exists. Check if it is a script type. */
+		if (0
 #if !defined(NO_CGI)
-			    || match_prefix(conn->ctx->config[CGI_EXTENSIONS],
-			                    strlen(conn->ctx->config[CGI_EXTENSIONS]),
-			                    filename) > 0
+		    || match_prefix(conn->ctx->config[CGI_EXTENSIONS],
+		                    strlen(conn->ctx->config[CGI_EXTENSIONS]),
+		                    filename) > 0
 #endif
 #if defined(USE_LUA)
-			    || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
-			                    strlen(
-			                        conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
-			                    filename) > 0
+		    || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
+		                    strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
+		                    filename) > 0
 #endif
 #if defined(USE_DUKTAPE)
-			    || match_prefix(
-			           conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
-			           strlen(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
-			           filename) > 0
-#endif
-			    ) {
-				/* The request addresses a CGI script or a Lua script. The URI
-				 * corresponds to the script itself (like /path/script.cgi),
-				 * and there is no additional resource path
-				 * (like /path/script.cgi/something).
-				 * Requests that modify (replace or delete) a resource, like
-				 * PUT and DELETE requests, should replace/delete the script
-				 * file.
-				 * Requests that read or write from/to a resource, like GET and
-				 * POST requests, should call the script and return the
-				 * generated response. */
-				*is_script_resource = !*is_put_or_delete_request;
-			}
-#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
-			*is_found = 1;
-			return;
+		    || match_prefix(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
+		                    strlen(
+		                        conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
+		                    filename) > 0
+#endif
+		    ) {
+			/* The request addresses a CGI script or a Lua script. The URI
+			 * corresponds to the script itself (like /path/script.cgi),
+			 * and there is no additional resource path
+			 * (like /path/script.cgi/something).
+			 * Requests that modify (replace or delete) a resource, like
+			 * PUT and DELETE requests, should replace/delete the script
+			 * file.
+			 * Requests that read or write from/to a resource, like GET and
+			 * POST requests, should call the script and return the
+			 * generated response. */
+			*is_script_resource = !*is_put_or_delete_request;
 		}
+#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
+		*is_found = 1;
+		return;
+	}
 
-		/* If we can't find the actual file, look for the file
-		 * with the same name but a .gz extension. If we find it,
-		 * use that and set the gzipped flag in the file struct
-		 * to indicate that the response need to have the content-
-		 * encoding: gzip header.
-		 * We can only do this if the browser declares support. */
-		if ((accept_encoding = mg_get_header(conn, "Accept-Encoding"))
-		    != NULL) {
-			if (strstr(accept_encoding, "gzip") != NULL) {
-				mg_snprintf(conn,
-				            &truncated,
-				            gz_path,
-				            sizeof(gz_path),
-				            "%s.gz",
-				            filename);
-
-				if (truncated) {
-					goto interpret_cleanup;
-				}
+	/* If we can't find the actual file, look for the file
+	 * with the same name but a .gz extension. If we find it,
+	 * use that and set the gzipped flag in the file struct
+	 * to indicate that the response need to have the content-
+	 * encoding: gzip header.
+	 * We can only do this if the browser declares support. */
+	if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
+		if (strstr(accept_encoding, "gzip") != NULL) {
+			mg_snprintf(
+			    conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename);
 
-				if (mg_stat(conn, gz_path, filep)) {
-					if (filep) {
-						filep->gzipped = 1;
-						*is_found = 1;
-					}
-					/* Currently gz files can not be scripts. */
-					return;
+			if (truncated) {
+				goto interpret_cleanup;
+			}
+
+			if (mg_stat(conn, gz_path, filep)) {
+				if (filep) {
+					filep->gzipped = 1;
+					*is_found = 1;
 				}
+				/* Currently gz files can not be scripts. */
+				return;
 			}
 		}
+	}
 
 #if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
-		/* Support PATH_INFO for CGI scripts. */
-		for (p = filename + strlen(filename); p > filename + 1; p--) {
-			if (*p == '/') {
-				*p = '\0';
-				if ((0
+	/* Support PATH_INFO for CGI scripts. */
+	for (p = filename + strlen(filename); p > filename + 1; p--) {
+		if (*p == '/') {
+			*p = '\0';
+			if ((0
 #if !defined(NO_CGI)
-				     || match_prefix(conn->ctx->config[CGI_EXTENSIONS],
-				                     strlen(conn->ctx->config[CGI_EXTENSIONS]),
-				                     filename) > 0
+			     || match_prefix(conn->ctx->config[CGI_EXTENSIONS],
+			                     strlen(conn->ctx->config[CGI_EXTENSIONS]),
+			                     filename) > 0
 #endif
 #if defined(USE_LUA)
-				     || match_prefix(
-				            conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
-				            strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
-				            filename) > 0
+			     || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
+			                     strlen(
+			                         conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
+			                     filename) > 0
 #endif
 #if defined(USE_DUKTAPE)
-				     || match_prefix(
-				            conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
-				            strlen(
-				                conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
-				            filename) > 0
-#endif
-				     ) && mg_stat(conn, filename, filep)) {
-					/* Shift PATH_INFO block one character right, e.g.
-					 * "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
-					 * conn->path_info is pointing to the local variable "path"
-					 * declared in handle_request(), so PATH_INFO is not valid
-					 * after handle_request returns. */
-					conn->path_info = p + 1;
-					memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for
-					                                           * trailing \0 */
-					p[1] = '/';
-					*is_script_resource = 1;
-					break;
-				} else {
-					*p = '/';
-				}
+			     || match_prefix(
+			            conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
+			            strlen(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]),
+			            filename) > 0
+#endif
+			     ) && mg_stat(conn, filename, filep)) {
+				/* Shift PATH_INFO block one character right, e.g.
+				 * "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
+				 * conn->path_info is pointing to the local variable "path"
+				 * declared in handle_request(), so PATH_INFO is not valid
+				 * after handle_request returns. */
+				conn->path_info = p + 1;
+				memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for
+				                                           * trailing \0 */
+				p[1] = '/';
+				*is_script_resource = 1;
+				break;
+			} else {
+				*p = '/';
 			}
 		}
+	}
 #endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
 #endif /* !defined(NO_FILES) */
-	}
 	return;
 
 #if !defined(NO_FILES)
@@ -4414,7 +4400,9 @@ interpret_cleanup:
 	*filename = 0;
 	*is_found = 0;
 	*is_script_resource = 0;
-#endif
+	*is_websocket_request = 0;
+	*is_put_or_delete_request = 0;
+#endif /* !defined(NO_FILES) */
 }
 
 /* Check whether full request is buffered. Return:
@@ -4744,11 +4732,7 @@ check_password(const char *method,
 	}
 
 	/* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */
-	/* TODO(lsm): check for authentication timeout */
-	if (/* strcmp(dig->uri, c->ouri) != 0 || */
-	    strlen(response) != 32
-	    /* || now - strtoul(dig->nonce, NULL, 10) > 3600 */
-	    ) {
+	if (strlen(response) != 32) {
 		return 0;
 	}
 
@@ -4831,11 +4815,13 @@ open_auth_file(struct mg_connection *conn, const char *path, struct file *filep)
 	}
 }
 
+
 /* Parsed Authorization header */
 struct ah {
 	char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
 };
 
+
 /* Return 1 on success. Always initializes the ah structure. */
 static int
 parse_auth_header(struct mg_connection *conn,
@@ -4901,12 +4887,7 @@ parse_auth_header(struct mg_connection *conn,
 	}
 
 #ifndef NO_NONCE_CHECK
-	/* Convert the nonce from the client to a number and check it. */
-	/* Server side nonce check is valuable in all situations but one: if the
-	 * server restarts frequently,
-	 * but the client should not see that, so the server should accept nonces
-	 * from
-	 * previous starts. */
+	/* Read the nonce from the response. */
 	if (ah->nonce == NULL) {
 		return 0;
 	}
@@ -4915,12 +4896,25 @@ parse_auth_header(struct mg_connection *conn,
 	if ((s == NULL) || (*s != 0)) {
 		return 0;
 	}
+
+	/* Convert the nonce from the client to a number. */
 	nonce ^= (uintptr_t)(conn->ctx);
+
+	/* The converted number corresponds to the time the nounce has been
+	 * created. This should not be earlier than the server start. */
+	/* Server side nonce check is valuable in all situations but one:
+	 * if the server restarts frequently, but the client should not see
+	 * that, so the server should accept nonces from previous starts. */
+	/* However, the reasonable default is to not accept a nonce from a
+	 * previous start, so if anyone changed the access rights between
+	 * two restarts, a new login is required. */
 	if (nonce < conn->ctx->start_time) {
 		/* nonce is from a previous start of the server and no longer valid
 		 * (replay attack?) */
 		return 0;
 	}
+	/* Check if the nonce is too high, so it has not (yet) been used by the
+	 * server. */
 	if (nonce >= conn->ctx->start_time + conn->ctx->nonce_count) {
 		return 0;
 	}
@@ -4936,6 +4930,7 @@ parse_auth_header(struct mg_connection *conn,
 	return 1;
 }
 
+
 static char *
 mg_fgets(char *buf, size_t size, struct file *filep, char **p)
 {
@@ -5711,6 +5706,7 @@ remove_directory(struct mg_connection *conn, const char *dir)
 
 			if (truncated) {
 				/* Do not delete anything shorter */
+				ok = 0;
 				continue;
 			}
 
@@ -5720,6 +5716,7 @@ remove_directory(struct mg_connection *conn, const char *dir)
 				       __func__,
 				       path,
 				       strerror(ERRNO));
+				ok = 0;
 			}
 			if (de.file.membuf == NULL) {
 				/* file is not in memory */
@@ -5732,6 +5729,9 @@ remove_directory(struct mg_connection *conn, const char *dir)
 						ok = 0;
 					}
 				}
+			} else {
+				/* file is in memory. It can not be deleted. */
+				ok = 0;
 			}
 		}
 		(void)closedir(dirp);
@@ -6197,14 +6197,29 @@ parse_http_headers(char **buf, struct mg_request_info *ri)
 static int
 is_valid_http_method(const char *method)
 {
-	return !strcmp(method, "GET") || !strcmp(method, "POST")
-	       || !strcmp(method, "HEAD") || !strcmp(method, "CONNECT")
-	       || !strcmp(method, "PUT") || !strcmp(method, "DELETE")
-	       || !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND")
-	       || !strcmp(method, "MKCOL") || !strcmp(method, "PATCH");
+	return !strcmp(method, "GET")        /* HTTP (RFC 2616) */
+	       || !strcmp(method, "POST")    /* HTTP (RFC 2616) */
+	       || !strcmp(method, "HEAD")    /* HTTP (RFC 2616) */
+	       || !strcmp(method, "PUT")     /* HTTP (RFC 2616) */
+	       || !strcmp(method, "DELETE")  /* HTTP (RFC 2616) */
+	       || !strcmp(method, "OPTIONS") /* HTTP (RFC 2616) */
+	       /* TRACE method (RFC 2616) is not supported for security reasons */
+	       || !strcmp(method, "CONNECT") /* HTTP (RFC 2616) */
 
-	/* TRACE method is not supported for security reasons */
-	/* PATCH method (RFC 5789) only allowed for CGI/Lua/LSP and callbacks. */
+	       || !strcmp(method, "PROPFIND") /* WEBDAV (RFC 2518) */
+	       || !strcmp(method, "MKCOL")    /* WEBDAV (RFC 2518) */
+
+	       /* Unsupported WEBDAV Methods: */
+	       /* PROPPATCH, COPY, MOVE, LOCK, UNLOCK (RFC 2518) */
+	       /* + 11 methods from RFC 3253 */
+	       /* ORDERPATCH (RFC 3648) */
+	       /* ACL (RFC 3744) */
+	       /* SEARCH (RFC 5323) */
+	       /* + MicroSoft extensions
+	        * https://msdn.microsoft.com/en-us/library/aa142917.aspx */
+
+	       /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */
+	       || !strcmp(method, "PATCH"); /* PATCH method (RFC 5789) */
 }
 
 /* Parse HTTP request, fill in mg_request_info structure.
@@ -7112,7 +7127,7 @@ mkcol(struct mg_connection *conn, const char *path)
 	} else if (rc == -1) {
 		if (errno == EEXIST) {
 			send_http_error(
-			    conn, 405, "Error:mkcol(%s): %s", path, strerror(ERRNO));
+			    conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO));
 		} else if (errno == EACCES) {
 			send_http_error(
 			    conn, 403, "Error: mkcol(%s): %s", path, strerror(ERRNO));
@@ -7285,10 +7300,13 @@ delete_file(struct mg_connection *conn, const char *path)
 	}
 
 	if (de.file.is_directory) {
-		remove_directory(conn, path);
-		/* TODO (mid): remove_dir does not return success of the operation */
-		/* Assume delete is successful: Return 204 without content. */
-		send_http_error(conn, 204, "%s", "");
+		if (remove_directory(conn, path)) {
+			/* Delete is successful: Return 204 without content. */
+			send_http_error(conn, 204, "%s", "");
+		} else {
+			/* Delete is not successful: Return 500 (Server error). */
+			send_http_error(conn, 500, "Error: Could not delete %s", path);
+		}
 		return;
 	}