summaryrefslogtreecommitdiff
path: root/delete.c
diff options
context:
space:
mode:
Diffstat (limited to 'delete.c')
-rw-r--r--delete.c318
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;
+}