diff options
| -rw-r--r-- | Makefile | 38 | ||||
| -rw-r--r-- | README.md | 32 | ||||
| -rw-r--r-- | browse.c | 72 | ||||
| -rw-r--r-- | browse.h | 9 | ||||
| -rw-r--r-- | cgi.h | 4 | ||||
| -rw-r--r-- | config.h | 4 | ||||
| -rw-r--r-- | create.c | 242 | ||||
| -rw-r--r-- | create.h | 54 | ||||
| -rw-r--r-- | delete.c | 82 | ||||
| -rw-r--r-- | delete.h | 2 | ||||
| -rw-r--r-- | download.c | 46 | ||||
| -rw-r--r-- | download.h | 2 | ||||
| -rw-r--r-- | env.c | 4 | ||||
| -rw-r--r-- | http.c | 7 | ||||
| -rw-r--r-- | http.h | 12 | ||||
| -rw-r--r-- | main.c | 50 | ||||
| -rw-r--r-- | mime.c | 1 | ||||
| -rw-r--r-- | template/browse_head.html | 5 | ||||
| -rw-r--r-- | template/create_head.html | 17 | ||||
| -rw-r--r-- | template/head.html | 3 | ||||
| -rw-r--r-- | test.c | 224 | ||||
| -rw-r--r-- | upload.c | 72 | ||||
| -rw-r--r-- | upload.h | 2 |
23 files changed, 678 insertions, 306 deletions
@@ -26,11 +26,18 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +# OpenBSD +CHROOT=/var/www +WWW_USER=www +# Linux +#CHROOT= +#WWW_USER=www-data + CC=cc CFLAGS_DEPS != pkg-config --cflags kcgi kcgi-html LDFLAGS_DEPS != pkg-config --libs kcgi kcgi-html -CFLAGS=-DLOG_INFO -std=c89 $(CFLAGS_DEPS) +CFLAGS=-DLOG_INFO -D_POSIX_C_SOURCE=200809L -std=c89 $(CFLAGS_DEPS) LDFLAGS=-static $(LDFLAGS_DEPS) .if make(debug) || make(test) @@ -47,10 +54,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 @@ -60,21 +67,22 @@ kcgienv: kcgienv.o $(CC) -o $@ $> $(LDFLAGS) install: - install -D -o www -g www -m 0500 main /var/www/cgi-bin/vault - install -D -o www -g www -m 0440 template/head.html /var/www/usr/share/vault/template/head.html - install -D -o www -g www -m 0440 template/foot.html /var/www/usr/share/vault/template/foot.html - install -D -o www -g www -m 0440 template/browse_head.html /var/www/usr/share/vault/template/browse_head.html - install -D -o www -g www -m 0440 template/browse_foot.html /var/www/usr/share/vault/template/browse_foot.html - 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 fontawesome-6.5.1/css/fontawesome.css /var/www/vault-static/fontawesome-6.5.1/css/fontawesome.css + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0500 main /var/www/cgi-bin/vault + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 template/head.html $(CHROOT)/usr/share/vault/template/head.html + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 template/foot.html $(CHROOT)/usr/share/vault/template/foot.html + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 template/browse_head.html $(CHROOT)/usr/share/vault/template/browse_head.html + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 template/browse_foot.html $(CHROOT)/usr/share/vault/template/browse_foot.html + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 template/browse_item.html $(CHROOT)/usr/share/vault/template/browse_item.html + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 template/upload_head.html $(CHROOT)/usr/share/vault/template/upload_head.html + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 template/delete_head.html $(CHROOT)/usr/share/vault/template/delete_head.html + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 template/create_head.html $(CHROOT)/usr/share/vault/template/create_head.html + install -D -o $(WWW_USER) -g $(WWW_USER) -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 + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 fontawesome-6.5.1/css/solid.css /var/www/vault-static/fontawesome-6.5.1/css/solid.css gzip -fk /var/www/vault-static/fontawesome-6.5.1/css/solid.css - install -D -o www -g www -m 0440 fontawesome-6.5.1/webfonts/fa-solid-900.ttf /var/www/vault-static/fontawesome-6.5.1/webfonts/fa-solid-900.ttf + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 fontawesome-6.5.1/webfonts/fa-solid-900.ttf /var/www/vault-static/fontawesome-6.5.1/webfonts/fa-solid-900.ttf gzip -fk /var/www/vault-static/fontawesome-6.5.1/webfonts/fa-solid-900.ttf - install -D -o www -g www -m 0440 fontawesome-6.5.1/webfonts/fa-solid-900.woff2 /var/www/vault-static/fontawesome-6.5.1/webfonts/fa-solid-900.woff2 + install -D -o $(WWW_USER) -g $(WWW_USER) -m 0440 fontawesome-6.5.1/webfonts/fa-solid-900.woff2 /var/www/vault-static/fontawesome-6.5.1/webfonts/fa-solid-900.woff2 gzip -fk /var/www/vault-static/fontawesome-6.5.1/webfonts/fa-solid-900.woff2 clean: @@ -1,12 +1,13 @@ # Vault +Note: This software is WIP. There may be bugs or unintended behavior. + Vault is an opinionated web-based file manager. It is built in C for OpenBSD around the CGI standard, following these main principles: -* __secure__: a great amount of time has been devoted to avoid undefined behaviour and security issues * __simple__: browse, create and delete folders; download and upload files. JavaScript support is optional on the client side and its absence should be handled gracefully. Everything should work from a terminal-based web browser * __fast__: with the server running on an ARM SBC, there should not be any noticeable delay when loading a page -It should also compile and run on any POSIX OS with minor modifications, although this has not been tested (yet). +It should also compile and run on any POSIX OS with minor modifications. On Linux, `bmake(1)` should be used instead of `make(1)`, and configuration should be adjusted in the `Makefile`. Please note that sandboxing is implemented for OpenBSD _only_. ## Configure @@ -19,11 +20,12 @@ Vault comes with a Makefile: $ make # make install -The vault binary will be installed as `/var/www/cgi-bin/vault`. Static resources will be installed in `/var/www/vault-static` and should be served from `/static`. You need to configure your web server accordingly, see below for a sample file. If the log file does not exist, you need to create it. Here is how to do it for a default installation: +The vault binary will be installed as `/var/www/cgi-bin/vault`. Static resources will be installed in `/var/www/vault-static` and should be served from `/static`. You need to configure your web server accordingly, see below for sample files. If the log file does not exist, you need to create it. Here is how to do it for a chrooted web server, like OpenBSD's `httpd(8)`: + + # touch /var/www/logs/vault.log + # chown www /var/www/logs/vault.log - # mkdir -p /var/www/var/log - # touch /var/www/var/log/vault.log - # chown www /var/www/var/log/vault.log +On other operating systems, assuming a web server that is _not_ chrooted, `config.h` sets the log path to `/var/log/vault.log` instead. By default on OpenBSD, the `slowcgi(8)` daemon allows a timeout of 2 minutes for CGI programs. This might not be enough if you want to allow users to download large files. This timeout can be increased by changing the `slowcgi(8)` parameters in `/etc/rc.conf.local`, for example to allow up to 10 minutes : @@ -48,3 +50,21 @@ By default on OpenBSD, the `slowcgi(8)` daemon allows a timeout of 2 minutes for gzip-static } } + +## Sample lighttpd.conf + + server.modules+=("mod_alias", "mod_cgi", "mod_setenv") + server.port=80 + server.document-root="/var/www/html" + server.errorlog="/var/log/lighttpd/error.log" + server.pid-file="/run/lighttpd.pid" + server.username="www-data" + server.groupname="www-data" + index-file.names=("index.html") + + setenv.set-environment=("VAULT_DATA_DIR" => "/var/www/vault-data") + alias.url = ("/static" => "/var/www/vault-static") + $HTTP["url"] =~ "^/vault(?:/|$)" { + alias.url = ("/vault" => "/var/www/cgi-bin/vault") + cgi.assign = ("" => "") + } @@ -28,10 +28,13 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <sys/types.h> #include <sys/queue.h> -#include <dirent.h> +#include <stdarg.h> +#include <stdint.h> #include <kcgi.h> +#include <dirent.h> #include <kcgihtml.h> #include <limits.h> #include <stdbool.h> @@ -41,6 +44,7 @@ #include "browse.h" #include "cgi.h" #include "config.h" +#include "create.h" #include "download.h" #include "file.h" #include "http.h" @@ -164,7 +168,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 +189,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, @@ -241,7 +259,11 @@ file_template_callback(size_t index, void *arg) K_OK(khtml_puts(html, data->f->action_url), r); break; case 3: - /* size, print as human readable */ + /* size, only for files, print as human readable */ + if (data->f->is_dir) { + break; + } + file_size = data->f->size; i = 0; while (file_size > 1024 && i < 3) { @@ -275,12 +297,11 @@ file_template_callback(size_t index, void *arg) return 1; } -struct http_ret +void browse(struct kreq * r) { struct file *file; struct page_template *tmpl; - struct http_ret ret; struct khtmlreq html; struct ktemplate template; struct template_data data; @@ -289,53 +310,26 @@ browse(struct kreq * r) file = NULL; tmpl = NULL; - /* initialize return structure for success */ - ret = (struct http_ret) { - KHTTP_200, "" - }; - /* list requested directory content */ file = file_new(r->path); if (file == NULL) { - ret = (struct http_ret) { - KHTTP_404, - "browse: Unable to build data file" - }; + http_exit(r, KHTTP_404, "browse: Unable to build data file"); goto end; } if (!file->is_dir) { - ret = (struct http_ret) { - KHTTP_404, - "browse: Invalid data file" - }; - goto end; - } - if (build_upload_url(r, file) == 0) { - ret = (struct http_ret) { - KHTTP_500, - "browse: Can't build upload url" - }; + http_exit(r, KHTTP_404, "browse: Invalid data file"); goto end; } - if (build_file_list(r, file) < 0) { - ret = (struct http_ret) { - KHTTP_500, - "browse: Unable to build file list" - }; + http_exit(r, KHTTP_500, "browse: Unable to build file list"); goto end; } - /* read template */ tmpl = page_template_new(BROWSE_URL); if (tmpl == NULL) { - ret = (struct http_ret) { - KHTTP_500, - "browse: Unable to read template" - }; + http_exit(r, KHTTP_500, "browse: Unable to read template"); goto end; } - /* we have all the data we need, we can start to write output page */ http_open(r, KHTTP_200, KMIME_TEXT_HTML); @@ -345,7 +339,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; @@ -383,6 +377,4 @@ end: file_free(file); file = NULL; } - - return ret; } @@ -31,6 +31,10 @@ #ifndef BROWSE_H #define BROWSE_H +#include <sys/types.h> + +#include <stdarg.h> +#include <stdint.h> #include <kcgi.h> #include "file.h" @@ -43,13 +47,12 @@ * All parameters are required. * Returns the length of the created URL, or 0 in case of failure. */ -size_t -build_browse_url(struct kreq *, struct file *); +size_t build_browse_url(struct kreq *, struct file *); /* * Print a browse page that allows to browse through the folder hierarchy. * The KCGI request is required. */ -struct http_ret browse(struct kreq *); +void browse(struct kreq *); #endif /* BROWSE_H */ @@ -31,6 +31,10 @@ #ifndef CGI_H #define CGI_H +#include <sys/types.h> + +#include <stdarg.h> +#include <stdint.h> #include <kcgi.h> #define K_OK(kcgi_call, r) \ @@ -36,8 +36,12 @@ * otherwise it will quit with an error. */ #ifndef LOG_FILE +#ifdef __OpenBSD__ +#define LOG_FILE "/logs/vault.log" +#else #define LOG_FILE "/var/log/vault.log" #endif +#endif /* The directory that contains the HTML template files */ #ifndef TEMPLATE_DIR diff --git a/create.c b/create.c new file mode 100644 index 0000000..5355be0 --- /dev/null +++ b/create.c @@ -0,0 +1,242 @@ +/* + * 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 <sys/types.h> + +#include <stdarg.h> +#include <stdint.h> +#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 */ @@ -30,9 +30,12 @@ #include <sys/types.h> #include <sys/stat.h> -#include <fts.h> + +#include <stdarg.h> +#include <stdint.h> #include <kcgi.h> #include <kcgihtml.h> +#include <fts.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> @@ -122,7 +125,7 @@ header_template_callback(size_t index, void *arg) * GET request, we print delete form */ void -delete_get(struct kreq * r, struct http_ret * ret, struct file * file) +delete_get(struct kreq * r, struct file * file) { struct khtmlreq html; struct ktemplate template; @@ -133,19 +136,13 @@ delete_get(struct kreq * r, struct http_ret * ret, struct file * file) /* action url is form submit url */ if (build_delete_url(r, file) == 0) { - *ret = (struct http_ret) { - KHTTP_500, - "delete: Can't build delete url" - }; + http_exit(r, KHTTP_500, "delete: Can't build delete url"); goto end; } /* read template */ tmpl = page_template_new(DELETE_URL); if (tmpl == NULL) { - *ret = (struct http_ret) { - KHTTP_500, - "delete: Unable to read template" - }; + http_exit(r, KHTTP_500, "delete: Unable to read template"); goto end; } /* print delete form */ @@ -177,7 +174,7 @@ end: * POST request, we handle delete form */ void -delete_post(struct kreq * r, struct http_ret * ret, struct file * f) +delete_post(struct kreq * r, struct file * f) { char path[PATH_MAX]; char *paths[2]; @@ -193,32 +190,22 @@ delete_post(struct kreq * r, struct http_ret * ret, struct file * f) /* prepare action url for return redirection in case of success */ parent = file_get_parent(f); if (parent == NULL || build_browse_url(r, parent) == 0) { - *ret = (struct http_ret) { - KHTTP_500, - "delete: can't build return url" - }; + http_exit(r, KHTTP_500, "delete: can't build return url"); goto end; } - /* build file path on disk */ if (file_get_data_path(f, path, sizeof(path), NULL) <= 0) { - *ret = (struct http_ret) { - KHTTP_500, - "delete: can't build file data path" - }; + http_exit(r, KHTTP_500, "delete: can't build file data path"); goto end; } - if (f->is_dir) { /* don't follow symlinks */ fts_opts = 0x0 | FTS_PHYSICAL; paths[0] = path; paths[1] = NULL; if ((fts = fts_open(paths, fts_opts, NULL)) == NULL) { - *ret = (struct http_ret) { - KHTTP_500, - "delete: can't open file hierarchy" - }; + http_exit(r, KHTTP_500, + "delete: can't open file hierarchy"); goto end; } /* file and directory delete loop */ @@ -227,28 +214,22 @@ delete_post(struct kreq * r, struct http_ret * ret, struct file * f) /* halt on fts errors */ if (fi == FTS_DNR || fi == FTS_ERR || fi == FTS_NS) { - *ret = (struct http_ret) { - KHTTP_500, - "delete: fts_read error" - }; + http_exit(r, KHTTP_500, + "delete: fts_read error"); goto end; } /* delete regular files and empty folders */ else if ((fi == FTS_DP || fi == FTS_F) && remove(ftsent->fts_path) < 0) { - *ret = (struct http_ret) { - KHTTP_500, - "delete: failed to delete file" - }; + http_exit(r, KHTTP_500, + "delete: failed to delete file"); goto end; } } } else { if (remove(path) < 0) { - *ret = (struct http_ret) { - KHTTP_500, - "delete: failed to delete file" - }; + http_exit(r, KHTTP_500, + "delete: failed to delete file"); goto end; } } @@ -257,10 +238,6 @@ delete_post(struct kreq * r, struct http_ret * ret, struct file * f) khttp_head(r, kresps[KRESP_LOCATION], "%s", parent->action_url); http_open(r, KHTTP_303, r->mime); - *ret = (struct http_ret) { - KHTTP_303, - "" - }; end: if (parent != NULL) file_free(parent); @@ -268,28 +245,21 @@ end: fts_close(fts); } - -struct http_ret +void del(struct kreq * r) { char *path; size_t suffix_len; struct file *file; - struct http_ret ret; file = NULL; - ret = (struct http_ret) { - KHTTP_200, "" - }; /* build file corresponding to request (ie. delete) path */ suffix_len = strlen(r->suffix); if (suffix_len > 0) { if (str_concat(&path, r->path, ".", r->suffix, NULL) <= 0) { - ret = (struct http_ret) { - KHTTP_404, - "delete: Unable to build file path" - }; + http_exit(r, KHTTP_404, + "delete: Unable to build file path"); goto end; } } else { @@ -298,21 +268,17 @@ del(struct kreq * r) file = file_new(path); if (file == NULL) { - ret = (struct http_ret) { - KHTTP_404, - "delete: Unable to build data file" - }; + http_exit(r, KHTTP_404, "delete: Unable to build data file"); goto end; } /* print form or handle submission according to HTTP method */ if (r->method == KMETHOD_POST) - delete_post(r, &ret, file); + delete_post(r, file); else - delete_get(r, &ret, file); + delete_get(r, file); end: if (suffix_len > 0) free(path); file_free(file); - return ret; } @@ -49,6 +49,6 @@ build_delete_url(const struct kreq *, struct file *); * Print a delete form (http GET) or handle a delete form submission (http * POST). */ -struct http_ret del(struct kreq *); +void del(struct kreq *); #endif /* DELETE_H */ @@ -76,7 +76,7 @@ build_download_url(const struct kreq * r, struct file * file) return action_url_len; } -struct http_ret +void download(struct kreq * r) { void *buffer; @@ -85,22 +85,16 @@ download(struct kreq * r) char file_path[PATH_MAX]; char *path; size_t path_size, suffix_len; - struct http_ret ret; - /* initialize empty file and return struct to success */ + /* initialize empty file to success */ f = NULL; - ret = (struct http_ret) { - KHTTP_200, "" - }; /* build requested file path, with suffix or without */ suffix_len = strlen(r->suffix); if (suffix_len > 0) { if (str_concat(&path, r->path, ".", r->suffix, NULL) <= 0) { - ret = (struct http_ret) { - KHTTP_414, - "download: unable to build requested file path" - }; + http_exit(r, KHTTP_414, + "download: unable to build requested file path"); goto end; } } else { @@ -109,54 +103,37 @@ download(struct kreq * r) /* build file metadata */ f = file_new(path); if (f == NULL) { - ret = (struct http_ret) { - KHTTP_404, - "download: file metadata failure" - }; + http_exit(r, KHTTP_404, "download: file metadata failure"); goto end; } /* we do not support downloading folders */ if (f->is_dir) { - ret = (struct http_ret) { - KHTTP_400, - "download: can't download folder" - }; + http_exit(r, KHTTP_400, "download: can't download folder"); goto end; } /* memory map the file */ path_size = file_get_data_path(f, file_path, PATH_MAX, NULL); if (path_size == 0 || path_size >= PATH_MAX) { - ret = (struct http_ret) { - KHTTP_404, - "download: unable to build file path" - }; + http_exit(r, KHTTP_404, "download: unable to build file path"); goto end; } fd = open(file_path, O_RDONLY); if (fd < 0) { - ret = (struct http_ret) { - KHTTP_404, - "download: unable to open file" - }; + http_exit(r, KHTTP_404, "download: unable to open file"); goto end; } /* mmap does not work with empty file: st_size = 0 */ if (f->size > 0) { buffer = mmap(NULL, f->size, PROT_READ, MAP_PRIVATE, fd, 0); if (buffer == MAP_FAILED) { - ret = (struct http_ret) { - KHTTP_500, - "download: mmap failed" - }; + http_exit(r, KHTTP_500, "download: mmap failed"); goto end; } } /* write the file */ if (!http_open_file(r, KHTTP_200, f)) { - ret = (struct http_ret) { - KHTTP_500, - "download: filename url encoding failed" - }; + http_exit(r, KHTTP_500, + "download: filename url encoding failed"); goto end; } if (f->size > 0) @@ -169,5 +146,4 @@ end: if (f != NULL && f->size > 0) munmap(buffer, f->size); file_free(f); - return ret; } @@ -49,6 +49,6 @@ build_download_url(const struct kreq *, struct file *); * Return a requested file. * The KCGI request is required. */ -struct http_ret download(struct kreq *); +void download(struct kreq *); #endif /* DOWNLOAD_H */ @@ -34,11 +34,13 @@ extern char** environ; int main(void) { + char **var; + puts("Status: 200 OK\r"); puts("Content-Type: text/html\r"); puts("\r"); - for(char **var = environ; *var; var++) { + for(var = environ; *var; var++) { puts(*var); puts("</br>"); } @@ -53,7 +53,12 @@ http_open_file(struct kreq * r, enum khttp code, const struct file * f) return false; khttp_head(r, kresps[KRESP_STATUS], "%s", khttps[code]); - khttp_head(r, kresps[KRESP_CONTENT_DISPOSITION], + + /* no content-disposition for pictures & videos (open them in browser) */ + if (f->mime != MIME_GIF && f->mime != MIME_JPG && f->mime != MIME_MP4 && + f->mime != MIME_PNG && f->mime != MIME_SVG && f->mime != MIME_WEBM && + f->mime != MIME_WEBP) + khttp_head(r, kresps[KRESP_CONTENT_DISPOSITION], "attachment;filename=\"%s\"", filename); free(filename); khttp_head(r, kresps[KRESP_CONTENT_TYPE], "%s", mime_str(f->mime)); @@ -31,19 +31,15 @@ #ifndef HTTP_H #define HTTP_H +#include <sys/types.h> + +#include <stdint.h> +#include <stdarg.h> #include <kcgi.h> #include "file.h" /* - * Return structure for HTTP action methods - */ -struct http_ret { - enum khttp code; - char *message; -}; - -/* * Initialize headers and start the document body with the provided http code. * All parameters are required. */ @@ -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 @@ -62,60 +65,77 @@ main(void) char *data_dir; enum kcgi_err parse_err; struct kreq r; - struct http_ret ret; - if (kutil_openlog(LOG_FILE) == 0) + if (kutil_openlog(LOG_FILE) == 0) { http_exit(NULL, KHTTP_500, "Unable to open %s", LOG_FILE); + return EXIT_SUCCESS; + } parse_err = khttp_parse(&r, NULL, 0, pages, PAGE__MAX, PAGE_BROWSE); - if (parse_err != KCGI_OK) + if (parse_err != KCGI_OK) { http_exit(NULL, KHTTP_500, "Unable to parse request: %s", kcgi_strerror(parse_err)); + return EXIT_SUCCESS; + } data_dir = config_data_dir(); - if (data_dir == NULL) + if (data_dir == NULL) { http_exit(NULL, KHTTP_500, "Data dir not configured"); + goto end; + } /* A bit of security cannot hurt */ +#ifdef __OpenBSD__ if (-1 == unveil(data_dir, "rwc") || -1 == unveil(TEMPLATE_DIR, "r") - || -1 == unveil(NULL, NULL)) + || -1 == unveil(NULL, NULL)) { http_exit(&r, KHTTP_500, "Unveil failed: %s", strerror(errno)); - if (-1 == pledge("stdio rpath wpath cpath", NULL)) + goto end; + } + if (-1 == pledge("stdio rpath wpath cpath", NULL)) { http_exit(&r, KHTTP_500, "Pledge failed: %s", strerror(errno)); + goto end; + } +#endif /* __OpenBSD__ */ /* * Make sure basic request parameters are as expected : GET or POST, * valid page and HTML document */ - if (r.method != KMETHOD_GET && r.method != KMETHOD_POST) + if (r.method != KMETHOD_GET && r.method != KMETHOD_POST) { http_exit(&r, KHTTP_405, NULL); - if (r.page == PAGE__MAX) + goto end; + } + if (r.page == PAGE__MAX) { http_exit(&r, KHTTP_404, NULL); + goto end; + } switch (r.page) { case PAGE_BROWSE: if (r.mime != KMIME_TEXT_HTML) http_exit(&r, KHTTP_406, NULL); /* Not Acceptable */ - ret = browse(&r); + browse(&r); break; case PAGE_DOWNLOAD: - ret = download(&r); + download(&r); break; case PAGE_UPLOAD: - ret = upload(&r); + upload(&r); break; case PAGE_DELETE: - ret = del(&r); + del(&r); + break; + case PAGE_CREATE: + create(&r); break; default: http_exit(&r, KHTTP_404, NULL); + goto end; } +end: khttp_free(&r); - if (ret.code >= KHTTP_400) - http_exit(&r, ret.code, ret.message); - return EXIT_SUCCESS; } @@ -132,7 +132,6 @@ mime_from_ext(const char *ext) return MIME_BIN; } - const char * mime_str(enum mime mime) { 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; @@ -1,4 +1,5 @@ #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "browse.h" @@ -18,41 +19,163 @@ int tests_run; static char * -test_browse_invalid_traversal() +test_build_browse_url() { + size_t s; + struct file f; struct kreq r; - struct http_ret ret; - /* attempt to traverse out of DATA_DIR... */ + /* nominal case */ r = (struct kreq) { - .path = "..", + .pname = "vault", + .path = "Alice/Bob", .suffix = "", - .mime = KMIME_TEXT_HTML }; - ret = browse(&r); + f = (struct file) { + .is_dir = true, + .path = "Alice/Bob", + .action_url = NULL + }; + s = build_browse_url(&r, &f); + mu_assert("failed to build browse url", s > 0 && f.action_url != NULL); + + /* refuse to browse files */ + if (f.action_url != NULL) + free(f.action_url); + f.action_url = NULL; + f.is_dir = false; + s = build_browse_url(&r, &f); + mu_assert("allowed to browse a file", s <= 0 && f.action_url == NULL); + + return 0; +} + +static char * +test_build_download_url() +{ + size_t s; + struct file f; + struct kreq r; + + /* nominal case */ + r = (struct kreq) { + .pname = "vault", + .path = "Alice/Bob", + .suffix = "txt", + }; + f = (struct file) { + .is_dir = false, + .path = "Alice/Bob.txt", + .action_url = NULL + }; + s = build_download_url(&r, &f); + mu_assert("failed to build download url", + s > 0 && f.action_url != NULL); + + /* refuse to download directories */ + if (f.action_url != NULL) + free(f.action_url); + f.action_url = NULL; + f.is_dir = true; + s = build_download_url(&r, &f); + mu_assert("allowed to download a directory", + s <= 0 && f.action_url == NULL); + + return 0; +} + +static char * +test_build_upload_url() +{ + size_t s; + struct file f; + struct kreq r; + + /* nominal case */ + r = (struct kreq) { + .pname = "vault", + .path = "Alice/Bob", + .suffix = "", + }; + f = (struct file) { + .is_dir = true, + .path = "Alice/Bob", + .action_url = NULL + }; + s = build_upload_url(&r, &f); + mu_assert("failed to build upload url", s > 0 && f.action_url != NULL); + + /* refuse to upload to a file */ + if (f.action_url != NULL) + free(f.action_url); + f.action_url = NULL; + f.is_dir = false; + s = build_upload_url(&r, &f); + mu_assert("allowed to upload to a file", + s <= 0 && f.action_url == NULL); - /* ...should return an error */ - mu_assert("error, browse allowed invalid traversal!", - ret.code >= KHTTP_400); return 0; } static char * -test_browse_path_too_long() +test_build_delete_url() { + size_t s; + struct file f; struct kreq r; - struct http_ret ret; - /* a lengthy path should cause URL overflow... */ + /* nominal case */ r = (struct kreq) { - .path = "this/is/a/very/very/lengthy/path/that/should/overflow/the/maximum/allowed/path/length/of/PATH_MAX/well/of/course/this/limit/is/platform/dependent", + .pname = "vault", + .path = "Alice/Bob", .suffix = "", - .mime = KMIME_TEXT_HTML }; - ret = browse(&r); + f = (struct file) { + .is_dir = true, + .path = "Alice/Bob", + .action_url = NULL + }; + s = build_delete_url(&r, &f); + mu_assert("failed to build delete url", s > 0 && f.action_url != NULL); + + /* allow also to delete a file */ + if (f.action_url != NULL) + free(f.action_url); + f.action_url = NULL; + f.is_dir = false; + s = build_delete_url(&r, &f); + mu_assert("refused to delete a file", s > 0 && f.action_url != NULL); - mu_assert("error, browse allowed invalid traversal!", - ret.code >= KHTTP_400); + return 0; +} + +static char * +test_url_build() +{ + char dst[BUF_SZ], *expected; + size_t dst_len; + + expected = "/vault/browse"; + dst_len = url_build(dst, BUF_SZ, "/vault/browse", "", NULL); + mu_assert("test_url_build failed", dst_len > 0 && dst_len < BUF_SZ); + mu_assert("test_url_build failed", strncmp(expected, dst, BUF_SZ) == 0); + + expected = "/vault/browse/hello/world"; + dst_len = url_build(dst, BUF_SZ, "/vault/browse", "hello", "world", + NULL); + mu_assert("test_url_build failed", dst_len > 0 && dst_len < BUF_SZ); + mu_assert("test_url_build failed", strncmp(expected, dst, BUF_SZ) == 0); + + expected = "/vault/browse"; + dst_len = url_build(dst, BUF_SZ, "/vault/", "browse/", NULL); + mu_assert("test_url_build failed", dst_len > 0 && dst_len < BUF_SZ); + mu_assert("test_url_build failed", strncmp(expected, dst, BUF_SZ) == 0); + + dst_len = url_build(dst, BUF_SZ, "", "", NULL); + mu_assert("test_url_build failed", dst_len == 0); + + dst_len = url_build(dst, BUF_SZ, NULL); + mu_assert("test_url_build failed", dst_len == 0); return 0; } @@ -61,7 +184,6 @@ test_upload_post() { char *file_content; struct kreq r; - struct http_ret ret; struct kpair fields[2]; file_content = "text file"; @@ -86,9 +208,7 @@ test_upload_post() .fieldsz = 2, .fields = fields }; - ret = upload(&r); - - mu_assert("error, POST upload failed!", ret.code <= KHTTP_400); + upload(&r); return 0; } @@ -96,16 +216,12 @@ static char * test_download() { struct kreq r; - struct http_ret ret; - r = (struct kreq) { .pname = "/vault", .path = "a", .suffix = "txt", }; - ret = download(&r); - - mu_assert("error, download failed!", ret.code <= KHTTP_400); + download(&r); return 0; } @@ -113,17 +229,13 @@ static char * test_delete_get() { struct kreq r; - struct http_ret ret; - r = (struct kreq) { .pname = "/vault", .path = "a", .suffix = "txt", .method = KMETHOD_GET, }; - ret = del(&r); - - mu_assert("error, GET delete failed!", ret.code <= KHTTP_400); + del(&r); return 0; } @@ -131,60 +243,34 @@ static char * test_delete_post() { struct kreq r; - struct http_ret ret; - r = (struct kreq) { .pname = "/vault", .path = "Folder", .suffix = "", .method = KMETHOD_POST, }; - ret = del(&r); - - mu_assert("error, POST delete failed!", ret.code <= KHTTP_400); - return 0; -} - -static char * -test_url_build() -{ - char dst[BUF_SZ], *expected; - size_t dst_len; - - expected = "/vault/browse"; - dst_len = url_build(dst, BUF_SZ, "/vault/browse", "", NULL); - mu_assert("test_url_build failed", dst_len > 0 && dst_len < BUF_SZ); - mu_assert("test_url_build failed", strncmp(expected, dst, BUF_SZ) == 0); - - expected = "/vault/browse/hello/world"; - dst_len = url_build(dst, BUF_SZ, "/vault/browse", "hello", "world", - NULL); - mu_assert("test_url_build failed", dst_len > 0 && dst_len < BUF_SZ); - mu_assert("test_url_build failed", strncmp(expected, dst, BUF_SZ) == 0); - - expected = "/vault/browse"; - dst_len = url_build(dst, BUF_SZ, "/vault/", "browse/", NULL); - mu_assert("test_url_build failed", dst_len > 0 && dst_len < BUF_SZ); - mu_assert("test_url_build failed", strncmp(expected, dst, BUF_SZ) == 0); - - dst_len = url_build(dst, BUF_SZ, "", "", NULL); - mu_assert("test_url_build failed", dst_len == 0); - - dst_len = url_build(dst, BUF_SZ, NULL); - mu_assert("test_url_build failed", dst_len == 0); + del(&r); return 0; } static char * all_tests() { - mu_run_test(test_browse_invalid_traversal); - mu_run_test(test_browse_path_too_long); - mu_run_test(test_url_build); + /* the following tests are for debug only */ + /* mu_run_test(test_download); mu_run_test(test_upload_post); mu_run_test(test_delete_get); mu_run_test(test_delete_post); + */ + + /* url building */ + mu_run_test(test_url_build); + mu_run_test(test_build_browse_url); + mu_run_test(test_build_download_url); + mu_run_test(test_build_upload_url); + mu_run_test(test_build_delete_url); + return 0; } @@ -28,9 +28,13 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <stdlib.h> +#include <sys/types.h> + +#include <stdarg.h> +#include <stdint.h> #include <kcgi.h> #include <kcgihtml.h> +#include <stdlib.h> #include <limits.h> #include <stdio.h> #include <string.h> @@ -121,7 +125,7 @@ header_template_callback(size_t index, void *arg) * GET request, we print upload form */ void -upload_get(struct kreq * r, struct http_ret * ret, struct file * file) +upload_get(struct kreq * r, struct file * file) { struct khtmlreq html; struct ktemplate template; @@ -132,19 +136,13 @@ upload_get(struct kreq * r, struct http_ret * ret, struct file * file) /* action url is form submit url */ if (build_upload_url(r, file) == 0) { - *ret = (struct http_ret) { - KHTTP_500, - "upload: Can't build upload url" - }; + http_exit(r, KHTTP_500, "upload: Can't build upload url"); goto end; } /* read template */ tmpl = page_template_new(UPLOAD_URL); if (tmpl == NULL) { - *ret = (struct http_ret) { - KHTTP_500, - "upload: Unable to read template" - }; + http_exit(r, KHTTP_500, "upload: Unable to read template"); goto end; } /* print upload form */ @@ -176,7 +174,7 @@ end: * POST request, we handle upload form */ void -upload_post(struct kreq * r, struct http_ret * ret, struct file * f) +upload_post(struct kreq * r, struct file * f) { char path[PATH_MAX]; FILE *to_write; @@ -184,13 +182,9 @@ upload_post(struct kreq * r, struct http_ret * ret, struct file * f) /* prepare action url for return redirection in case of success */ if (build_browse_url(r, f) == 0) { - *ret = (struct http_ret) { - KHTTP_500, - "upload: can't build return url" - }; + http_exit(r, KHTTP_500, "upload: can't build return url"); return; } - /* * data should have been validated prior to handling the request, so * here we should have an array of fields named "file", each field @@ -200,33 +194,25 @@ upload_post(struct kreq * r, struct http_ret * ret, struct file * f) struct kpair *upl = &(r->fields[i]); path_len = file_get_data_path(f, path, PATH_MAX, upl->file); if (path_len == 0 || path_len >= PATH_MAX) { - *ret = (struct http_ret) { - KHTTP_500, - "upload: can't build file path" - }; + http_exit(r, KHTTP_500, + "upload: can't build file path"); return; } to_write = fopen(path, "wb"); if (to_write == NULL) { - *ret = (struct http_ret) { - KHTTP_500, - "upload: can't open file for writing" - }; + http_exit(r, KHTTP_500, + "upload: can't open file for writing"); return; } if (fwrite(upl->val, sizeof(char), upl->valsz, to_write) != upl->valsz) { - *ret = (struct http_ret) { - KHTTP_500, - "upload: error while writing file" - }; + http_exit(r, KHTTP_500, + "upload: error while writing file"); return; } if (fclose(to_write) != 0) { - *ret = (struct http_ret) { - KHTTP_500, - "upload: error while closing file" - }; + http_exit(r, KHTTP_500, + "upload: error while closing file"); return; } } @@ -234,41 +220,27 @@ upload_post(struct kreq * r, struct http_ret * ret, struct file * f) /* on success, redirect to the directory where new files were created */ khttp_head(r, kresps[KRESP_LOCATION], "%s", f->action_url); http_open(r, KHTTP_303, r->mime); - - *ret = (struct http_ret) { - KHTTP_303, - "" - }; } - -struct http_ret +void upload(struct kreq * r) { struct file *file; - struct http_ret ret; file = NULL; - ret = (struct http_ret) { - KHTTP_200, "" - }; /* build file corresponding to request (ie. upload) path */ file = file_new(r->path); if (file == NULL) { - ret = (struct http_ret) { - KHTTP_404, - "upload: Unable to build data file" - }; + http_exit(r, KHTTP_404, "upload: Unable to build data file"); goto end; } /* print form or handle submission according to HTTP method */ if (r->method == KMETHOD_POST) - upload_post(r, &ret, file); + upload_post(r, file); else - upload_get(r, &ret, file); + upload_get(r, file); end: file_free(file); - return ret; } @@ -49,6 +49,6 @@ build_upload_url(const struct kreq *, struct file *); * Print an upload form (http GET) or handle an upload form submission (http * POST). */ -struct http_ret upload(struct kreq *); +void upload(struct kreq *); #endif /* UPLOAD_H */ |
