|
@@ -1849,19 +1849,19 @@ skip_quoted(char **buf,
|
|
if (end_word > begin_word) {
|
|
if (end_word > begin_word) {
|
|
p = end_word - 1;
|
|
p = end_word - 1;
|
|
while (*p == quotechar) {
|
|
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);
|
|
size_t end_off = strcspn(end_word + 1, delimiters);
|
|
memmove(p, end_word, end_off + 1);
|
|
memmove(p, end_word, end_off + 1);
|
|
p += end_off; /* p must correspond to end_word - 1 */
|
|
p += end_off; /* p must correspond to end_word - 1 */
|
|
end_word += end_off + 1;
|
|
end_word += end_off + 1;
|
|
|
|
+ } else {
|
|
|
|
+ *p = '\0';
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (p++; p < end_word; p++) {
|
|
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)) {
|
|
if (conn && is_file_in_memory(conn, path, filep)) {
|
|
/* filep->is_directory = 0; filep->gzipped = 0; .. already done by
|
|
/* filep->is_directory = 0; filep->gzipped = 0; .. already done by
|
|
* memset */
|
|
* memset */
|
|
- last_modified = time(NULL);
|
|
|
|
|
|
+ filep->last_modified = time(NULL);
|
|
/* last_modified = now ... assumes the file may change during runtime,
|
|
/* last_modified = now ... assumes the file may change during runtime,
|
|
* so every mg_fopen call may return different data */
|
|
* so every mg_fopen call may return different data */
|
|
/* last_modified = conn->ctx.start_time;
|
|
/* last_modified = conn->ctx.start_time;
|
|
@@ -4199,7 +4199,7 @@ is_put_or_delete_method(const struct mg_connection *conn)
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
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 */
|
|
char *filename, /* out: filename */
|
|
size_t filename_buf_len, /* in: size of filename buffer */
|
|
size_t filename_buf_len, /* in: size of filename buffer */
|
|
struct file *filep, /* out: file structure */
|
|
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? */
|
|
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)
|
|
#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)
|
|
#if !defined(NO_CGI) || defined(USE_LUA)
|
|
- char *p;
|
|
|
|
|
|
+ char *p;
|
|
#endif
|
|
#endif
|
|
#else
|
|
#else
|
|
- (void)filename_buf_len; /* unused if NO_FILES is defined */
|
|
|
|
|
|
+ (void)filename_buf_len; /* unused if NO_FILES is defined */
|
|
#endif
|
|
#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)
|
|
#if defined(USE_WEBSOCKET)
|
|
- *is_websocket_request = is_websocket_protocol(conn);
|
|
|
|
|
|
+ *is_websocket_request = is_websocket_protocol(conn);
|
|
#if !defined(NO_FILES)
|
|
#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 */
|
|
#endif /* !NO_FILES */
|
|
#else /* USE_WEBSOCKET */
|
|
#else /* USE_WEBSOCKET */
|
|
- *is_websocket_request = 0;
|
|
|
|
|
|
+ *is_websocket_request = 0;
|
|
#endif /* USE_WEBSOCKET */
|
|
#endif /* USE_WEBSOCKET */
|
|
|
|
|
|
#if !defined(NO_FILES)
|
|
#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)
|
|
#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)
|
|
#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
|
|
#endif
|
|
#if defined(USE_LUA)
|
|
#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
|
|
#endif
|
|
#if defined(USE_DUKTAPE)
|
|
#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)
|
|
#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)
|
|
#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
|
|
#endif
|
|
#if defined(USE_LUA)
|
|
#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
|
|
#endif
|
|
#if defined(USE_DUKTAPE)
|
|
#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_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
|
|
#endif /* !defined(NO_FILES) */
|
|
#endif /* !defined(NO_FILES) */
|
|
- }
|
|
|
|
return;
|
|
return;
|
|
|
|
|
|
#if !defined(NO_FILES)
|
|
#if !defined(NO_FILES)
|
|
@@ -4414,7 +4400,9 @@ interpret_cleanup:
|
|
*filename = 0;
|
|
*filename = 0;
|
|
*is_found = 0;
|
|
*is_found = 0;
|
|
*is_script_resource = 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:
|
|
/* 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 */
|
|
/* 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;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -4831,11 +4815,13 @@ open_auth_file(struct mg_connection *conn, const char *path, struct file *filep)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
/* Parsed Authorization header */
|
|
/* Parsed Authorization header */
|
|
struct ah {
|
|
struct ah {
|
|
char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
|
|
char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+
|
|
/* Return 1 on success. Always initializes the ah structure. */
|
|
/* Return 1 on success. Always initializes the ah structure. */
|
|
static int
|
|
static int
|
|
parse_auth_header(struct mg_connection *conn,
|
|
parse_auth_header(struct mg_connection *conn,
|
|
@@ -4901,12 +4887,7 @@ parse_auth_header(struct mg_connection *conn,
|
|
}
|
|
}
|
|
|
|
|
|
#ifndef NO_NONCE_CHECK
|
|
#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) {
|
|
if (ah->nonce == NULL) {
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -4915,12 +4896,25 @@ parse_auth_header(struct mg_connection *conn,
|
|
if ((s == NULL) || (*s != 0)) {
|
|
if ((s == NULL) || (*s != 0)) {
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /* Convert the nonce from the client to a number. */
|
|
nonce ^= (uintptr_t)(conn->ctx);
|
|
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) {
|
|
if (nonce < conn->ctx->start_time) {
|
|
/* nonce is from a previous start of the server and no longer valid
|
|
/* nonce is from a previous start of the server and no longer valid
|
|
* (replay attack?) */
|
|
* (replay attack?) */
|
|
return 0;
|
|
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) {
|
|
if (nonce >= conn->ctx->start_time + conn->ctx->nonce_count) {
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -4936,6 +4930,7 @@ parse_auth_header(struct mg_connection *conn,
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
static char *
|
|
static char *
|
|
mg_fgets(char *buf, size_t size, struct file *filep, char **p)
|
|
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) {
|
|
if (truncated) {
|
|
/* Do not delete anything shorter */
|
|
/* Do not delete anything shorter */
|
|
|
|
+ ok = 0;
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -5720,6 +5716,7 @@ remove_directory(struct mg_connection *conn, const char *dir)
|
|
__func__,
|
|
__func__,
|
|
path,
|
|
path,
|
|
strerror(ERRNO));
|
|
strerror(ERRNO));
|
|
|
|
+ ok = 0;
|
|
}
|
|
}
|
|
if (de.file.membuf == NULL) {
|
|
if (de.file.membuf == NULL) {
|
|
/* file is not in memory */
|
|
/* file is not in memory */
|
|
@@ -5732,6 +5729,9 @@ remove_directory(struct mg_connection *conn, const char *dir)
|
|
ok = 0;
|
|
ok = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ } else {
|
|
|
|
+ /* file is in memory. It can not be deleted. */
|
|
|
|
+ ok = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
(void)closedir(dirp);
|
|
(void)closedir(dirp);
|
|
@@ -6197,14 +6197,29 @@ parse_http_headers(char **buf, struct mg_request_info *ri)
|
|
static int
|
|
static int
|
|
is_valid_http_method(const char *method)
|
|
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.
|
|
/* 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) {
|
|
} else if (rc == -1) {
|
|
if (errno == EEXIST) {
|
|
if (errno == EEXIST) {
|
|
send_http_error(
|
|
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) {
|
|
} else if (errno == EACCES) {
|
|
send_http_error(
|
|
send_http_error(
|
|
conn, 403, "Error: mkcol(%s): %s", path, strerror(ERRNO));
|
|
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) {
|
|
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;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|