/* * Copyright 2024, Vincent Douillet * * 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 #include #include #include #include #include #include #include #include #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 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) { http_exit(r, KHTTP_500, "delete: Can't build delete url"); goto end; } /* read template */ tmpl = page_template_new(DELETE_URL); if (tmpl == NULL) { http_exit(r, KHTTP_500, "delete: Unable to read template"); goto end; } /* print delete 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 delete form */ void delete_post(struct kreq * r, 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) { 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) { 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) { http_exit(r, 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) { 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) { http_exit(r, KHTTP_500, "delete: failed to delete file"); goto end; } } } else { if (remove(path) < 0) { http_exit(r, 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); end: if (parent != NULL) file_free(parent); if (fts != NULL) fts_close(fts); } void del(struct kreq * r) { char *path; size_t suffix_len; struct file *file; file = NULL; /* 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) { http_exit(r, KHTTP_404, "delete: Unable to build file path"); goto end; } } else { path = r->path; } file = file_new(path); if (file == NULL) { 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, file); else delete_get(r, file); end: if (suffix_len > 0) free(path); file_free(file); }