diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | browse.c | 25 | ||||
-rw-r--r-- | create.c | 238 | ||||
-rw-r--r-- | create.h | 54 | ||||
-rw-r--r-- | main.c | 6 | ||||
-rw-r--r-- | template/browse_head.html | 5 | ||||
-rw-r--r-- | template/create_head.html | 17 | ||||
-rw-r--r-- | template/head.html | 3 |
8 files changed, 343 insertions, 10 deletions
@@ -47,10 +47,10 @@ all: main .c.o: $(CC) -o $@ -c $< $(CFLAGS) -main: str.o config.o mime.o url.o template.o file.o http.o delete.o upload.o download.o browse.o main.o +main: str.o config.o mime.o url.o template.o file.o http.o delete.o upload.o download.o create.o browse.o main.o $(CC) -o $@ $> $(LDFLAGS) -test: str.o config.o mime.o url.o template.o file.o http.o delete.o upload.o download.o browse.o test.o +test: str.o config.o mime.o url.o template.o file.o http.o delete.o upload.o download.o create.o browse.o test.o $(CC) -o $@ $> $(LDFLAGS) env: env.o @@ -68,6 +68,7 @@ install: install -D -o www -g www -m 0440 template/browse_item.html /var/www/usr/share/vault/template/browse_item.html install -D -o www -g www -m 0440 template/upload_head.html /var/www/usr/share/vault/template/upload_head.html install -D -o www -g www -m 0440 template/delete_head.html /var/www/usr/share/vault/template/delete_head.html + install -D -o www -g www -m 0440 template/create_head.html /var/www/usr/share/vault/template/create_head.html install -D -o www -g www -m 0440 fontawesome-6.5.1/css/fontawesome.css /var/www/vault-static/fontawesome-6.5.1/css/fontawesome.css gzip -fk /var/www/vault-static/fontawesome-6.5.1/css/fontawesome.css install -D -o www -g www -m 0440 fontawesome-6.5.1/css/solid.css /var/www/vault-static/fontawesome-6.5.1/css/solid.css @@ -41,6 +41,7 @@ #include "browse.h" #include "cgi.h" #include "config.h" +#include "create.h" #include "download.h" #include "file.h" #include "http.h" @@ -164,7 +165,7 @@ struct template_data { struct file *f; }; -static const char *const header_template_keys[] = {"upload_url"}; +static const char *const header_template_keys[] = {"upload_url", "create_url"}; static int header_template_callback(size_t index, void *arg) @@ -185,7 +186,21 @@ header_template_callback(size_t index, void *arg) switch (index) { case 0: /* upload_url */ - K_OK(khtml_puts(html, data->f->action_url), r); + if (build_upload_url(r, data->f) > 0) { + K_OK(khtml_puts(html, data->f->action_url), r); + } else + kutil_warnx(r, NULL, + "failed to build upload URL for path %s", + data->f->path); + break; + case 1: + /* create_url */ + if (build_create_url(r, data->f) > 0) { + K_OK(khtml_puts(html, data->f->action_url), r); + } else + kutil_warnx(r, NULL, + "failed to build create URL for path %s", + data->f->path); break; default: kutil_warnx(r, NULL, @@ -302,10 +317,6 @@ browse(struct kreq * r) http_exit(r, KHTTP_404, "browse: Invalid data file"); goto end; } - if (build_upload_url(r, file) == 0) { - http_exit(r, KHTTP_500, "browse: Can't build upload url"); - goto end; - } if (build_file_list(r, file) < 0) { http_exit(r, KHTTP_500, "browse: Unable to build file list"); goto end; @@ -325,7 +336,7 @@ browse(struct kreq * r) /* print browse header with action buttons for current dir */ template.key = header_template_keys; - template.keysz = 1; + template.keysz = 2; template.cb = header_template_callback; template.arg = &data; data.r = r; diff --git a/create.c b/create.c new file mode 100644 index 0000000..e5a98b0 --- /dev/null +++ b/create.c @@ -0,0 +1,238 @@ +/* + * Copyright 2025, 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 <stdlib.h> +#include <kcgi.h> +#include <kcgihtml.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> + +#include "browse.h" +#include "cgi.h" +#include "http.h" +#include "template.h" +#include "create.h" +#include "url.h" + +/* + * create url = r->pname / CREATE_URL / file->path + */ +size_t +build_create_url(const struct kreq * r, struct file * file) +{ + char action_url[PATH_MAX]; + size_t action_url_len; + + if (!file->is_dir) + return 0; + + action_url_len = url_build(action_url, PATH_MAX, r->pname, CREATE_URL, + file->path, NULL); + if (action_url_len == 0 || action_url_len >= PATH_MAX) { + kutil_warn(r, NULL, + "create: action URL overflow: %s", action_url); + return 0; + } + file->action_url = strndup(action_url, action_url_len); + if (file->action_url == NULL) { + kutil_warn(r, NULL, + "create: unable to allocate file url buffer: %s", + action_url); + return 0; + } + return action_url_len; +} + +/* + * data required in the template functions + */ +struct template_data { + struct kreq *r; + struct khtmlreq *html; + struct file *f; +}; + +static const char *const header_template_keys[] = {"path", "submit_url"}; + +static int +header_template_callback(size_t index, void *arg) +{ + struct kreq *r; + struct khtmlreq *html; + struct template_data *data; + + if (arg == NULL) { + kutil_warn(NULL, NULL, + "Invalid data for create header template"); + return 0; + } + data = arg; + r = data->r; + html = data->html; + + switch (index) { + case 0: + /* path */ + K_OK(khtml_puts(html, data->f->path), r); + break; + case 1: + /* submit_url */ + K_OK(khtml_puts(html, data->f->action_url), r); + break; + default: + kutil_warnx(r, NULL, + "Invalid key index for create header template: %zd", + index); + return 0; + } + + return 1; +} + +/* + * GET request, we print create form + */ +void +create_get(struct kreq * r, struct file * file) +{ + struct khtmlreq html; + struct ktemplate template; + struct page_template *tmpl; + struct template_data data; + + tmpl = NULL; + + /* action url is form submit url */ + if (build_create_url(r, file) == 0) { + http_exit(r, KHTTP_500, "create: Can't build create url"); + goto end; + } + /* read template */ + tmpl = page_template_new(CREATE_URL); + if (tmpl == NULL) { + http_exit(r, KHTTP_500, "create: Unable to read template"); + goto end; + } + /* print create form */ + http_open(r, KHTTP_200, KMIME_TEXT_HTML); + + K_OK(khttp_puts(r, tmpl->header), r); + K_OK(khtml_open(&html, r, 0), r); + + template.key = header_template_keys; + template.keysz = 2; + template.cb = header_template_callback; + template.arg = &data; + data.r = r; + data.html = &html; + data.f = file; + K_OK(khttp_template_buf(r, &template, tmpl->page_header, + strlen(tmpl->page_header)), r); + + K_OK(khttp_puts(r, tmpl->page_footer), r); + K_OK(khttp_puts(r, tmpl->footer), r); + + K_OK(khtml_close(&html), r); + +end: + page_template_free(tmpl); +} + +/* + * POST request, we handle create form + */ +void +create_post(struct kreq * r, struct file * f) +{ + char path[PATH_MAX]; + size_t path_len, i; + struct kpair *name; + + /* prepare action url for return redirection in case of success */ + if (build_browse_url(r, f) == 0) { + http_exit(r, KHTTP_500, "create: can't build return url"); + return; + } + /* validate input data */ + if (r->fieldsz != 1) { + http_exit(r, KHTTP_400, "create: missing folder name"); + return; + } + name = &(r->fields[0]); + /* + * TODO name->val == NULL is not enough, need better sanitization + * for example, some chars are not allowed in a folder name + * an empty non-NULL string is also not ok + */ + if (strncmp(name->key, "name", 4) || name->val == NULL) { + http_exit(r, KHTTP_400, "create: invalide folder name"); + return; + } + path_len = file_get_data_path(f, path, PATH_MAX, name->val); + if (path_len == 0 || path_len >= PATH_MAX) { + http_exit(r, KHTTP_500, + "create: can't build file path"); + return; + } + /* TODO check if folder already exists */ + if (mkdir(path, 0755)) { + http_exit(r, KHTTP_500, + "create: can't create directory"); + return; + } + + /* on success, redirect to the new folder */ + khttp_head(r, kresps[KRESP_LOCATION], "%s", f->action_url); + http_open(r, KHTTP_303, r->mime); +} + +void +create(struct kreq * r) +{ + struct file *file; + + file = NULL; + + /* build file corresponding to request (ie. create) path */ + file = file_new(r->path); + if (file == NULL) { + http_exit(r, KHTTP_404, "create: Unable to build data file"); + goto end; + } + /* print form or handle submission according to HTTP method */ + if (r->method == KMETHOD_POST) + create_post(r, file); + else + create_get(r, file); + +end: + file_free(file); +} diff --git a/create.h b/create.h new file mode 100644 index 0000000..1626a63 --- /dev/null +++ b/create.h @@ -0,0 +1,54 @@ +/* + * Copyright 2025, 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 CREATE_H +#define CREATE_H + +#include <kcgi.h> + +#include "file.h" + +#define CREATE_URL "create" + +/* + * Build the URL to create a folder 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_create_url(const struct kreq *, struct file *); + +/* + * Print a create form (http GET) or handle a create form submission (http + * POST). + */ +void create(struct kreq *); + +#endif /* CREATE_H */ @@ -36,6 +36,7 @@ #include "browse.h" #include "cgi.h" #include "config.h" +#include "create.h" #include "delete.h" #include "download.h" #include "http.h" @@ -46,6 +47,7 @@ enum page { PAGE_DOWNLOAD, PAGE_UPLOAD, PAGE_DELETE, + PAGE_CREATE, PAGE__MAX }; @@ -54,6 +56,7 @@ static const char *const pages[PAGE__MAX] = { DOWNLOAD_URL, UPLOAD_URL, DELETE_URL, + CREATE_URL, }; int @@ -121,6 +124,9 @@ main(void) case PAGE_DELETE: del(&r); break; + case PAGE_CREATE: + create(&r); + break; default: http_exit(&r, KHTTP_404, NULL); goto end; diff --git a/template/browse_head.html b/template/browse_head.html index 6775012..8b53948 100644 --- a/template/browse_head.html +++ b/template/browse_head.html @@ -1,5 +1,8 @@ <h1><i class="fa-regular fa-folder-open"></i> Browsing</h1> -<a href="@@upload_url@@">Upload here</a> +<ul id="action-list"> + <li><a href="@@upload_url@@">Upload here</a></li> + <li><a href="@@create_url@@">Create folder here</a></li> +</ul> <hr /> <table id="file-list"> <thead> diff --git a/template/create_head.html b/template/create_head.html new file mode 100644 index 0000000..5b273ce --- /dev/null +++ b/template/create_head.html @@ -0,0 +1,17 @@ +<h1><i class="fa-regular fa-folder-open"></i> New folder</h1> +<p>in</p> +<pre>@@path@@</pre> +<hr /> +<div> +<form action="@@submit_url@@" method="post" enctype="multipart/form-data"> + <div> + <label for="name">Folder name:</label> + </div> + <div> + <input type="text" name="name" id="name" /> + </div> + <div> + <input type="submit" value="Create" /> + </div> +</form> +</div> diff --git a/template/head.html b/template/head.html index a36c475..85f28e3 100644 --- a/template/head.html +++ b/template/head.html @@ -15,6 +15,9 @@ body { font:1.2em/1.62 sans-serif; } a:visited { color:blue; } +ul#action-list { padding:0; } +ul#action-list li { display:inline; } +ul#action-list li:not(:last-child):after { content:' |'; } table#file-list { width:100%; border-collapse:collapse; |