diff options
Diffstat (limited to 'src/fileserv.c')
-rw-r--r-- | src/fileserv.c | 192 |
1 files changed, 139 insertions, 53 deletions
diff --git a/src/fileserv.c b/src/fileserv.c index dc218de..f25b6ad 100644 --- a/src/fileserv.c +++ b/src/fileserv.c @@ -42,6 +42,8 @@ char *progname; int verbose; /* reserved for future use */ char *address = "0.0.0.0"; char *forwarded_header = "X-Forwarded-For"; +char *index_css; +char *tmpdir; #ifndef DEFAULT_SERVICE # define DEFAULT_SERVICE "8080" @@ -320,56 +322,111 @@ errno_to_http_code(int ec) return ec == ENOENT ? MHD_HTTP_NOT_FOUND : MHD_HTTP_FORBIDDEN; } +typedef struct file_resp { + int fd; + char *file_name; + DIRCONFIG *conf; + struct stat st; + char *location; +} FILE_RESP; + +static void +file_resp_init(FILE_RESP *resp) +{ + memset(resp, 0, sizeof(*resp)); + resp->fd = -1; +} + +static void +file_resp_free(FILE_RESP *resp) +{ + if (resp->fd >= 0) + close(resp->fd); + free(resp->file_name); + dirconfig_free(resp->conf); + free(resp->location); +} + int -get_file_name(char const *host, char const *url, char **fname, struct stat *stp) +get_file_name(char const *host, char const *url, FILE_RESP *resp) { struct urimap const *map; - char *file_name; char *cf; - struct stat st; - DIRCONFIG *conf; + + file_resp_init(resp); map = urimap_find(host, url); if (!map) return MHD_HTTP_NOT_FOUND; - file_name = catfile_n(map->dir, map->dir_len, url + map->uri_len); - if (lstat(file_name, &st)) + resp->file_name = catfile_n(map->dir, map->dir_len, url + map->uri_len); + if (lstat(resp->file_name, &resp->st)) return errno_to_http_code(errno); + + resp->conf = dirconfig(resp->file_name, map->dir_len); - conf = dirconfig(file_name, map->dir_len); + if (S_ISDIR(resp->st.st_mode)) { + int res; + + if (url[strlen(url) - 1] != '/') { + resp->location = catfile(url, ""); + return MHD_HTTP_MOVED_PERMANENTLY; + } - if (S_ISDIR(st.st_mode)) { - int res = find_index_file(file_name, conf, &cf, &st); + res = find_index_file(resp->file_name, resp->conf, &cf, + &resp->st); if (res == MHD_HTTP_OK) { - free(file_name); - file_name = cf; - } else if (conf->listing) { + free(resp->file_name); + resp->file_name = cf; + } else if (resp->conf->listing) { //FIXME - return MHD_HTTP_FORBIDDEN; - } else + char *template = catfile(tmpdir, "idxXXXXXX"); + int fd = mkstemp(template); + if (fd == -1) { + error("can't create temporary file name: %s", + strerror(errno)); + /*FIXME: leak */ + return MHD_HTTP_INTERNAL_SERVER_ERROR; + } + unlink(template); + free(template); + res = directory_index(fd, resp->conf, url, + resp->file_name); + if (res) { + close(fd); + return MHD_HTTP_FORBIDDEN; + } + fstat(fd, &resp->st); + resp->fd = fd; + return MHD_HTTP_OK; + } else return res; } - if (S_ISLNK(st.st_mode)) { - if (!conf->follow) + if (S_ISLNK(resp->st.st_mode)) { + if (!resp->conf->follow) return MHD_HTTP_FORBIDDEN; - cf = cfname(file_name); + + cf = cfname(resp->file_name); if (!cf) { - free(file_name); return MHD_HTTP_NOT_FOUND; } else if (urimap_find_dir(host, cf)) { - free(file_name); - file_name = cf; + free(resp->file_name); + resp->file_name = cf; } else { - free(file_name); free(cf); return MHD_HTTP_NOT_FOUND; } - } else if (!S_ISREG(st.st_mode)) + } else if (!S_ISREG(resp->st.st_mode)) { return MHD_HTTP_FORBIDDEN; - - *fname = file_name; - *stp = st; + } + + resp->fd = open(resp->file_name, O_RDONLY); + if (resp->fd == -1) { + error("can't open %s: %s", + resp->file_name, strerror(errno)); + return errno_to_http_code(errno); + } + fstat(resp->fd, &resp->st); return MHD_HTTP_OK; } @@ -732,6 +789,27 @@ http_error(struct MHD_Connection *connection, } static int +http_redirect(struct MHD_Connection *connection, + char const *method, char const *url, + int status, char const *loc) +{ + int ret; + struct MHD_Response *response; + + http_log(connection, method, url, status, NULL); + response = MHD_create_response_from_buffer (strlen(loc), + xstrdup(loc), + MHD_RESPMEM_MUST_FREE); + MHD_add_response_header(response, + MHD_HTTP_HEADER_LOCATION, + loc); + + ret = MHD_queue_response(connection, status, response); + MHD_destroy_response(response); + return ret; +} + +static int fileserv_handler(void *cls, struct MHD_Connection *conn, const char *url, const char *method, @@ -743,11 +821,9 @@ fileserv_handler(void *cls, char const *host = MHD_lookup_connection_value(conn, MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST); - char *file_name; - struct stat st; + FILE_RESP resp; struct MHD_Response *response; int ret; - int fd; char const *type; int status; @@ -761,35 +837,39 @@ fileserv_handler(void *cls, } *con_cls = NULL; - status = get_file_name(host, url, &file_name, &st); - if (status != MHD_HTTP_OK) + status = get_file_name(host, url, &resp); + switch (status) { + case MHD_HTTP_OK: + break; + case MHD_HTTP_MOVED_PERMANENTLY: + case MHD_HTTP_FOUND: + ret = http_redirect(conn, method, url, status, + resp.location); + file_resp_free(&resp); + return ret; + default: + file_resp_free(&resp); return http_error(conn, method, url, status, NULL); - - fd = open(file_name, O_RDONLY); - if (fd == -1) { - free(file_name); - return http_error(conn, method, url, - errno_to_http_code(errno), - NULL); } - type = get_file_type(file_name); - free(file_name); + if (resp.file_name) + type = get_file_type(resp.file_name); + else + type = NULL; - response = MHD_create_response_from_fd64(st.st_size, fd); - if (!response) { - close(fd); - return MHD_NO; - } - - if (type) - MHD_add_response_header(response, - MHD_HTTP_HEADER_CONTENT_TYPE, - type); - - ret = MHD_queue_response(conn, MHD_HTTP_OK, response); - MHD_destroy_response(response); - http_log(conn, method, url, MHD_HTTP_OK, &st); + response = MHD_create_response_from_fd64(resp.st.st_size, resp.fd); + if (response) { + resp.fd = -1; + if (type) + MHD_add_response_header(response, + MHD_HTTP_HEADER_CONTENT_TYPE, + type); + ret = MHD_queue_response(conn, MHD_HTTP_OK, response); + MHD_destroy_response(response); + http_log(conn, method, url, MHD_HTTP_OK, &resp.st); + } else + ret = MHD_NO; + file_resp_free(&resp); return ret; } @@ -827,6 +907,9 @@ main(int argc, char **argv) progname = argv[0]; mimetypes_error_printer = fileserv_error_printer; + tmpdir = getenv("TMP"); + if (!tmpdir) + tmpdir = "/tmp"; while ((c = getopt(argc, argv, "a:F:fg:i:hm:p:t:x:u:v")) != EOF) { switch (c) { @@ -845,6 +928,9 @@ main(int argc, char **argv) case 'h': usage(); exit(0); + case 'i': + parse_template_file(optarg); + break; case 'm': mime_types_file = optarg; break; |