diff --git a/build/src/core.c b/build/src/core.c index 2dc75333a2b2fc2be7e4b5d94d73081f3976e2b6..cb61383912cbed071b4b874533068da6021199fa 100644 --- a/build/src/core.c +++ b/build/src/core.c @@ -278,6 +278,10 @@ static const struct { {NULL, 0, NULL} }; +static const char *static_month_names[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; const char **mg_get_valid_option_names(void) { return static_config_options; @@ -1051,8 +1055,64 @@ static void open_file_endpoint(struct connection *conn, const char *path, mime_vec.ptr, cl, "keep-alive", range, EXTRA_HTTP_HEADERS); if (!strcmp(conn->mg_conn.request_method, "HEAD")) { - lseek(conn->endpoint.fd, 0, SEEK_END); + conn->flags |= CONN_SPOOL_DONE; + close(conn->endpoint.fd); + conn->endpoint_type = EP_NONE; + } +} + +// Convert month to the month number. Return -1 on error, or month number +static int get_month_index(const char *s) { + int i; + + for (i = 0; i < (int) ARRAY_SIZE(static_month_names); i++) + if (!strcmp(s, static_month_names[i])) + return i; + + return -1; +} + +static int num_leap_years(int year) { + return year / 4 - year / 100 + year / 400; +} + +// Parse UTC date-time string, and return the corresponding time_t value. +static time_t parse_date_string(const char *datetime) { + static const unsigned short days_before_month[] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + char month_str[32]; + int second, minute, hour, day, month, year, leap_days, days; + time_t result = (time_t) 0; + + if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d", + &day, month_str, &year, &hour, &minute, &second) == 6) || + (sscanf(datetime, "%d %3s %d %d:%d:%d", + &day, month_str, &year, &hour, &minute, &second) == 6) || + (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d", + &day, month_str, &year, &hour, &minute, &second) == 6) || + (sscanf(datetime, "%d-%3s-%d %d:%d:%d", + &day, month_str, &year, &hour, &minute, &second) == 6)) && + year > 1970 && + (month = get_month_index(month_str)) != -1) { + leap_days = num_leap_years(year) - num_leap_years(1970); + year -= 1970; + days = year * 365 + days_before_month[month] + (day - 1) + leap_days; + result = days * 24 * 3600 + hour * 3600 + minute * 60 + second; } + + return result; +} + +// Return True if we should reply 304 Not Modified. +static int is_not_modified(const struct connection *conn, + const struct stat *stp) { + char etag[64]; + const char *ims = mg_get_header(&conn->mg_conn, "If-Modified-Since"); + const char *inm = mg_get_header(&conn->mg_conn, "If-None-Match"); + construct_etag(etag, sizeof(etag), stp); + return (inm != NULL && !mg_strcasecmp(etag, inm)) || + (ims != NULL && stp->st_mtime <= parse_date_string(ims)); } static struct uri_handler *find_uri_handler(struct mg_server *server, @@ -1068,6 +1128,53 @@ static struct uri_handler *find_uri_handler(struct mg_server *server, return NULL; } +// For given directory path, substitute it to valid index file. +// Return 0 if index file has been found, -1 if not found. +// If the file is found, it's stats is returned in stp. +static int substitute_index_file(struct connection *conn, char *path, + size_t path_len, struct stat *stp) { + const char *list = conn->server->config_options[INDEX_FILES]; + struct stat st; + struct vec filename_vec; + size_t n = strlen(path), found = 0; + + // The 'path' given to us points to the directory. Remove all trailing + // directory separator characters from the end of the path, and + // then append single directory separator character. + while (n > 0 && path[n - 1] == '/') { + n--; + } + path[n] = '/'; + + // Traverse index files list. For each entry, append it to the given + // path and see if the file exists. If it exists, break the loop + while ((list = next_option(list, &filename_vec, NULL)) != NULL) { + + // Ignore too long entries that may overflow path buffer + if (filename_vec.len > (int) (path_len - (n + 2))) + continue; + + // Prepare full path to the index file + strncpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1); + + // Does it exist? + if (!stat(path, &st)) { + // Yes it does, break the loop + *stp = st; + found = 1; + break; + } + } + + // If no index file exists, restore directory path + if (!found) { + path[n] = '\0'; + } + + return found; +} + + static void open_local_endpoint(struct connection *conn) { char path[MAX_PATH_SIZE] = {'\0'}; struct stat st; @@ -1082,13 +1189,6 @@ static void open_local_endpoint(struct connection *conn) { uri_len + 1, 0); remove_double_dots_and_double_slashes((char *) conn->mg_conn.uri); -#if 0 - if (call_user(MG_REQUEST_BEGIN, conn, NULL) != 0) { - conn->flags |= CONN_SPOOL_DONE; - return; - } -#endif - if ((uh = find_uri_handler(conn->server, conn->mg_conn.uri)) != NULL) { conn->endpoint_type = EP_USER; } @@ -1097,20 +1197,24 @@ static void open_local_endpoint(struct connection *conn) { is_directory = S_ISDIR(st.st_mode); if (!exists) { - mg_printf(&conn->mg_conn, "%s", "HTTP/1.0 404 Not Found\r\n\r\n"); + mg_printf(&conn->mg_conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n"); conn->flags |= CONN_SPOOL_DONE; } else if (is_directory && conn->mg_conn.uri[uri_len - 1] != '/') { mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n" "Location: %s/\r\n\r\n", conn->mg_conn.uri); conn->flags |= CONN_SPOOL_DONE; - } else if (is_directory) { + } else if (is_directory && + !substitute_index_file(conn, path, sizeof(path), &st)) { mg_printf(&conn->mg_conn, "%s", - "HTTP/1.0 403 Directory Listing Denied\r\n\r\n"); + "HTTP/1.1 403 Directory Listing Denied\r\n\r\n"); + conn->flags |= CONN_SPOOL_DONE; + } else if (is_not_modified(conn, &st)) { + mg_printf(&conn->mg_conn, "%s", "HTTP/1.1 304 Not Modified\r\n\r\n"); conn->flags |= CONN_SPOOL_DONE; } else if ((conn->endpoint.fd = open(path, O_RDONLY)) != -1) { open_file_endpoint(conn, path, &st); } else { - mg_printf(&conn->mg_conn, "%s", "HTTP/1.0 404 Not Found\r\n\r\n"); + mg_printf(&conn->mg_conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n"); conn->flags |= CONN_SPOOL_DONE; } }