simplefiles

A lightweight C CGI web application for managing files.
git clone https://scm.kuandu.systems/git-raw/simplefiles.git
Log | Files | Refs | README | LICENSE

rename.c (4389B)


      1 #include <errno.h>
      2 #include <limits.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include <sys/stat.h>
      7 #include <unistd.h>
      8 
      9 #include "common.h"
     10 #include "config.h"
     11 #include "html.h"
     12 #include "http.h"
     13 #include "request.h"
     14 
     15 static int do_get(void);
     16 static int do_post(void);
     17 
     18 
     19 static int
     20 do_get(void)
     21 {
     22 	char filename[NAME_MAX + 1];
     23 	char path[PATH_MAX];
     24 	char template[PATH_MAX];
     25 	struct stat st;
     26 
     27 	if (!config_write_allowed()) {
     28 		reply("403 Forbidden", "Repository is read-only");
     29 		return 0;
     30 	}
     31 	if (get_filename(getenv("QUERY_STRING"),
     32 		filename,
     33 		sizeof(filename)) == -1)
     34 		return 0;
     35 
     36 	if (build_storage_path(path,
     37 		sizeof(path),
     38 		storage_root(),
     39 		repository(),
     40 		filename) == -1) {
     41 		reply("500 Internal Server Error",
     42 		    "Path too long");
     43 		return 0;
     44 	}
     45 	if (stat(path, &st) == -1 || !S_ISREG(st.st_mode)) {
     46 		reply("404 Not Found",
     47 		    "File not found");
     48 		return 0;
     49 	}
     50 	struct html_var vars[] = {
     51 		{"FILENAME", filename},
     52 		{"CANCEL_URI", DEFAULT_REDIRECT_URI},
     53 	};
     54 
     55 	if (snprintf(template,
     56 		sizeof(template),
     57 		"%s/%s",
     58 		simplefiles_root(), RENAME_TEMPLATE_NAME) >= (int) sizeof(template)) {
     59 		reply("500 Internal Server Error",
     60 		    "Template path too long");
     61 		return 0;
     62 	}
     63 	if (render_template_response(
     64 		template,
     65 		vars,
     66 		sizeof(vars) / sizeof(vars[0]),
     67 		NULL,
     68 		0) == -1) {
     69 		reply("500 Internal Server Error", "Template error");
     70 		return 0;
     71 	}
     72 	return 0;
     73 }
     74 
     75 
     76 static int
     77 do_post(void)
     78 {
     79 	char *body;
     80 	const char *p;
     81 	char oldname[NAME_MAX + 1];
     82 	char newname[NAME_MAX + 1];
     83 	char oldpath[PATH_MAX];
     84 	char newpath[PATH_MAX];
     85 	long len;
     86 	char *end;
     87 	int lock_fd = -1;
     88 	struct stat st;
     89 	int rc;
     90 
     91 	if (!config_write_allowed()) {
     92 		reply("403 Forbidden", "Repository is read-only");
     93 		return 0;
     94 	}
     95 	rc = check_csrf_origin();
     96 
     97 	if (rc == -2) {
     98 		reply("500 Internal Server Error",
     99 		    "CSRF origin is not configured");
    100 		return 0;
    101 	}
    102 	if (rc == -1) {
    103 		reply("403 Forbidden",
    104 		    "Forbidden");
    105 		return 0;
    106 	}
    107 	p = getenv("CONTENT_LENGTH");
    108 
    109 	if (p == NULL) {
    110 		reply("400 Bad Request",
    111 		    "Missing content length");
    112 		return 0;
    113 	}
    114 	errno = 0;
    115 
    116 	len = strtol(p, &end, 10);
    117 
    118 	if (errno != 0 ||
    119 	    end == p ||
    120 	    *end != '\0' ||
    121 	    len <= 0 ||
    122 	    len > 1024) {
    123 		reply("400 Bad Request",
    124 		    "Invalid content length");
    125 		return 0;
    126 	}
    127 	body = malloc((size_t) len + 1);
    128 
    129 	if (body == NULL) {
    130 		reply("500 Internal Server Error",
    131 		    "Out of memory");
    132 		return 0;
    133 	}
    134 	if (fread(body, 1, (size_t) len, stdin)
    135 	    != (size_t) len) {
    136 		free(body);
    137 		reply("400 Bad Request",
    138 		    "Could not read request");
    139 		return 0;
    140 	}
    141 	body[len] = '\0';
    142 
    143 
    144 	if (get_filename(body,
    145 		oldname,
    146 		sizeof(oldname)) == -1) {
    147 		free(body);
    148 		return 0;
    149 	}
    150 	if (get_new_filename(body,
    151 		newname,
    152 		sizeof(newname)) == -1) {
    153 		free(body);
    154 		return 0;
    155 	}
    156 	free(body);
    157 
    158 
    159 	if (strcmp(oldname, newname) == 0) {
    160 		redirect(DEFAULT_REDIRECT_URI);
    161 		return 0;
    162 	}
    163 	if (build_storage_path(oldpath,
    164 		sizeof(oldpath),
    165 		storage_root(),
    166 		repository(),
    167 		oldname) == -1) {
    168 		reply("500 Internal Server Error",
    169 		    "Path too long");
    170 		return 0;
    171 	}
    172 	if (build_storage_path(newpath,
    173 		sizeof(newpath),
    174 		storage_root(),
    175 		repository(),
    176 		newname) == -1) {
    177 		reply("500 Internal Server Error",
    178 		    "Path too long");
    179 		return 0;
    180 	}
    181 	lock_fd = repository_lock(repository());
    182 
    183 	if (lock_fd == -1) {
    184 		reply("500 Internal Server Error",
    185 		    "Could not lock repository");
    186 		return 0;
    187 	}
    188 	if (stat(oldpath, &st) == -1 ||
    189 	    !S_ISREG(st.st_mode)) {
    190 		reply("404 Not Found",
    191 		    "File not found");
    192 		goto out;
    193 	}
    194 	if (stat(newpath, &st) == 0) {
    195 		reply("409 Conflict",
    196 		    "Destination already exists");
    197 		goto out;
    198 	}
    199 	if (errno != ENOENT) {
    200 		reply("500 Internal Server Error",
    201 		    "Could not check destination");
    202 		goto out;
    203 	}
    204 	if (rename(oldpath, newpath) == -1) {
    205 		reply("500 Internal Server Error",
    206 		    "Could not rename file");
    207 		goto out;
    208 	}
    209 	repository_unlock(lock_fd);
    210 	lock_fd = -1;
    211 
    212 	redirect(DEFAULT_REDIRECT_URI);
    213 
    214 
    215 out:
    216 	if (lock_fd != -1)
    217 		repository_unlock(lock_fd);
    218 
    219 	return 0;
    220 }
    221 
    222 
    223 int
    224 main(void)
    225 {
    226 	const char *method;
    227 
    228 	method = getenv("REQUEST_METHOD");
    229 
    230 	if (method == NULL) {
    231 		reply("400 Bad Request",
    232 		    "Missing request method");
    233 		return 0;
    234 	}
    235 	if (strcmp(method, "GET") == 0)
    236 		return do_get();
    237 
    238 
    239 	if (strcmp(method, "POST") == 0)
    240 		return do_post();
    241 
    242 
    243 	reply("405 Method Not Allowed",
    244 	    "GET or POST required");
    245 
    246 	return 0;
    247 }