diff options
author | Vincent Douillet <vincent@vdouillet.fr> | 2024-12-24 11:52:50 +0100 |
---|---|---|
committer | Vincent Douillet <vincent@vdouillet.fr> | 2024-12-26 10:49:13 +0100 |
commit | 2e04b2c23c66a42372d0ecaea2b0f4b2c7b7c7e5 (patch) | |
tree | 9249bb0e9792a11bb27df78a4d93a0b3c0be0b33 /delete.c | |
parent | 878cd6e8792d5ac03eea59fec9fe8e325440d50c (diff) |
delete files
Diffstat (limited to 'delete.c')
-rw-r--r-- | delete.c | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/delete.c b/delete.c new file mode 100644 index 0000000..29c5cb8 --- /dev/null +++ b/delete.c @@ -0,0 +1,318 @@ +/* + * Copyright 2024, 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 <sys/stat.h> +#include <fts.h> +#include <kcgi.h> +#include <kcgihtml.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "browse.h" +#include "cgi.h" +#include "delete.h" +#include "http.h" +#include "str.h" +#include "template.h" +#include "url.h" + +/* + * delete url = r->pname / DELETE_URL / file->path + */ +size_t +build_delete_url(const struct kreq * r, struct file * file) +{ + char action_url[PATH_MAX]; + size_t action_url_len; + + action_url_len = url_build(action_url, PATH_MAX, r->pname, DELETE_URL, + file->path, NULL); + if (action_url_len == 0 || action_url_len >= PATH_MAX) { + kutil_warn(r, NULL, + "delete: 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, + "delete: 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 delete 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 delete header template: %zd", + index); + return 0; + } + + return 1; +} + +/* + * GET request, we print delete form + */ +void +delete_get(struct kreq * r, struct http_ret * ret, 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_delete_url(r, file) == 0) { + *ret = (struct http_ret) { + 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" + }; + goto end; + } + /* print delete form */ + http_open(r, KHTTP_200, r->mime); + + 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 delete form + */ +void +delete_post(struct kreq * r, struct http_ret * ret, struct file * f) +{ + char path[PATH_MAX]; + char *paths[2]; + FTS *fts; + FTSENT *ftsent; + int fts_opts; + struct file *parent; + unsigned short fi; + + fts = NULL; + ftsent = NULL; + + /* 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" + }; + 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" + }; + 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" + }; + goto end; + } + /* file and directory delete loop */ + while ((ftsent = fts_read(fts)) != NULL) { + fi = ftsent->fts_info; + + /* halt on fts errors */ + if (fi == FTS_DNR || fi == FTS_ERR || fi == FTS_NS) { + *ret = (struct http_ret) { + 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" + }; + goto end; + } + } + } else { + if (remove(path) < 0) { + *ret = (struct http_ret) { + KHTTP_500, + "delete: failed to delete file" + }; + goto end; + } + } + + /* on success, redirect to the parent directory of deleted files */ + 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); + if (fts != NULL) + fts_close(fts); +} + + +struct http_ret +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" + }; + goto end; + } + } else { + path = r->path; + } + + file = file_new(path); + if (file == NULL) { + ret = (struct http_ret) { + 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); + else + delete_get(r, &ret, file); + +end: + if (suffix_len > 0) + free(path); + file_free(file); + return ret; +} |