* Gets parsed requests and performs the requested actions / sends the requested
* response.
*
- * These functions are only called from storage processes.
*/
-static char *escape_name(const char *name);
-static char *make_etag(struct stat *stat_buf);
-static int serve_directory(struct rs_request *request, struct stat *stat_buf);
-static int serve_file_head(struct rs_request *request, struct stat *stat_buf,
- const char *mime_type);
-static int serve_file(struct rs_request *request, struct stat *stat_buf);
-static int handle_get_or_head(struct rs_request *request, int include_body);
+static char *make_disk_path(char *dom_user, char *path, gss_buffer_t authuser, char **storage_root);
+static evhtp_res serve_directory(evhtp_request_t *request, char *disk_path,
+ struct stat *stat_buf);
+static evhtp_res serve_file_head(evhtp_request_t *request_t, char *disk_path,
+ struct stat *stat_buf,const char *mime_type);
+static evhtp_res serve_file(evhtp_request_t *request, const char *disk_path,
+ struct stat *stat_buf);
+static evhtp_res handle_get_or_head(evhtp_request_t *request, gss_buffer_t authuser, int include_body);
-
-int storage_handle_head(struct rs_request *request) {
- return handle_get_or_head(request, 0);
+evhtp_res storage_handle_head(evhtp_request_t *request, gss_buffer_t authuser) {
+ if(RS_EXPERIMENTAL) {
+ return handle_get_or_head(request, authuser, 0);
+ } else {
+ return EVHTP_RES_METHNALLOWED;
+ }
}
-int storage_handle_get(struct rs_request *request) {
- return handle_get_or_head(request, 1);
+evhtp_res storage_handle_get(evhtp_request_t *request, gss_buffer_t authuser) {
+ log_debug("storage_handle_get()");
+ return handle_get_or_head(request, authuser, 1);
}
-int storage_handle_put(struct rs_request *request) {
- return 501;
-}
+evhtp_res storage_handle_put(evhtp_request_t *request, gss_buffer_t authuser) {
+ log_debug("HANDLE PUT");
-int storage_handle_delete(struct rs_request *request) {
- return 501;
-}
+ if(request->uri->path->file == NULL) {
+ // PUT to directories aren't allowed
+ return 400;
+ }
+ char *storage_root = NULL;
+ char *disk_path = make_disk_path(REQUEST_GET_USER(request),
+ REQUEST_GET_PATH(request),
+ authuser,
+ &storage_root);
+ if(disk_path == NULL) {
+ return EVHTP_RES_SERVERR;
+ }
+ // check if file exists (needed for preconditions and response code)
+ struct stat stat_buf;
+ memset(&stat_buf, 0, sizeof(struct stat));
+ int exists = stat(disk_path, &stat_buf) == 0;
-static char *make_etag(struct stat *stat_buf) {
- char *etag = malloc(21);
- if(etag == NULL) {
- log_error("malloc() failed: %s", strerror(errno));
- return NULL;
+ // check preconditions
+ do {
+
+ // PUT and DELETE requests MAY have an 'If-Match' request header [HTTP], and
+ // MUST fail with a 412 response code if that doesn't match the document's
+ // current version.
+
+ evhtp_header_t *if_match = evhtp_headers_find_header(request->headers_in, "If-Match");
+ if(if_match && ((!exists) || strcmp(get_etag(disk_path), if_match->val) != 0)) {
+ return 412;
+ }
+
+ // A PUT request MAY have an 'If-None-Match:*' header [HTTP], in which
+ // case it MUST fail with a 412 response code if the document already
+ // exists.
+
+ evhtp_header_t *if_none_match = evhtp_headers_find_header(request->headers_in, "If-None-Match");
+ if(if_none_match && strcmp(if_none_match->val, "*") == 0 && exists) {
+ return 412;
+ }
+
+ } while(0);
+
+#if 0
+ // look up uid and gid of current user, so we can chown() correctly.
+ uid_t uid;
+ gid_t gid;
+ do {
+ char *bufptr = NULL;
+ struct passwd *user_entry = user_get_entry(REQUEST_GET_USER(request), &bufptr);
+ if(user_entry > 0) {
+ uid = user_entry->pw_uid;
+ gid = user_entry->pw_gid;
+ free(user_entry);
+ free(bufptr);
+ } else {
+ return EVHTP_RES_SERVERR;
+ }
+ } while(0);
+#endif
+
+ // create parent directories
+ do {
+
+ char *path_copy = strdup(REQUEST_GET_PATH(request));
+ if(path_copy == NULL) {
+ log_error("strdup() failed: %s", strerror(errno));
+ free(disk_path);
+ free(storage_root);
+ return EVHTP_RES_SERVERR;
+ }
+ char *dir_path = dirname(path_copy);
+ if(strcmp(dir_path, ".") == 0) { // PUT to file below root directory
+ continue;
+ }
+ char *saveptr = NULL;
+ char *dir_name;
+ int dirfd = open(storage_root, O_RDONLY), prevfd;
+ if(dirfd == -1) {
+ log_error("failed to open() storage path (\"%s\"): %s", storage_root, strerror(errno));
+ free(disk_path);
+ free(path_copy);
+ free(storage_root);
+ return EVHTP_RES_SERVERR;
+ }
+ struct stat dir_stat;
+ log_debug("strtok_r(\"%s\", ...), (dir_path: %p, saveptr: %p)", dir_path, dir_path, saveptr);
+ for(dir_name = strtok_r(dir_path, "/", &saveptr);
+ dir_name != NULL;
+ dir_name = strtok_r(NULL, "/", &saveptr)) {
+ if(fstatat(dirfd, dir_name, &dir_stat, 0) == 0) {
+ if(! S_ISDIR(dir_stat.st_mode)) {
+ // exists, but not a directory
+ log_error("Can't PUT to %s, found a non-directory parent.", request->uri->path->full);
+ close(dirfd);
+ free(disk_path);
+ free(path_copy);
+ free(storage_root);
+ return 400;
+ } else {
+ // directory exists
+ }
+ } else {
+ if(mkdirat(dirfd, dir_name, S_IRWXU | S_IRWXG) != 0) {
+ log_error("mkdirat() failed: %s", strerror(errno));
+ close(dirfd);
+ free(disk_path);
+ free(path_copy);
+ free(storage_root);
+ return EVHTP_RES_SERVERR;
+ }
+
+#if 0
+ if(fchownat(dirfd, dir_name, uid, gid, AT_SYMLINK_NOFOLLOW) != 0) {
+ log_warn("failed to chown() newly created directory: %s", strerror(errno));
+ }
+#endif
+ }
+ prevfd = dirfd;
+ dirfd = openat(prevfd, dir_name, O_RDONLY);
+ close(prevfd);
+ if(dirfd == -1) {
+ log_error("failed to openat() next directory (\"%s\"): %s",
+ dir_name, strerror(errno));
+ free(disk_path);
+ free(path_copy);
+ free(storage_root);
+ return EVHTP_RES_SERVERR;
+ }
+ }
+
+ free(path_copy);
+ free(storage_root);
+ close(dirfd);
+
+ } while(0);
+
+ // open (and possibly create) file
+ int fd = open(disk_path, O_NONBLOCK | O_CREAT | O_WRONLY | O_TRUNC,
+ RS_FILE_CREATE_MODE);
+
+ if(fd == -1) {
+ log_error("open() failed to open file \"%s\": %s", disk_path, strerror(errno));
+ free(disk_path);
+ return EVHTP_RES_SERVERR;
+ }
+
+ if(! exists) {
+#if 0
+ if(fchown(fd, uid, gid) != 0) {
+ log_warn("Failed to chown() newly created file: %s", strerror(errno));
+ }
+#endif
}
- snprintf(etag, 20, "%lld", ((long long int)stat_buf->st_mtime) * 1000);
- return etag;
+
+ // write buffered data
+ // TODO: open (and write) file earlier in the request, so it doesn't have to be buffered completely.
+ evbuffer_write(request->buffer_in, fd);
+
+ char *content_type = "application/octet-stream; charset=binary";
+ evhtp_kv_t *content_type_header = evhtp_headers_find_header(request->headers_in, "Content-Type");
+
+ if(content_type_header != NULL) {
+ content_type = content_type_header->val;
+ }
+
+ // remember content type in extended attributes
+ if(content_type_to_xattr(disk_path, content_type) != 0) {
+ log_error("Setting xattr for content type failed. Ignoring.");
+ }
+
+ close(fd);
+
+ // stat created file again to generate new etag
+ memset(&stat_buf, 0, sizeof(struct stat));
+ if(stat(disk_path, &stat_buf) != 0) {
+ log_error("failed to stat() file after writing: %s", strerror(errno));
+ free(disk_path);
+ return EVHTP_RES_SERVERR;
+ }
+
+ char *etag_string = get_etag(disk_path);
+
+ ADD_RESP_HEADER_CP(request, "Content-Type", content_type);
+ ADD_RESP_HEADER_CP(request, "ETag", etag_string);
+
+ free(etag_string);
+ free(disk_path);
+
+ return exists ? EVHTP_RES_OK : EVHTP_RES_CREATED;
}
-// escape backslashes (/) and double quotes (") to put the given string
-// in quoted JSON strings.
-static char *escape_name(const char *name) {
- int max_len = strlen(name) * 2, i = 0;
- char *escaped = malloc(max_len + 1);
- if(escaped == NULL) {
- perror("malloc() failed");
- return NULL;
+evhtp_res storage_handle_delete(evhtp_request_t *request, gss_buffer_t authuser) {
+
+ if(request->uri->path->file == NULL) {
+ // DELETE to directories aren't allowed
+ return 400;
}
- const char *name_p;
- for(name_p = name; *name_p != 0; name_p++) {
- if(*name_p == '"' || *name_p == '\\') {
- escaped[i++] = '\\';
+
+ char *storage_root = NULL;
+ char *disk_path = make_disk_path(REQUEST_GET_USER(request),
+ REQUEST_GET_PATH(request),
+ authuser,
+ &storage_root);
+ if(disk_path == NULL) {
+ return EVHTP_RES_SERVERR;
+ }
+
+ struct stat stat_buf;
+ if(stat(disk_path, &stat_buf) == 0) {
+
+ if(S_ISDIR(stat_buf.st_mode)) {
+ return 400;
+ }
+
+ char *etag_string = get_etag(disk_path);
+
+ evhtp_header_t *if_match = evhtp_headers_find_header(request->headers_in, "If-Match");
+ if(if_match && (strcmp(etag_string, if_match->val) != 0)) {
+ return 412;
+ }
+
+ ADD_RESP_HEADER_CP(request, "ETag", etag_string);
+
+ // file exists, delete it.
+ if(unlink(disk_path) == -1) {
+ log_error("unlink() failed: %s", strerror(errno));
+ return EVHTP_RES_SERVERR;
+ }
+
+ /*
+ * remove empty parents
+ */
+ char *path_copy = strdup(REQUEST_GET_PATH(request));
+ if(path_copy == NULL) {
+ log_error("strdup() failed to copy path: %s", strerror(errno));
+ free(disk_path);
+ return EVHTP_RES_SERVERR;
}
- escaped[i++] = *name_p;
+ char *dir_path;
+ int rootdirfd = open(storage_root, O_RDONLY);
+ if(rootdirfd == -1) {
+ log_error("failed to open() storage root: %s", strerror(errno));
+ free(path_copy);
+ free(disk_path);
+ return EVHTP_RES_SERVERR;
+ }
+ int result;
+ // skip leading slash
+ char *relative_path = path_copy + 1;
+ for(dir_path = dirname(relative_path);
+ ! (dir_path[0] == '.' && dir_path[1] == 0); // reached root
+ dir_path = dirname(dir_path)) {
+ log_debug("unlinking %s (relative to %s)", dir_path, storage_root);
+ result = unlinkat(rootdirfd, dir_path, AT_REMOVEDIR);
+ if(result != 0) {
+ if(errno == ENOTEMPTY || errno == EEXIST) {
+ // non-empty directory reached
+ break;
+ } else {
+ // other error occured
+ log_error("(while trying to remove %s)\n", dir_path);
+ log_error("unlinkat() failed to remove parent directory: %s", strerror(errno));
+ free(path_copy);
+ free(disk_path);
+ return EVHTP_RES_SERVERR;
+ }
+ }
+ }
+ close(rootdirfd);
+ free(path_copy);
+ } else {
+ // file doesn't exist, return 404.
+ return 404;
}
- escaped[i++] = 0;
- escaped = realloc(escaped, i);
- return escaped;
+
+ free(storage_root);
+
+ return 200;
+}
+
+size_t json_buf_writer(char *buf, size_t count, void *arg) {
+ return evbuffer_add((struct evbuffer*)arg, buf, count);
}
// serve a directory response for the given request
-static int serve_directory(struct rs_request *request, struct stat *stat_buf) {
- struct evbuffer *buf = evbuffer_new();
- if(buf == NULL) {
- log_error("evbuffer_new() failed: %s", strerror(errno));
- return 500;
- }
- DIR *dir = opendir(request->path);
+static evhtp_res serve_directory(evhtp_request_t *request, char *disk_path, struct stat *stat_buf) {
+ size_t disk_path_len = strlen(disk_path);
+ struct evbuffer *buf = request->buffer_out;
+ DIR *dir = opendir(disk_path);
if(dir == NULL) {
log_error("opendir() failed: %s", strerror(errno));
- return 500;
+ return EVHTP_RES_SERVERR;
}
struct dirent *entryp = malloc(offsetof(struct dirent, d_name) +
- pathconf(request->path, _PC_NAME_MAX) + 1);
+ pathconf(disk_path, _PC_NAME_MAX) + 1);
struct dirent *resultp = NULL;
if(entryp == NULL) {
log_error("malloc() failed while creating directory pointer: %s",
strerror(errno));
- return 500;
+ return EVHTP_RES_SERVERR;
}
+
+ struct json *json = new_json(json_buf_writer, buf);
+
struct stat file_stat_buf;
int entry_len;
- int first = 1;
+
+ json_start_object(json);
+
for(;;) {
readdir_r(dir, entryp, &resultp);
if(resultp == NULL) {
// skip.
continue;
}
- if(first) {
- evbuffer_add(buf, "{", 1);
- first = 0;
- } else {
- evbuffer_add(buf, ",", 1);
- }
entry_len = strlen(entryp->d_name);
- char full_path[request->path_len + entry_len + 1];
- sprintf(full_path, "%s%s", request->path, entryp->d_name);
+ char full_path[disk_path_len + entry_len + 1];
+ sprintf(full_path, "%s%s", disk_path, entryp->d_name);
stat(full_path, &file_stat_buf);
-
- char *escaped_name = escape_name(entryp->d_name);
- if(! escaped_name) {
- // failed to allocate name
- free(entryp);
- free(dir);
- return 500;
- }
- evbuffer_add_printf(buf, "\"%s%s\":%lld", escaped_name,
- S_ISDIR(file_stat_buf.st_mode) ? "/" : "",
- ((long long)file_stat_buf.st_mtime) * 1000);
- free(escaped_name);
- }
- if(first) {
- // empty directory.
- evbuffer_add(buf, "{", 1);
- }
- evbuffer_add(buf, "}", 1);
- struct rs_header type_header = {
- .key = "Content-Type",
- .value = "application/json",
- .next = NULL
- };
- struct rs_header etag_header = {
- .key = "ETag",
- .value = make_etag(stat_buf),
- .next = &type_header
- };
- if(etag_header.value == NULL) {
- log_error("make_etag() failed");
+
+ char key_string[entry_len + 2];
+ sprintf(key_string, "%s%s", entryp->d_name,
+ S_ISDIR(file_stat_buf.st_mode) ? "/": "");
+ char *val_string = get_etag(full_path);
+
+ json_write_key_val(json, key_string, val_string);
+
+ free(val_string);
+ }
+
+ json_end_object(json);
+
+ free_json(json);
+
+ char *etag = get_etag(disk_path);
+ if(etag == NULL) {
+ log_error("get_etag() failed");
free(entryp);
closedir(dir);
- return 500;
+ return EVHTP_RES_SERVERR;
}
- send_response_head(request, 200, &etag_header);
- free(etag_header.value);
- send_response_body(request, buf);
+
+ ADD_RESP_HEADER(request, "Content-Type", "application/json; charset=UTF-8");
+ ADD_RESP_HEADER_CP(request, "ETag", etag);
+
+ free(etag);
free(entryp);
closedir(dir);
- return 0;
+ return EVHTP_RES_OK;
}
-static char *get_xattr(const char *path, const char *key, int maxlen) {
- int len = 32;
- char *value = malloc(32);
- for(value = malloc(len);len<=maxlen;value = realloc(value, len+=16)) {
- if(value == NULL) {
- log_error("malloc() / realloc() failed: %s", strerror(errno));
- return NULL;
+static evhtp_res serve_file_head(evhtp_request_t *request, char *disk_path, struct stat *stat_buf, const char *mime_type) {
+
+ log_debug("serve file head");
+
+ if(request->uri->path->file == NULL) {
+ log_debug("HEAD dir requested");
+ // directory was requested
+ if(! S_ISDIR(stat_buf->st_mode)) {
+ log_debug("HEAD file found");
+ // but is actually a file
+ return EVHTP_RES_NOTFOUND;
}
- if(getxattr(path, key, value, len) > 0) {
- return value;
- } else {
- if(errno == ERANGE) {
- // buffer too small.
- continue;
- } else if(errno == ENOATTR) {
- // attribute not set
- free(value);
- return NULL;
- } else if(errno == ENOTSUP) {
- // xattr not supported
- log_error("File system doesn't support extended attributes! You may want to use another one.");
- free(value);
- return NULL;
- } else {
- log_error("Unexpected error while getting %s attribute: %s", key, strerror(errno));
- free(value);
- return NULL;
- }
+ log_debug("HEAD directory found");
+ } else {
+ log_debug("HEAD file requested");
+ // file was requested
+ if(S_ISDIR(stat_buf->st_mode)) {
+ log_debug("HEAD directory found");
+ // but is actually a directory
+ return EVHTP_RES_NOTFOUND;
}
+ log_debug("HEAD file found");
}
- log_error("%s attribute seems to be longer than %d bytes. That is simply unreasonable.", key, maxlen);
- free(value);
- return NULL;
-}
-
-static char *mime_type_from_xattr(const char *path) {
- char *mime_type = get_xattr(path, "user.mime_type", 128);
- if(mime_type == NULL) {
- return NULL;
+
+ char *etag_string = get_etag(disk_path);
+ if(etag_string == NULL) {
+ log_error("get_etag() failed");
+ return EVHTP_RES_SERVERR;
}
- char *charset = get_xattr(path, "user.charset", 64);
- if(charset == NULL) {
- return mime_type;
+
+ evhtp_header_t *if_none_match_header = evhtp_headers_find_header(request->headers_in, "If-None-Match");
+ if(if_none_match_header) {
+ // FIXME: support multiple comma-separated ETags in If-None-Match header
+ if(strcmp(if_none_match_header->val, etag_string) == 0) {
+ free(etag_string);
+ return EVHTP_RES_NOTMOD;
+ }
}
- int mt_len = strlen(mime_type);
- char *full_mime_type = realloc(mime_type, mt_len + strlen(charset) + 10 + 1);
- if(full_mime_type == NULL) {
- log_error("realloc() failed: %s", strerror(errno));
- free(charset);
- return mime_type;
+
+ char *length_string = malloc(24);
+ if(length_string == NULL) {
+ log_error("malloc() failed: %s", strerror(errno));
+ free(etag_string);
+ return EVHTP_RES_SERVERR;
}
- sprintf(full_mime_type + mt_len, "; charset=%s", charset);
- free(charset);
- return full_mime_type;
-}
+ snprintf(length_string, 24, "%ld", stat_buf->st_size);
-static int serve_file_head(struct rs_request *request, struct stat *stat_buf, const char *mime_type) {
int free_mime_type = 0;
// mime type is either passed in ... (such as for directory listings)
if(mime_type == NULL) {
// ... or detected based on xattr
- mime_type = mime_type_from_xattr(request->path);
+ mime_type = content_type_from_xattr(disk_path);
if(mime_type == NULL) {
// ... or guessed by libmagic
log_debug("mime type not given, detecting...");
- mime_type = magic_file(magic_cookie, request->path);
+ mime_type = magic_file(magic_cookie, disk_path);
if(mime_type == NULL) {
// ... or defaulted to "application/octet-stream"
log_error("magic failed: %s", magic_error(magic_cookie));
free_mime_type = 1;
}
}
- struct rs_header content_type_header = {
- .key = "Content-Type",
- // header won't be freed (it's completely stack based / constant),
- // so cast is valid here to satisfy type check.
- // (struct rs_header.value cannot be constant, because in the case of a request
- // header it will be free()'d at some point)
- .value = (char*)mime_type,
- .next = NULL
- };
- char *length_string = malloc(24);
- if(length_string == NULL) {
- log_error("malloc() failed: %s", strerror(errno));
- return 500;
- }
- snprintf(length_string, 24, "%ld", stat_buf->st_size);
- struct rs_header length_header = {
- .key = "Content-Length",
- .value = length_string,
- .next = &content_type_header
- };
- char *etag_string = make_etag(stat_buf);
- if(etag_string == NULL) {
- log_error("make_etag() failed");
- free(length_string);
- return 500;
- }
- struct rs_header etag_header = {
- .key = "ETag",
- .value = etag_string,
- .next = &length_header
- };
- send_response_head(request, 200, &etag_header);
+
+ log_info("setting Content-Type of %s: %s", request->uri->path->full, mime_type);
+ ADD_RESP_HEADER_CP(request, "Content-Type", mime_type);
+ ADD_RESP_HEADER_CP(request, "Content-Length", length_string);
+ ADD_RESP_HEADER_CP(request, "ETag", etag_string);
+
free(etag_string);
free(length_string);
if(free_mime_type) {
}
// serve a file body for the given request
-static int serve_file(struct rs_request *request, struct stat *stat_buf) {
- int fd = open(request->path, O_RDONLY | O_NONBLOCK);
+static evhtp_res serve_file(evhtp_request_t *request, const char *disk_path, struct stat *stat_buf) {
+ int fd = open(disk_path, O_RDONLY | O_NONBLOCK);
if(fd < 0) {
log_error("open() failed: %s", strerror(errno));
- return 500;
+ return EVHTP_RES_SERVERR;
}
- send_response_body_fd(request, fd);
- return 0;
+ while(evbuffer_read(request->buffer_out, fd, 4096) != 0);
+ close(fd);
+ return EVHTP_RES_OK;
+}
+
+static char *make_disk_path(char *dom_user, char *path, gss_buffer_t authuser, char **storage_root) {
+
+ // FIXME: use passwd->pwdir instead of /home/{user}/
+
+ // calculate maximum length of path
+ int pathlen = ( strlen(dom_user) + strlen(path) +
+ 6 + // "/home/"
+ 1 + // another slash
+ RS_HOME_SERVE_ROOT_LEN );
+ char *disk_path = malloc(pathlen + 1);
+ char *xsfile = NULL;
+ FILE *xsf;
+ char principal [1026];
+ bool authorized;
+ if(disk_path == NULL) {
+ log_error("malloc() failed: %s", strerror(errno));
+ return NULL;
+ }
+ log_debug("Constructing disk_path for dom_user = \"%s\"", dom_user);
+ xsfile = malloc( 7 + RS_HOME_SERVE_ROOT_LEN + strlen(dom_user) + 1 + 17);
+ if(xsfile == NULL) {
+ log_error("malloc() failed: %s", strerror(errno));
+ free(disk_path);
+ return NULL;
+ }
+ sprintf(xsfile, "/home/%s/%s/.k5remotestorage", dom_user, RS_HOME_SERVE_ROOT);
+ log_debug("Access control list = \"%s\"", xsfile);
+ xsf = fopen (xsfile, "r");
+ authorized = false;
+ if (xsf) {
+ while ((!authorized) && fgets (principal, sizeof (principal)-1, xsf)) {
+ int len = strlen (principal);
+ if ((len > 1) && (principal [len-1] == '\n')) {
+ principal [--len] = '\0';
+ }
+ log_debug("Considering acceptable principal \"%s\"", principal);
+ authorized = (len == authuser->length) && (0 == memcmp (principal, authuser->value, len));
+ }
+ fclose (xsf);
+ } else {
+ log_error ("Failed to open access control list");
+ free(xsfile);
+ free(disk_path);
+ return NULL;
+ }
+ if (!authorized) {
+ log_error ("Access control list does not contain authorized user");
+ free(xsfile);
+ free(disk_path);
+ return NULL;
+ }
+ log_debug ("xsfile = \"%s\"", xsfile);
+ if(storage_root) {
+ // Cut off .k5remotestorage and reuse for *storage_root
+ xsfile [7 + RS_HOME_SERVE_ROOT_LEN + strlen (dom_user)] = '\0';
+ *storage_root = xsfile;
+ log_debug ("storage_root = \"%s\"", storage_root);
+ } else {
+ free (xsfile);
+ xsfile = NULL;
+ }
+ // remove all /.. segments
+ // (we don't try to resolve them, but instead treat them as garbage)
+ char *pos = NULL;
+ while((pos = strstr(path, "/..")) != NULL) { // FIXME: this would also filter out valid paths like /foo/..bar
+ int restlen = strlen(pos + 3);
+ memmove(pos, pos + 3, restlen);
+ pos[restlen] = 0;
+ }
+ // remove all duplicate slashes (nasty things may be done with them at times)
+ while((pos = strstr(path, "//")) != NULL) {
+ int restlen = strlen(pos + 2);
+ memmove(pos, pos + 2, restlen);
+ pos[restlen] = 0;
+ }
+ // build path
+ sprintf(disk_path, "/home/%s/%s%s", dom_user, RS_HOME_SERVE_ROOT, path);
+ log_debug ("disk_path = \"%s\"", disk_path);
+ return disk_path;
}
-static int handle_get_or_head(struct rs_request *request, int include_body) {
+static evhtp_res handle_get_or_head(evhtp_request_t *request, gss_buffer_t authuser, int include_body) {
+
+ log_debug("HANDLE GET / HEAD (body: %s)", include_body ? "true" : "false");
+
+ char *disk_path = make_disk_path(REQUEST_GET_USER(request),
+ REQUEST_GET_PATH(request),
+ authuser,
+ NULL);
+ if(disk_path == NULL) {
+ return EVHTP_RES_SERVERR;
+ }
+
// stat
struct stat stat_buf;
- if(stat(request->path, &stat_buf) != 0) {
- if(errno != ENOENT) {
- log_error("stat() failed for path \"%s\": %s", request->path, strerror(errno));
- return 500;
+ if(stat(disk_path, &stat_buf) != 0) {
+ if(errno != ENOENT && errno != ENOTDIR) {
+ log_error("stat() failed for path \"%s\": %s", disk_path, strerror(errno));
+ return EVHTP_RES_SERVERR;
} else {
- return 404;
+ return EVHTP_RES_NOTFOUND;
}
}
// check for directory
- if(request->path[request->path_len - 1] == '/') {
+ if(request->uri->path->file == NULL) {
// directory requested
- if(! S_ISDIR(stat_buf.st_mode)) {
- // not a directory.
- return 404;
- }
- // directory found
if(include_body) {
- return serve_directory(request, &stat_buf);
+ return serve_directory(request, disk_path, &stat_buf);
} else {
- int head_result = serve_file_head(request, &stat_buf, "application/json");
- if(head_result != 0) {
- return head_result;
- }
- send_response_empty(request);
- return 0;
+ evhtp_res head_status = serve_file_head(request, disk_path, &stat_buf, "application/json");
+ return head_status != 0 ? head_status : EVHTP_RES_OK;
}
} else {
// file requested
- if(S_ISDIR(stat_buf.st_mode)) {
- // found, but is a directory
- return 404;
- }
- // file found
- int head_result = serve_file_head(request, &stat_buf, NULL);
+ evhtp_res head_result = serve_file_head(request, disk_path, &stat_buf, NULL);
if(head_result != 0) {
return head_result;
}
if(include_body) {
- return serve_file(request, &stat_buf);
+ return serve_file(request, disk_path, &stat_buf);
} else {
- send_response_empty(request);
- return 0;
+ return EVHTP_RES_OK;
}
}
}