diff options
author | Vincent Douillet <vincent@vdouillet.fr> | 2024-01-12 18:14:17 +0100 |
---|---|---|
committer | Vincent Douillet <vincent@vdouillet.fr> | 2024-01-12 18:26:22 +0100 |
commit | 760031b4a74d70bdeaab4ce9aedfe772acd05bd3 (patch) | |
tree | 443a456770d5db052a3dfe3d2f1a8e5f66604ccb | |
parent | 43f65c76152017420ce723b4d4ef4230ff072818 (diff) |
browse: build download url for files
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | browse.c | 106 | ||||
-rw-r--r-- | download.c | 37 | ||||
-rw-r--r-- | download.h | 10 | ||||
-rw-r--r-- | file.c | 138 | ||||
-rw-r--r-- | file.h | 70 | ||||
-rw-r--r-- | util.c | 19 | ||||
-rw-r--r-- | util.h | 10 |
8 files changed, 331 insertions, 67 deletions
@@ -30,11 +30,13 @@ CC=cc CFLAGS_DEPS != pkg-config --cflags kcgi kcgi-html LDFLAGS_DEPS != pkg-config --libs kcgi kcgi-html -CFLAGS=-O2 -DLOG_INFO $(CFLAGS_DEPS) +CFLAGS=-DLOG_INFO $(CFLAGS_DEPS) LDFLAGS=-static $(LDFLAGS_DEPS) .if make(debug) -CFLAGS+=-g -W -Wall -Wextra -DTEMPLATE_DIR='"template"' +CFLAGS+=-O0 -g -W -Wall -Wextra -DTEMPLATE_DIR='"template"' +.else +CFLAGS+=-O2 .endif debug: main @@ -44,7 +46,7 @@ all: main .c.o: $(CC) -o $@ -c $< $(CFLAGS) -main: util.o url.o http.o download.o browse.o main.o +main: util.o url.o http.o file.o download.o browse.o main.o $(CC) -o $@ $> $(LDFLAGS) env: env.o @@ -41,81 +41,52 @@ #include "browse.h" #include "cgi.h" #include "config.h" +#include "download.h" +#include "file.h" #include "http.h" #include "url.h" #include "util.h" /* - * list of files for current directory + * a list of files */ SLIST_HEAD(, file) list; -struct file { - char *name; - char *url; - SLIST_ENTRY(file) files; -}; /* - * should be able to free incomplete files as per file_new usage + * browse url = r->pname / BROWSE_URL / file->path / file->name */ -static void -file_free(struct file * file) +static size_t +build_browse_url(struct kreq * r, struct file * file) { - if (file == NULL) - return; - - if (file->name != NULL) { - free(file->name); - file->name = NULL; - } - if (file->url != NULL) { - free(file->url); - file->url = NULL; - } - free(file); -} + char action_url[PATH_MAX]; + size_t action_url_len; -static struct file * -file_new(char *name, size_t name_len, char *url, size_t url_len) -{ - struct file *file; + if (!file->is_dir) + return 0; - file = calloc(1, sizeof(struct file)); - if (file == NULL) - return NULL; - file->name = malloc(sizeof(char) * (name_len + 1)); - if (strlcpy(file->name, name, name_len + 1) >= name_len + 1) { - free(file); - return NULL; + action_url_len = url_build(action_url, PATH_MAX, r->pname, BROWSE_URL, + file->path, file->name, NULL); + if (action_url_len == 0 || action_url_len >= PATH_MAX) { + kutil_warn(r, NULL, + "browse: action URL overflow: %s", action_url); + return 0; } - file->url = malloc(sizeof(char) * (url_len + 1)); - if (strlcpy(file->url, url, url_len + 1) >= url_len + 1) { - free(file); - return NULL; + file->action_url = v_strcpy(action_url, action_url_len); + if (file->action_url == NULL) { + kutil_warn(r, NULL, + "browse: unable to allocate file url buffer: %s", + action_url); + return 0; } - return file; -} - -/* - * file url = r->pname / r->pagename / r->path / file_name - */ -static size_t -build_browse_url(struct kreq * r, char *url, size_t url_len, const char *file) -{ - char *page_name; - - page_name = strlen(r->pagename) == 0 ? BROWSE_URL : r->pagename; - return url_build(url, url_len, r->pname, page_name, r->path, file, - NULL); + return action_url_len; } static int -build_file_list(struct kreq * r, char *request_dir) +build_file_list(struct kreq * r, const char *request_dir) { + bool action_url_ok; char *file_name; - char url[PATH_MAX]; DIR *data_dir; - size_t url_len; struct dirent *dir; struct file *file; struct file *last_file; @@ -131,17 +102,28 @@ build_file_list(struct kreq * r, char *request_dir) file_name = dir->d_name; if (strcmp(".", file_name) == 0 || strcmp("..", file_name) == 0) continue; - url_len = build_browse_url(r, url, PATH_MAX, file_name); - if (url_len == 0 || url_len >= PATH_MAX) { - kutil_warn(r, NULL, "browse: Detected URL overflow: %s", url); + /* build file */ + file = file_new(r->path, strlen(r->path), dir->d_name, + dir->d_namlen); + if (file == NULL) { + kutil_warn(r, NULL, + "browse: unable to allocate file, skipping %s", + file_name); continue; } - /* build file and add to list */ - file = file_new(dir->d_name, dir->d_namlen, url, url_len); - if (file == NULL) { - kutil_warn(r, NULL, "browse: unable to build file info"); + /* build action url */ + if (file->is_dir) + action_url_ok = build_browse_url(r, file) > 0; + else + action_url_ok = build_download_url(r, file) > 0; + if (!action_url_ok) { + kutil_warn(r, NULL, + "browse: unable to build action url, skipping %s", + file_name); + file_free(file); continue; } + /* add file to list */ if (last_file == NULL) SLIST_INSERT_HEAD(&list, file, files); else @@ -183,7 +165,7 @@ template_callback(size_t index, void *arg) break; case 1: /* url */ - K_OK(khttp_puts(r, data->f->url), r); + K_OK(khttp_puts(r, data->f->action_url), r); break; default: kutil_warnx(r, NULL, "Invalid key index for browse template: %zd", index); @@ -38,8 +38,40 @@ #include "cgi.h" #include "config.h" #include "download.h" +#include "file.h" #include "http.h" #include "url.h" +#include "util.h" + +/* + * download url = r->pname / DOWNLOAD_URL / file->path / file->name + */ +size_t +build_download_url(struct kreq * r, struct file * file) +{ + char action_url[PATH_MAX]; + size_t action_url_len; + + /* don't download directories */ + if (file->is_dir) + return 0; + + action_url_len = url_build(action_url, PATH_MAX, r->pname, DOWNLOAD_URL, + file->path, file->name, NULL); + if (action_url_len == 0 || action_url_len >= PATH_MAX) { + kutil_warn(r, NULL, + "download: action URL overflow: %s", action_url); + return 0; + } + file->action_url = v_strcpy(action_url, action_url_len); + if (file->action_url == NULL) { + kutil_warn(r, NULL, + "download: unable to allocate file url buffer: %s", + action_url); + return 0; + } + return action_url_len; +} void download(struct kreq * r) @@ -55,13 +87,16 @@ download(struct kreq * r) http_exit(r, KHTTP_400, "download: Invalid request path"); /* build requested file path */ - path_size = url_build(file_path, PATH_MAX, DATA_DIR, "File.txt", + path_size = url_build(file_path, PATH_MAX, DATA_DIR, r->path, NULL); if (path_size == 0) http_exit(r, KHTTP_404, "download: Unable to build file path"); if (path_size >= PATH_MAX) http_exit(r, KHTTP_414, NULL); + /* check that it is a file */ + + /* memory map the file */ fd = open(file_path, O_RDONLY); if (fd < 0) @@ -33,9 +33,19 @@ #include <kcgi.h> +#include "file.h" + #define DOWNLOAD_URL "download" /* + * Build the URL to download a file and assigns it to the file's action_url. + * All parameters are required + * Returns the length of the created URL, or 0 in case of failure. + */ +size_t +build_download_url(struct kreq *, struct file *); + +/* * Return a requested file. * The KCGI request is required. */ @@ -0,0 +1,138 @@ +/* + * Copyright 2023, Vincent Douillet <vincent@vdouillet.fr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/stat.h> + +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include "config.h" +#include "file.h" +#include "url.h" +#include "util.h" + +void +file_free(struct file * file) +{ + if (file == NULL) + return; + + if (file->path != NULL) { + free(file->path); + file->path = NULL; + } + if (file->name != NULL) { + free(file->name); + file->name = NULL; + } + if (file->action_url != NULL) { + free(file->action_url); + file->action_url = NULL; + } + free(file); +} + +static bool +ext_cmp(char *ext, char **ext_list) +{ + int i; + + for (i = 0; ext_list[i] != NULL; i++) { + if (strcmp(ext, ext_list[i]) == 0) + return true; + } + + return false; +} + +static bool +find_type(struct file * f) +{ + char *ext; + char *video_ext[] = {"mp4", "mkv", "avi", NULL}; + char *audio_ext[] = {"mp3", "flac", "aac", NULL}; + char *text_ext[] = {"txt", "doc", "docx", "odt", NULL}; + char path[PATH_MAX]; + struct stat sb; + size_t path_len; + + path_len = url_build(path, PATH_MAX, DATA_DIR, f->path, f->name, NULL); + if (path_len == 0 || path_len >= PATH_MAX) + return false; + + if (stat(path, &sb) != 0) + return false; + + f->is_dir = S_ISDIR(sb.st_mode); + + ext = strrchr(f->name, '.'); + if (!ext || ext == f->name) { + f->type = OTHER; + return true; + } + ext++; + if (ext_cmp(ext, audio_ext)) + f->type = AUDIO; + else if (ext_cmp(ext, video_ext)) + f->type = VIDEO; + else if (ext_cmp(ext, text_ext)) + f->type = TEXT; + else + f->type = OTHER; + + return true; +} + +struct file * +file_new(char *dir, size_t dir_len, char *name, size_t name_len) +{ + struct file *file; + + file = (struct file *) calloc(1, sizeof(struct file)); + if (file == NULL) + return NULL; + + file->path = v_strcpy(dir, dir_len); + if (file->path == NULL) { + file_free(file); + return NULL; + } + file->name = v_strcpy(name, name_len); + if (file->name == NULL) { + file_free(file); + return NULL; + } + if (!find_type(file)) { + file_free(file); + return NULL; + } + return file; +} @@ -0,0 +1,70 @@ +/* + * Copyright 2023, Vincent Douillet <vincent@vdouillet.fr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FILE_H +#define FILE_H + +#include <sys/queue.h> + +#include <stdbool.h> + +enum file_type { + AUDIO, + VIDEO, + TEXT, + OTHER +}; + +/* + * a file + */ +struct file { + char *path; /* relative to DATA_DIR */ + char *name; + char *action_url; /* action URL for HTML page */ + bool is_dir; + enum file_type type; + SLIST_ENTRY(file) files; +}; + +/* + * Free a file struct. Should be able to free incomplete files as per file_new + * usage. + */ +void +file_free(struct file *); + +/* + * Build a new file + */ +struct file * +file_new(char *dir, size_t dir_len, char *name, size_t name_len); + +#endif /* FILE_H */ @@ -1,5 +1,6 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "util.h" @@ -41,3 +42,21 @@ read_file(char *path, char **output, size_t * read_size) return 0; } + +char * +v_strcpy(char *str, size_t len) +{ + char *new_str; + size_t new_len; + + new_str = (char *) malloc(sizeof(char) * (len + 1)); + if (new_str == NULL) + return NULL; + + new_len = strlcpy(new_str, str, len + 1); + if (new_len >= len + 1) { + free(new_str); + return NULL; + } + return new_str; +} @@ -7,6 +7,14 @@ * be used in case of success). */ int - read_file(char *path, char **output, size_t * read_size); +read_file(char *path, char **output, size_t * read_size); + +/* + * Copies the provided string to a newly allocated buffer. Length of the + * created string is the same as the provided string. + * Returns the new string or NULL in case of failure. + */ +char * +v_strcpy(char *str, size_t len); #endif /* UTIL_H */ |