Repository URL to install this package:
|
Version:
2.0.17 ▾
|
#include <uwsgi.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#if defined(__linux__) || defined(__APPLE__)
#include <sys/xattr.h>
#endif
/*
rfc4918 implementation (WebDAV)
requires libxml2
--webdav-mount [mountpoint=]<dir>
or
--webdav-use-docroot[=VAR]
steps to build a path:
1) get the mountpoint
2) concat the base with the path_info
3) realpath() on it
step 3 could be a non-existent file (for example on MKCOL or PUT). In such a case:
4) find the last / in the path_info, and try realpath() on it, if success the resource can be created
ALL MUST BE BOTH THREAD SAFE AND ASYNC SAFE !!!
Locking requires a cache (local or remote)
when a lock request is made, an item is added to the cache (directly using cache_set to avoid duplicates). The
item key is the full url of the request (host + path_info, in such a way we have virtualhosting for locks). The value is
a uuid.
if a lock_token is passed the url is checked in the cache and uuid compared
- Resource properties are stored as filesystem xattr (warning, not all operating system support them) -
*/
extern struct uwsgi_server uwsgi;
struct uwsgi_plugin webdav_plugin;
struct uwsgi_webdav {
struct uwsgi_string_list *mountpoints;
struct uwsgi_string_list *css;
struct uwsgi_string_list *javascript;
char *class_directory;
char *div;
char *lock_cache;
char *principal_base;
struct uwsgi_string_list *add_option;
struct uwsgi_string_list *add_prop;
struct uwsgi_string_list *add_collection_prop;
struct uwsgi_string_list *add_object_prop;
struct uwsgi_string_list *add_prop_href;
struct uwsgi_string_list *add_collection_prop_href;
struct uwsgi_string_list *add_object_prop_href;
struct uwsgi_string_list *add_prop_comp;
struct uwsgi_string_list *add_collection_prop_comp;
struct uwsgi_string_list *add_object_prop_comp;
struct uwsgi_string_list *add_rtype_prop;
struct uwsgi_string_list *add_rtype_collection_prop;
struct uwsgi_string_list *add_rtype_object_prop;
struct uwsgi_string_list *skip_prop;
} udav;
struct uwsgi_option uwsgi_webdav_options[] = {
{ "webdav-mount", required_argument, 0, "map a filesystem directory as a webdav store", uwsgi_opt_add_string_list, &udav.mountpoints, UWSGI_OPT_MIME},
{ "webdav-css", required_argument, 0, "add a css url for automatic webdav directory listing", uwsgi_opt_add_string_list, &udav.css, UWSGI_OPT_MIME},
{ "webdav-javascript", required_argument, 0, "add a javascript url for automatic webdav directory listing", uwsgi_opt_add_string_list, &udav.javascript, UWSGI_OPT_MIME},
{ "webdav-js", required_argument, 0, "add a javascript url for automatic webdav directory listing", uwsgi_opt_add_string_list, &udav.javascript, UWSGI_OPT_MIME},
{ "webdav-class-directory", required_argument, 0, "set the css directory class for automatic webdav directory listing", uwsgi_opt_set_str, &udav.class_directory, UWSGI_OPT_MIME},
{ "webdav-div", required_argument, 0, "set the div id for automatic webdav directory listing", uwsgi_opt_set_str, &udav.div, UWSGI_OPT_MIME},
{ "webdav-lock-cache", required_argument, 0, "set the cache to use for webdav locking", uwsgi_opt_set_str, &udav.lock_cache, UWSGI_OPT_MIME},
{ "webdav-principal-base", required_argument, 0, "enable WebDAV Current Principal Extension using the specified base", uwsgi_opt_set_str, &udav.principal_base, UWSGI_OPT_MIME},
{ "webdav-add-option", required_argument, 0, "add a WebDAV standard to the OPTIONS response", uwsgi_opt_add_string_list, &udav.add_option, UWSGI_OPT_MIME},
{ "webdav-add-prop", required_argument, 0, "add a WebDAV property to all resources", uwsgi_opt_add_string_list, &udav.add_prop, UWSGI_OPT_MIME},
{ "webdav-add-collection-prop", required_argument, 0, "add a WebDAV property to all collections", uwsgi_opt_add_string_list, &udav.add_collection_prop, UWSGI_OPT_MIME},
{ "webdav-add-object-prop", required_argument, 0, "add a WebDAV property to all objects", uwsgi_opt_add_string_list, &udav.add_object_prop, UWSGI_OPT_MIME},
{ "webdav-add-prop-href", required_argument, 0, "add a WebDAV property to all resources (href value)", uwsgi_opt_add_string_list, &udav.add_prop_href, UWSGI_OPT_MIME},
{ "webdav-add-collection-prop-href", required_argument, 0, "add a WebDAV property to all collections (href value)", uwsgi_opt_add_string_list, &udav.add_collection_prop_href, UWSGI_OPT_MIME},
{ "webdav-add-object-prop-href", required_argument, 0, "add a WebDAV property to all objects (href value)", uwsgi_opt_add_string_list, &udav.add_object_prop_href, UWSGI_OPT_MIME},
{ "webdav-add-prop-comp", required_argument, 0, "add a WebDAV property to all resources (xml value)", uwsgi_opt_add_string_list, &udav.add_prop_comp, UWSGI_OPT_MIME},
{ "webdav-add-collection-prop-comp", required_argument, 0, "add a WebDAV property to all collections (xml value)", uwsgi_opt_add_string_list, &udav.add_collection_prop_comp, UWSGI_OPT_MIME},
{ "webdav-add-object-prop-comp", required_argument, 0, "add a WebDAV property to all objects (xml value)", uwsgi_opt_add_string_list, &udav.add_object_prop_comp, UWSGI_OPT_MIME},
{ "webdav-add-rtype-prop", required_argument, 0, "add a WebDAV resourcetype property to all resources", uwsgi_opt_add_string_list, &udav.add_rtype_prop, UWSGI_OPT_MIME},
{ "webdav-add-rtype-collection-prop", required_argument, 0, "add a WebDAV resourcetype property to all collections", uwsgi_opt_add_string_list, &udav.add_rtype_collection_prop, UWSGI_OPT_MIME},
{ "webdav-add-rtype-object-prop", required_argument, 0, "add a WebDAV resourcetype property to all objects", uwsgi_opt_add_string_list, &udav.add_rtype_object_prop, UWSGI_OPT_MIME},
{ "webdav-skip-prop", required_argument, 0, "do not add the specified prop if available in resource xattr", uwsgi_opt_add_string_list, &udav.skip_prop, UWSGI_OPT_MIME},
{ 0, 0, 0, 0, 0, 0, 0 },
};
static int uwsgi_webdav_prop_requested(xmlNode *req_prop, char *ns, char *name) {
if (!req_prop) return 1;
xmlNode *node;
for (node = req_prop->children; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE) {
if (ns) {
if (node->ns && !strcmp((char *) node->ns->href, ns)) {
if (!strcmp((char *) node->name, name)) return 1;
}
}
else {
if (!strcmp((char *) node->name, name)) return 1;
}
}
}
return 0;
}
static void uwsgi_webdav_add_a_prop(xmlNode *node, char *opt, xmlNode *req_prop, int type, char *force_name) {
char *first_space = strchr(opt, ' ');
if (!first_space) return;
*first_space = 0;
char *second_space = strchr(first_space + 1, ' ');
xmlNode *new_node = NULL;
char *ns = opt;
if (!force_name) force_name = first_space + 1;
else {
ns = "DAV:";
}
if (second_space) {
*second_space = 0;
if (!uwsgi_webdav_prop_requested(req_prop, ns, force_name)) {
*first_space = ' ';
*second_space = ' ';
return;
}
// href
if (type == 1) {
new_node = xmlNewChild(node, NULL, BAD_CAST first_space + 1, NULL);
xmlNewTextChild(new_node, NULL, BAD_CAST "href", BAD_CAST second_space + 1);
}
// comp
else if (type == 2) {
new_node = xmlNewChild(node, NULL, BAD_CAST first_space + 1, NULL);
char *comps = uwsgi_str(second_space + 1);
char *p, *ctx = NULL;
uwsgi_foreach_token(comps, ",", p, ctx) {
xmlNode *comp = xmlNewChild(new_node, NULL, BAD_CAST "comp", NULL);
xmlNewProp(comp, BAD_CAST "name", BAD_CAST p);
}
free(comps);
}
else {
if (!uwsgi_webdav_prop_requested(req_prop, ns, first_space + 1)) {
*first_space = ' ';
*second_space = ' ';
return;
}
new_node = xmlNewTextChild(node, NULL, BAD_CAST first_space + 1, BAD_CAST second_space + 1);
}
*second_space = ' ';
}
else {
if (!uwsgi_webdav_prop_requested(req_prop, ns, force_name)) {
*first_space = ' ';
return;
}
new_node = xmlNewChild(node, NULL, BAD_CAST first_space + 1, NULL);
}
xmlNsPtr x_ns = xmlNewNs(new_node, BAD_CAST opt, NULL);
xmlSetNs(new_node, x_ns);
*first_space = ' ';
}
static void uwsgi_webdav_foreach_prop(struct uwsgi_string_list *usl, xmlNode *req_prop, xmlNode *node, int type, char *force_name) {
if (!usl) return;
while(usl) {
uwsgi_webdav_add_a_prop(node, usl->value, req_prop, type, force_name);
usl = usl->next;
}
}
/*
OPTIONS: if it is a valid webdav resource add Dav: to the response header
*/
static int uwsgi_wevdav_manage_options(struct wsgi_request *wsgi_req) {
uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6);
if (udav.add_option) {
struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size);
if (uwsgi_buffer_append(ub, "1, 2, 3", 7)) goto end;
struct uwsgi_string_list *usl = udav.add_option;
while(usl) {
if (uwsgi_buffer_append(ub, ", ", 2)) goto end;
if (uwsgi_buffer_append(ub, usl->value, usl->len)) goto end;
usl = usl->next;
}
uwsgi_response_add_header(wsgi_req, "Dav", 3, ub->buf, ub->pos);
end:
uwsgi_buffer_destroy(ub);
}
else {
uwsgi_response_add_header(wsgi_req, "Dav", 3, "1, 2, 3", 7);
}
return UWSGI_OK;
}
static char *uwsgi_webdav_new_date(uint64_t t) {
// 30+1
char d[31];
int len = uwsgi_http_date((time_t) t, d);
if (!len) {
return NULL;
}
return uwsgi_concat2n(d, len, "", 0);
}
static int uwsgi_webdav_add_props(struct wsgi_request *wsgi_req, xmlNode *req_prop, xmlNode * multistatus, xmlNsPtr dav_ns, char *uri, char *filename, int with_values) {
struct stat st;
if (stat(filename, &st)) {
uwsgi_error("uwsgi_webdav_add_props()/stat()");
return -1;
}
int is_collection = 0;
xmlNode *response = xmlNewChild(multistatus, dav_ns, BAD_CAST "response", NULL);
uint16_t uri_len = strlen(uri) ;
char *encoded_uri = uwsgi_malloc( (uri_len * 3) + 1);
http_url_encode(uri, &uri_len, encoded_uri);
encoded_uri[uri_len] = 0;
xmlNewChild(response, dav_ns, BAD_CAST "href", BAD_CAST encoded_uri);
free(encoded_uri);
xmlNode *r_propstat = xmlNewChild(response, dav_ns, BAD_CAST "propstat", NULL);
char *r_status = uwsgi_concat2n(wsgi_req->protocol, wsgi_req->protocol_len, " 200 OK", 7);
xmlNewChild(r_propstat, dav_ns, BAD_CAST "status", BAD_CAST r_status);
free(r_status);
xmlNode *r_prop = xmlNewChild(r_propstat, dav_ns, BAD_CAST "prop", NULL);
if (with_values) {
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "displayname")) {
char *base_uri = uwsgi_get_last_char(uri, '/');
if (base_uri) {
xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", BAD_CAST base_uri+1);
}
else {
xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", BAD_CAST uri);
}
}
if (S_ISDIR(st.st_mode)) is_collection = 1;
xmlNode *r_type = NULL;
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "resourcetype")) {
r_type = xmlNewChild(r_prop, dav_ns, BAD_CAST "resourcetype", NULL);
if (is_collection) {
xmlNewChild(r_type, dav_ns, BAD_CAST "collection", NULL);
is_collection = 1;
}
}
if (!is_collection) {
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getcontentlength")) {
char *r_contentlength = uwsgi_num2str(st.st_size);
xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontentlength", BAD_CAST r_contentlength);
free(r_contentlength);
}
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getcontenttype")) {
size_t mime_type_len = 0;
char *mime_type = uwsgi_get_mime_type(filename, strlen(filename), &mime_type_len);
if (mime_type) {
char *r_ctype = uwsgi_concat2n(mime_type, mime_type_len, "", 0);
xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getcontenttype", BAD_CAST r_ctype);
free(r_ctype);
}
}
}
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "creationdate")) {
// there is no creation date on UNIX/POSIX, ctime is the nearest thing...
char *cdate = uwsgi_webdav_new_date(st.st_ctime);
if (cdate) {
xmlNewTextChild(r_prop, dav_ns, BAD_CAST "creationdate", BAD_CAST cdate);
free(cdate);
}
}
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getlastmodified")) {
char *mdate = uwsgi_webdav_new_date(st.st_mtime);
if (mdate) {
xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getlastmodified", BAD_CAST mdate);
free(mdate);
}
}
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getetag")) {
char *etag = uwsgi_num2str(st.st_mtime);
xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getetag", BAD_CAST etag);
free(etag);
}
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "executable")) {
xmlNewChild(r_prop, dav_ns, BAD_CAST "executable", NULL);
}
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "owner")) {
xmlNewTextChild(r_prop, dav_ns, BAD_CAST "owner", NULL);
}
if (wsgi_req->remote_user_len > 0) {
if (udav.principal_base) {
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "current-user-principal")) {
char *current_user_principal = uwsgi_concat2n(udav.principal_base, strlen(udav.principal_base), wsgi_req->remote_user, wsgi_req->remote_user_len);
xmlNode *cup = xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-principal", NULL);
xmlNewTextChild(cup, dav_ns, BAD_CAST "href", BAD_CAST current_user_principal);
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "resourcetype")) {
if (!strcmp(current_user_principal, uri)) {
xmlNewChild(r_type, dav_ns, BAD_CAST "principal", NULL);
}
}
free(current_user_principal);
}
}
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "current-user-privilege-set")) {
xmlNode *cups = xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-privilege-set", NULL);
xmlNode *privilege = xmlNewChild(cups, dav_ns, BAD_CAST "privilege", NULL);
xmlNewChild(privilege, dav_ns, BAD_CAST "all", NULL);
xmlNewChild(privilege, dav_ns, BAD_CAST "read", NULL);
xmlNewChild(privilege, dav_ns, BAD_CAST "write", NULL);
xmlNewChild(privilege, dav_ns, BAD_CAST "write-content", NULL);
xmlNewChild(privilege, dav_ns, BAD_CAST "write-properties", NULL);
}
}
if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "supported-report-set")) {
xmlNode *report_set = xmlNewChild(r_prop, dav_ns, BAD_CAST "supported-report-set", NULL);
xmlNode *supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL);
xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "principal-property-search");
supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL);
xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "sync-collection");
supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL);
xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "expand-property");
supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL);
xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "principal-search-property-set");
}
uwsgi_webdav_foreach_prop(udav.add_prop, req_prop, r_prop, 0, NULL );
uwsgi_webdav_foreach_prop(udav.add_prop_href, req_prop, r_prop, 1, NULL);
uwsgi_webdav_foreach_prop(udav.add_prop_comp,req_prop, r_prop, 2 , NULL);
uwsgi_webdav_foreach_prop(udav.add_rtype_prop,req_prop, r_type, 0, "resourcetype");
if (is_collection) {
uwsgi_webdav_foreach_prop(udav.add_rtype_collection_prop,req_prop, r_type, 0, "resourcetype");
uwsgi_webdav_foreach_prop(udav.add_collection_prop,req_prop, r_prop, 0, NULL);
uwsgi_webdav_foreach_prop(udav.add_collection_prop_href,req_prop, r_prop, 1, NULL);
uwsgi_webdav_foreach_prop(udav.add_collection_prop_comp,req_prop, r_prop, 2, NULL);
}
else {
uwsgi_webdav_foreach_prop(udav.add_rtype_object_prop,req_prop, r_type, 0, "resourcetype");
uwsgi_webdav_foreach_prop(udav.add_object_prop,req_prop, r_prop, 0, NULL);
uwsgi_webdav_foreach_prop(udav.add_object_prop_href,req_prop, r_prop, 1, NULL);
uwsgi_webdav_foreach_prop(udav.add_object_prop_comp,req_prop, r_prop, 2, NULL);
}
}
else {
xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", NULL);
xmlNewChild(r_prop, dav_ns, BAD_CAST "resourcetype", NULL);
if (!S_ISDIR(st.st_mode)) {
xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontentlength", NULL);
xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontenttype", NULL);
}
xmlNewChild(r_prop, dav_ns, BAD_CAST "creationdate", NULL);
xmlNewChild(r_prop, dav_ns, BAD_CAST "getlastmodified", NULL);
xmlNewChild(r_prop, dav_ns, BAD_CAST "supported-report-set", NULL);
if (wsgi_req->remote_user_len > 0) {
xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-privilege-set", NULL);
if (udav.principal_base) {
xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-principal", NULL);
}
}
}
#if defined(__linux__) || defined(__APPLE__)
// get xattr for user.uwsgi.webdav.
#if defined(__linux__)
ssize_t rlen = listxattr(filename, NULL, 0);
#elif defined(__APPLE__)
ssize_t rlen = listxattr(filename, NULL, 0, 0);
#endif
// do not return -1 as the previous xml is valid !!!
if (rlen <= 0) return 0;
// use calloc to avoid races
char *xattrs = uwsgi_calloc(rlen);
#if defined(__linux__)
if (listxattr(filename, xattrs, rlen) <= 0) {
#elif defined(__APPLE__)
if (listxattr(filename, xattrs, rlen, 0) <= 0) {
#endif
free(xattrs);
return 0;
}
// parse the name list
ssize_t i;
char *key = NULL;
for(i=0;i<rlen;i++) {
// check for wrong condition
if (xattrs[i] == 0 && key == NULL) break;
if (key && xattrs[i] == 0) {
if (!uwsgi_starts_with(key, strlen(key), "user.uwsgi.webdav.", 18)) {
if (uwsgi_string_list_has_item(udav.skip_prop, key + 18, strlen(key + 18))) continue;
xmlNsPtr xattr_ns = NULL;
// does it has a namespace ?
char *separator = strchr(key + 18, '|');
char *xattr_key = key + 18;
if (separator) {
xattr_key = separator + 1;
*separator = 0;
if (!uwsgi_webdav_prop_requested(req_prop, key + 18, xattr_key)) continue;
}
else {
if (!uwsgi_webdav_prop_requested(req_prop, NULL, xattr_key)) continue;
}
xmlNode *xattr_item = NULL;
if (with_values) {
#if defined(__linux__)
ssize_t rlen2 = getxattr(filename, key, NULL, 0);
#elif defined(__APPLE__)
ssize_t rlen2 = getxattr(filename, key, NULL, 0, 0, 0);
#endif
if (rlen > 0) {
// leave space for final 0
char *xvalue = uwsgi_calloc(rlen2 + 1);
#if defined(__linux__)
if (getxattr(filename, key, xvalue, rlen2) > 0) {
#elif defined(__APPLE__)
if (getxattr(filename, key, xvalue, rlen2, 0 ,0) > 0) {
#endif
xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, BAD_CAST xvalue);
}
free(xvalue);
}
else if (rlen == 0) {
xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, NULL);
}
}
else {
xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, NULL);
}
if (separator && xattr_item) {
xattr_ns = xmlNewNs(xattr_item, BAD_CAST (key + 18), NULL);
*separator = '|';
xmlSetNs(xattr_item, xattr_ns);
}
}
key = NULL;
}
else if (key == NULL) {
key = &xattrs[i];
}
}
free(xattrs);
#endif
return 0;
}
static size_t uwsgi_webdav_expand_path(struct wsgi_request *wsgi_req, char *item, uint16_t item_len, char *filename) {
struct uwsgi_app *ua = &uwsgi_apps[wsgi_req->app_id];
char *docroot = ua->interpreter;
size_t docroot_len = strlen(docroot);
// merge docroot with path_info
char *tmp_filename = uwsgi_concat3n(docroot, docroot_len, "/", 1, item, item_len);
// try expanding the path
if (!realpath(tmp_filename, filename)) {
free(tmp_filename);
return 0;
}
free(tmp_filename);
return strlen(filename);
}
static size_t uwsgi_webdav_expand_fake_path(struct wsgi_request *wsgi_req, char *item, uint16_t item_len, char *filename) {
char *last_slash = uwsgi_get_last_charn(item, item_len, '/');
if (!last_slash) return 0;
size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, item, last_slash - item, filename);
if (!filename_len) return 0;
// check for overflow
if (filename_len + (item_len - (last_slash - item)) >= PATH_MAX) return 0;
memcpy(filename + filename_len, last_slash, (item_len - (last_slash - item)));
filename_len += (item_len - (last_slash - item));
filename[(int)filename_len] = 0;
return filename_len;
}
static xmlDoc *uwsgi_webdav_manage_prop(struct wsgi_request *wsgi_req, xmlNode *req_prop, char *filename, size_t filename_len, int with_values) {
// default 1 depth
int depth = 1;
uint16_t http_depth_len = 0;
char *http_depth = uwsgi_get_var(wsgi_req, "HTTP_DEPTH", 10, &http_depth_len);
if (http_depth) {
depth = uwsgi_str_num(http_depth, http_depth_len);
}
xmlDoc *rdoc = xmlNewDoc(BAD_CAST "1.0");
xmlNode *multistatus = xmlNewNode(NULL, BAD_CAST "multistatus");
xmlDocSetRootElement(rdoc, multistatus);
xmlNsPtr dav_ns = xmlNewNs(multistatus, BAD_CAST "DAV:", BAD_CAST "D");
xmlSetNs(multistatus, dav_ns);
if (depth == 0) {
char *uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, "", 0);
uwsgi_webdav_add_props(wsgi_req, req_prop, multistatus, dav_ns, uri, filename, with_values);
free(uri);
}
else {
DIR *collection = opendir(filename);
struct dirent de;
for (;;) {
struct dirent *de_r = NULL;
if (readdir_r(collection, &de, &de_r)) {
uwsgi_error("uwsgi_wevdav_manage_propfind()/readdir_r()");
break;
}
if (de_r == NULL) {
break;
}
char *uri = NULL;
char *direntry = NULL;
if (!strcmp(de.d_name, "..")) {
// skip ..
continue;
}
else if (!strcmp(de.d_name, ".")) {
uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, "", 0);
direntry = uwsgi_concat2n(filename, filename_len, "", 0);
}
else if (wsgi_req->path_info[wsgi_req->path_info_len - 1] == '/') {
uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, de.d_name, strlen(de.d_name));
direntry = uwsgi_concat3n(filename, filename_len, "/", 1, de.d_name, strlen(de.d_name));
}
else {
uri = uwsgi_concat3n(wsgi_req->path_info, wsgi_req->path_info_len, "/", 1, de.d_name, strlen(de.d_name));
direntry = uwsgi_concat3n(filename, filename_len, "/", 1, de.d_name, strlen(de.d_name));
}
uwsgi_webdav_add_props(wsgi_req, req_prop, multistatus, dav_ns, uri, direntry, with_values);
free(uri);
free(direntry);
}
closedir(collection);
}
return rdoc;
}
static int uwsgi_wevdav_manage_propfind(struct wsgi_request *wsgi_req, xmlDoc * doc) {
char filename[PATH_MAX];
size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
if (filename_len == 0) {
uwsgi_404(wsgi_req);
return UWSGI_OK;
}
xmlDoc *rdoc = NULL;
xmlNode *element = NULL;
if (doc) {
element = xmlDocGetRootElement(doc);
if (!element) return -1;
if (!element || strcmp((char *) element->name, "propfind")) return -1;
}
if (uwsgi_response_prepare_headers(wsgi_req, "207 Multi-Status", 16))
return -1;
if (uwsgi_response_add_content_type(wsgi_req, "application/xml; charset=\"utf-8\"", 32))
return -1;
if (doc) {
// propfind must have a child (scan them until you find a valid one)
xmlNode *node;
for (node = element->children; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE) {
if (node->ns && !strcmp((char *) node->ns->href, "DAV:")) {
if (!strcmp((char *) node->name, "prop")) {
rdoc = uwsgi_webdav_manage_prop(wsgi_req, node, filename, filename_len, 1);
break;
}
if (!strcmp((char *) node->name, "allprop")) {
rdoc = uwsgi_webdav_manage_prop(wsgi_req, NULL, filename, filename_len, 1);
break;
}
if (!strcmp((char *) node->name, "propname")) {
rdoc = uwsgi_webdav_manage_prop(wsgi_req, node, filename, filename_len, 0);
break;
}
}
}
}
}
else {
rdoc = uwsgi_webdav_manage_prop(wsgi_req, NULL, filename, filename_len, 1);
}
if (!rdoc) return UWSGI_OK;
xmlChar *xmlbuf;
int xlen = 0;
xmlDocDumpFormatMemory(rdoc, &xmlbuf, &xlen, 1);
uwsgi_response_add_content_length(wsgi_req, xlen);
uwsgi_response_write_body_do(wsgi_req, (char *) xmlbuf, xlen);
#ifdef UWSGI_DEBUG
uwsgi_log("\n%.*s\n", xlen, xmlbuf);
#endif
xmlFreeDoc(rdoc);
xmlFree(xmlbuf);
return UWSGI_OK;
}
static int uwsgi_webdav_prop_set(char *filename, char *attr, char *ns, char *body) {
int ret = 0;
#if defined(__linux__) || defined(__APPLE__)
char *xattr_name = NULL;
if (ns) {
xattr_name = uwsgi_concat4("user.uwsgi.webdav.", ns, "|", attr);
}
else {
xattr_name = uwsgi_concat2("user.uwsgi.webdav.", attr);
}
#if defined(__linux__)
ret = setxattr(filename, xattr_name, body, strlen(body), 0);
#elif defined(__APPLE__)
ret = setxattr(filename, xattr_name, body, strlen(body), 0, 0);
#endif
free(xattr_name);
#endif
return ret;
}
static int uwsgi_webdav_prop_del(char *filename, char *attr, char *ns) {
int ret = 0;
#if defined(__linux__) || defined(__APPLE__)
char *xattr_name = NULL;
if (ns) {
xattr_name = uwsgi_concat4("user.uwsgi.webdav.", ns, "|", attr);
}
else {
xattr_name = uwsgi_concat2("user.uwsgi.webdav.", attr);
}
#if defined(__linux__)
ret = removexattr(filename, xattr_name);
#elif defined(__APPLE__)
ret = removexattr(filename, xattr_name, 0);
#endif
free(xattr_name);
#endif
return ret;
}
static void uwsgi_webdav_do_prop_update(struct wsgi_request *wsgi_req, xmlNode *prop, xmlNode *response, char *filename, uint8_t action) {
xmlNode *node;
// search for "prop"
for (node = prop->children; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE) {
xmlNode *propstat = xmlNewChild(response, NULL, BAD_CAST "propstat", NULL);
xmlNode *r_prop = xmlNewChild(propstat, NULL, BAD_CAST "prop" , NULL);
xmlNode *new_prop = xmlNewChild(r_prop, NULL, node->name, NULL);
if (node->ns) {
xmlNsPtr xattr_ns = xmlNewNs(new_prop, node->ns->href, NULL);
xmlSetNs(new_prop, xattr_ns);
}
if (action == 0) {
if (uwsgi_webdav_prop_set(filename, (char *) node->name, node->ns ? (char *) node->ns->href : NULL, node->children ? (char *) node->children->content : "")) {
char *r_status = uwsgi_concat2n(wsgi_req->protocol, wsgi_req->protocol_len, " 403 Forbidden", 14);
xmlNewChild(r_prop, NULL, BAD_CAST "status", BAD_CAST r_status);
free(r_status);
}
else {
char *r_status = uwsgi_concat2n(wsgi_req->protocol, wsgi_req->protocol_len, " 200 OK", 7);
xmlNewChild(r_prop, NULL, BAD_CAST "status", BAD_CAST r_status);
free(r_status);
}
}
else if (action == 1) {
if (uwsgi_webdav_prop_del(filename, (char *) node->name, node->ns ? (char *) node->ns->href : NULL)) {
char *r_status = uwsgi_concat2n(wsgi_req->protocol, wsgi_req->protocol_len, " 403 Forbidden", 14);
xmlNewChild(r_prop, NULL, BAD_CAST "status", BAD_CAST r_status);
free(r_status);
}
else {
char *r_status = uwsgi_concat2n(wsgi_req->protocol, wsgi_req->protocol_len, " 200 OK", 7);
xmlNewChild(r_prop, NULL, BAD_CAST "status", BAD_CAST r_status);
free(r_status);
}
}
}
}
}
// action 0 is set, 1 is remove
static void uwsgi_webdav_manage_prop_update(struct wsgi_request *wsgi_req, xmlNode *parent, xmlNode *response, char *filename, uint8_t action) {
xmlNode *node;
// search for "prop"
for (node = parent->children; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE) {
if (node->ns && !strcmp((char *) node->ns->href, "DAV:")) {
if (!strcmp((char *) node->name, "prop")) {
uwsgi_webdav_do_prop_update(wsgi_req, node, response, filename, action);
}
}
}
}
}
static int uwsgi_wevdav_manage_proppatch(struct wsgi_request *wsgi_req, xmlDoc * doc) {
char filename[PATH_MAX];
size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
if (filename_len == 0) {
uwsgi_404(wsgi_req);
return UWSGI_OK;
}
xmlNode *element = xmlDocGetRootElement(doc);
if (!element) return -1;
if (!element || (strcmp((char *) element->name, "propertyupdate"))) return -1;
if (uwsgi_response_prepare_headers(wsgi_req, "207 Multi-Status", 16))
return -1;
if (uwsgi_response_add_content_type(wsgi_req, "application/xml; charset=\"utf-8\"", 32))
return -1;
xmlDoc *rdoc = xmlNewDoc(BAD_CAST "1.0");
xmlNode *multistatus = xmlNewNode(NULL, BAD_CAST "multistatus");
xmlDocSetRootElement(rdoc, multistatus);
xmlNsPtr dav_ns = xmlNewNs(multistatus, BAD_CAST "DAV:", BAD_CAST "D");
xmlSetNs(multistatus, dav_ns);
xmlNode *response = xmlNewChild(multistatus, dav_ns, BAD_CAST "response", NULL);
char *uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, "", 0);
uint16_t uri_len = strlen(uri) ;
char *encoded_uri = uwsgi_malloc( (uri_len * 3) + 1);
http_url_encode(uri, &uri_len, encoded_uri);
encoded_uri[uri_len] = 0;
xmlNewChild(response, dav_ns, BAD_CAST "href", BAD_CAST encoded_uri);
free(encoded_uri);
// propfind can be "set" or "remove"
xmlNode *node;
for (node = element->children; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE) {
if (node->ns && !strcmp((char *) node->ns->href, "DAV:")) {
if (!strcmp((char *) node->name, "set")) {
uwsgi_webdav_manage_prop_update(wsgi_req, node, response, filename, 0);
}
else if (!strcmp((char *) node->name, "remove")) {
uwsgi_webdav_manage_prop_update(wsgi_req, node, response, filename, 1);
}
}
}
}
if (!rdoc) return UWSGI_OK;
xmlChar *xmlbuf;
int xlen = 0;
xmlDocDumpFormatMemory(rdoc, &xmlbuf, &xlen, 1);
uwsgi_response_add_content_length(wsgi_req, xlen);
uwsgi_response_write_body_do(wsgi_req, (char *) xmlbuf, xlen);
#ifdef UWSGI_DEBUG
uwsgi_log("\n%.*s\n", xlen, xmlbuf);
#endif
xmlFreeDoc(rdoc);
xmlFree(xmlbuf);
return UWSGI_OK;
}
static int uwsgi_wevdav_manage_put(struct wsgi_request *wsgi_req) {
char filename[PATH_MAX];
size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
// the collection does not exist, search for the last /
if (!filename_len) {
filename_len = uwsgi_webdav_expand_fake_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
if (!filename_len) {
uwsgi_response_prepare_headers(wsgi_req, "409 Conflict", 12);
return UWSGI_OK;
}
}
int fd = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
uwsgi_403(wsgi_req);
return UWSGI_OK;
}
if (uwsgi_response_prepare_headers(wsgi_req, "201 Created", 11)) goto end;
size_t remains = wsgi_req->post_cl;
while(remains > 0) {
ssize_t body_len = 0;
char *body = uwsgi_request_body_read(wsgi_req, UMIN(remains, 32768) , &body_len);
if (!body || body == uwsgi.empty) break;
if (write(fd, body, body_len) != body_len) goto end;
}
end:
close(fd);
return UWSGI_OK;
}
static int uwsgi_webdav_massive_delete(char *dir) {
int ret = 0;
DIR *d = opendir(dir);
for (;;) {
struct dirent *de_r = NULL;
struct dirent de;
if (readdir_r(d, &de, &de_r)) {
ret = -1;
goto end;
}
if (de_r == NULL) break;
// skip myself and parent
if (!strcmp(de.d_name, ".") || !strcmp(de.d_name, "..")) continue;
char *item = uwsgi_concat3(dir, "/", de.d_name);
if (de.d_type == DT_DIR) {
if (uwsgi_webdav_massive_delete(item)) {
free(item);
ret = -1;
goto end;
}
}
else {
if (unlink(item)) {
free(item);
ret = -1;
goto end;
}
}
free(item);
}
if (rmdir(dir)) ret = -1;
end:
closedir(d);
return ret;
}
static int uwsgi_wevdav_manage_delete(struct wsgi_request *wsgi_req) {
char filename[PATH_MAX];
size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
// the collection does not exists
if (!filename_len) {
uwsgi_404(wsgi_req);
return UWSGI_OK;
}
if (uwsgi_is_dir(filename)) {
int ret = rmdir(filename);
if (ret < 0) {
if (errno == ENOTEMPTY) {
if (uwsgi_webdav_massive_delete(filename)) {
uwsgi_403(wsgi_req);
return UWSGI_OK;
}
}
else {
uwsgi_403(wsgi_req);
return UWSGI_OK;
}
}
}
else {
if (unlink(filename)) {
uwsgi_403(wsgi_req);
return UWSGI_OK;
}
}
uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6);
return UWSGI_OK;
}
static int uwsgi_webdav_dirlist_add_item(struct uwsgi_buffer *ub, char *item, size_t item_len, uint8_t is_dir) {
if (is_dir) {
if (udav.class_directory) {
if (uwsgi_buffer_append(ub, "<li class=\"", 11)) return -1;
if (uwsgi_buffer_append(ub, udav.class_directory, strlen(udav.class_directory))) return -1;
if (uwsgi_buffer_append(ub, "\"><a href=\"", 11)) return -1;
}
else {
if (uwsgi_buffer_append(ub, "<li class=\"directory\"><a href=\"", 31)) return -1;
}
}
else {
if (uwsgi_buffer_append(ub, "<li><a href=\"", 13)) return -1;
}
if (uwsgi_buffer_append(ub, item, item_len)) return -1;
if (is_dir) {
if (uwsgi_buffer_append(ub, "/\">", 3)) return -1;
if (uwsgi_buffer_append(ub, item, item_len)) return -1;
if (uwsgi_buffer_append(ub, "/", 1)) return -1;
}
else {
if (uwsgi_buffer_append(ub, "\">", 2)) return -1;
if (uwsgi_buffer_append(ub, item, item_len)) return -1;
}
if (uwsgi_buffer_append(ub, "</a></li>", 9)) return -1;
return 0;
}
static void uwsgi_webdav_dirlist(struct wsgi_request *wsgi_req, char *dir) {
struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size);
if (uwsgi_buffer_append(ub, "<html><head><title>", 19)) goto end;
if (uwsgi_buffer_append(ub, dir, strlen(dir))) goto end;
if (uwsgi_buffer_append(ub, "</title>", 8)) goto end;
struct uwsgi_string_list *usl = udav.css;
while(usl) {
if (uwsgi_buffer_append(ub, "<link rel=\"stylesheet\" href=\"", 29)) goto end;
if (uwsgi_buffer_append(ub, usl->value, usl->len)) goto end;
if (uwsgi_buffer_append(ub, "\" type=\"text/css\">", 18)) goto end;
usl = usl->next;
}
usl = udav.javascript;
while(usl) {
if (uwsgi_buffer_append(ub, "<script src=\"", 13)) goto end;
if (uwsgi_buffer_append(ub, usl->value, usl->len)) goto end;
if (uwsgi_buffer_append(ub, "\"></script>", 11)) goto end;
usl = usl->next;
}
if (uwsgi_buffer_append(ub, "</head><body>", 13)) goto end;
if (udav.div) {
if (uwsgi_buffer_append(ub, "<div id=\"", 9)) goto end;
if (uwsgi_buffer_append(ub, udav.div, strlen(udav.div))) goto end;
if (uwsgi_buffer_append(ub, "\">", 2)) goto end;
}
else {
if (uwsgi_buffer_append(ub, "<div>", 5)) goto end;
}
if (uwsgi_webdav_dirlist_add_item(ub, "..", 2, 1)) goto end;
#ifdef __linux__
struct dirent **tasklist;
int n = scandir(dir, &tasklist, 0, versionsort);
if (n < 0) goto end;
int i;
for(i=0;i<n;i++) {
if (tasklist[i]->d_name[0] == '.') goto next;
if (uwsgi_webdav_dirlist_add_item(ub, tasklist[i]->d_name, strlen(tasklist[i]->d_name), tasklist[i]->d_type == DT_DIR ? 1 : 0)) {
free(tasklist[i]);
free(tasklist);
goto end;
}
next:
free(tasklist[i]);
}
free(tasklist);
#else
DIR *d = opendir(dir);
for (;;) {
struct dirent *de_r = NULL;
struct dirent de;
if (readdir_r(d, &de, &de_r)) goto end;
if (de_r == NULL) break;
// skip items startign with a dot
if (de.d_name[0] == '.') continue;
if (uwsgi_webdav_dirlist_add_item(ub, de.d_name, strlen(de.d_name), de.d_type == DT_DIR ? 1 : 0)) goto end;
}
closedir(d);
#endif
if (uwsgi_buffer_append(ub, "</ul></div></body></html>", 25)) goto end;
if (uwsgi_response_add_content_type(wsgi_req, "text/html", 9)) goto end;
if (uwsgi_response_add_content_length(wsgi_req, ub->pos)) goto end;
uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos);
end:
uwsgi_buffer_destroy(ub);
}
static int uwsgi_wevdav_manage_get(struct wsgi_request *wsgi_req, int send_body) {
char filename[PATH_MAX];
size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
if (!filename_len) {
uwsgi_404(wsgi_req);
return UWSGI_OK;
}
if (uwsgi_is_dir(filename)) {
uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6);
if (send_body) {
uwsgi_webdav_dirlist(wsgi_req, filename);
}
return UWSGI_OK;
}
int fd = open(filename, O_RDONLY);
if (fd < 0) {
uwsgi_403(wsgi_req);
return UWSGI_OK;
}
struct stat st;
if (fstat(fd, &st)) {
close(fd);
uwsgi_403(wsgi_req);
return UWSGI_OK;
}
if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6))
goto end;
// add content_length
if (uwsgi_response_add_content_length(wsgi_req, st.st_size))
goto end;
// add last-modified
if (uwsgi_response_add_last_modified(wsgi_req, st.st_mtime))
goto end;
// add mime_type
size_t mime_type_len = 0;
char *mime_type = uwsgi_get_mime_type(filename, filename_len, &mime_type_len);
if (mime_type) {
if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_len))
goto end;
}
// add ETag (based on file mtime, not rock-solid, but good enough)
char *etag = uwsgi_num2str(st.st_mtime);
if (uwsgi_response_add_header(wsgi_req, "ETag", 4, etag, strlen(etag))) {
free(etag);
goto end;
}
free(etag);
// start sending the file (note: we do not use sendfile() api, for being able to use caching and transformations)
if (!send_body)
goto end;
// use a pretty big buffer (for performance reasons)
char buf[32768];
size_t remains = st.st_size;
while (remains > 0) {
ssize_t rlen = read(fd, buf, UMIN(32768, remains));
if (rlen <= 0) {
uwsgi_error("uwsgi_wevdav_manage_get/read()");
break;
}
remains -= rlen;
if (uwsgi_response_write_body_do(wsgi_req, buf, rlen)) {
break;
}
}
end:
close(fd);
return UWSGI_OK;
}
static int uwsgi_wevdav_manage_copy(struct wsgi_request *wsgi_req) {
uint16_t destination_len = 0;
char *destination = uwsgi_get_var(wsgi_req, "HTTP_DESTINATION", 16, &destination_len);
uwsgi_log("Destination: %.*s\n", destination_len, destination);
return -1;
}
static int uwsgi_wevdav_manage_move(struct wsgi_request *wsgi_req) {
char filename[PATH_MAX];
char d_filename[PATH_MAX];
size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
if (filename_len == 0) {
uwsgi_404(wsgi_req);
return UWSGI_OK;
}
uint16_t destination_len = 0;
char *destination = uwsgi_get_var(wsgi_req, "HTTP_DESTINATION", 16, &destination_len);
if (destination_len == 0) {
uwsgi_403(wsgi_req);
return UWSGI_OK;
}
uint16_t overwrite_len = 0;
int can_overwrite = 1;
char *overwrite = uwsgi_get_var(wsgi_req, "HTTP_OVERWRITE", 14, &overwrite_len);
if (overwrite) {
if (overwrite[0] == 'F') {
can_overwrite = 0;
}
}
uint16_t scheme_len = wsgi_req->scheme_len;
if (wsgi_req->scheme_len == 0) {
// http
scheme_len = 4;
}
uint16_t skip = scheme_len + 3 + wsgi_req->host_len;
int already_exists = 0;
size_t d_filename_len = uwsgi_webdav_expand_path(wsgi_req, destination + skip, destination_len - skip, d_filename);
if (d_filename_len > 0) {
already_exists = 1;
if (!can_overwrite) {
uwsgi_response_prepare_headers(wsgi_req, "412 Precondition Failed", 23);
return UWSGI_OK;
}
}
else {
d_filename_len = uwsgi_webdav_expand_fake_path(wsgi_req, destination + skip, destination_len - skip, d_filename);
}
if (d_filename_len == 0) {
uwsgi_response_prepare_headers(wsgi_req, "409 Conflict", 12);
return UWSGI_OK;
}
if (rename(filename, d_filename)) {
uwsgi_403(wsgi_req);
return UWSGI_OK;
}
if (already_exists) {
uwsgi_response_prepare_headers(wsgi_req, "204 No Content", 14);
}
else {
uwsgi_response_prepare_headers(wsgi_req, "201 Created", 11);
}
return UWSGI_OK;
}
static int uwsgi_wevdav_manage_mkcol(struct wsgi_request *wsgi_req) {
if (wsgi_req->post_cl > 0) {
uwsgi_response_prepare_headers(wsgi_req, "415 Unsupported Media Type", 26);
return UWSGI_OK;
}
char filename[PATH_MAX];
size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
// the collection already exists
if (filename_len > 0) {
uwsgi_response_prepare_headers(wsgi_req, "405 Method Not Allowed", 22);
return UWSGI_OK;
}
// remove the last slash (if needed)
if (wsgi_req->path_info_len > 1 && wsgi_req->path_info[wsgi_req->path_info_len-1] == '/') {
wsgi_req->path_info_len--;
}
filename_len = uwsgi_webdav_expand_fake_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
if (!filename_len) {
uwsgi_response_prepare_headers(wsgi_req, "409 Conflict", 12);
return UWSGI_OK;
}
// mkdir, if it fails, return a 409 (Conflict)
if (mkdir(filename, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
uwsgi_response_prepare_headers(wsgi_req, "409 Conflict", 12);
}
uwsgi_response_prepare_headers(wsgi_req, "201 Created", 11);
return UWSGI_OK;
}
static int uwsgi_wevdav_manage_mkcalendar(struct wsgi_request *wsgi_req, xmlDoc *doc) {
char filename[PATH_MAX];
size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
// the collection already exists
if (filename_len > 0) {
uwsgi_response_prepare_headers(wsgi_req, "405 Method Not Allowed", 22);
return UWSGI_OK;
}
// remove the last slash (if needed)
if (wsgi_req->path_info_len > 1 && wsgi_req->path_info[wsgi_req->path_info_len-1] == '/') {
wsgi_req->path_info_len--;
}
filename_len = uwsgi_webdav_expand_fake_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename);
if (!filename_len) {
uwsgi_response_prepare_headers(wsgi_req, "409 Conflict", 12);
return UWSGI_OK;
}
// mkdir, if it fails, return a 409 (Conflict)
if (mkdir(filename, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
uwsgi_response_prepare_headers(wsgi_req, "409 Conflict", 12);
return UWSGI_OK;
}
xmlNode *element = xmlDocGetRootElement(doc);
if (!element) return -1;
if (!element || (strcmp((char *) element->name, "mkcalendar"))) return -1;
xmlDoc *rdoc = xmlNewDoc(BAD_CAST "1.0");
xmlNode *foobar = xmlNewNode(NULL, BAD_CAST "foobar");
xmlDocSetRootElement(rdoc, foobar);
// propfind can be "set" or "remove"
xmlNode *node;
for (node = element->children; node; node = node->next) {
if (node->type == XML_ELEMENT_NODE) {
if (node->ns && !strcmp((char *) node->ns->href, "DAV:")) {
if (!strcmp((char *) node->name, "set")) {
uwsgi_webdav_manage_prop_update(wsgi_req, node, foobar, filename, 0);
}
}
}
}
uwsgi_response_prepare_headers(wsgi_req, "201 Created", 11);
xmlFreeDoc(rdoc);
return UWSGI_OK;
}
static int uwsgi_wevdav_manage_lock(struct wsgi_request *wsgi_req) {
uwsgi_response_prepare_headers(wsgi_req, "201 Created", 11);
return UWSGI_OK;
}
static int uwsgi_webdav_request(struct wsgi_request *wsgi_req) {
if (!udav.mountpoints) {
uwsgi_500(wsgi_req);
return -1;
}
if (uwsgi_parse_vars(wsgi_req)) {
return -1;
}
if (wsgi_req->path_info_len == 0) {
uwsgi_403(wsgi_req);
return UWSGI_OK;
}
wsgi_req->app_id = uwsgi_get_app_id(wsgi_req, wsgi_req->appid, wsgi_req->appid_len, webdav_plugin.modifier1);
if (wsgi_req->app_id == -1) {
uwsgi_403(wsgi_req);
return UWSGI_OK;
}
// non lockables methods...
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "OPTIONS", 7)) {
return uwsgi_wevdav_manage_options(wsgi_req);
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "GET", 3)) {
return uwsgi_wevdav_manage_get(wsgi_req, 1);
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) {
return uwsgi_wevdav_manage_get(wsgi_req, 0);
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "PROPFIND", 8)) {
if (wsgi_req->post_cl > 0) {
ssize_t body_len = 0;
char *body = uwsgi_request_body_read(wsgi_req, wsgi_req->post_cl, &body_len);
#ifdef UWSGI_DEBUG
uwsgi_log("%.*s\n", body_len, body);
#endif
xmlDoc *doc = xmlReadMemory(body, body_len, NULL, NULL, 0);
if (!doc) goto end;
uwsgi_wevdav_manage_propfind(wsgi_req, doc);
xmlFreeDoc(doc);
}
else {
uwsgi_wevdav_manage_propfind(wsgi_req, NULL);
}
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "REPORT", 6)) {
if (wsgi_req->post_cl > 0) {
ssize_t body_len = 0;
char *body = uwsgi_request_body_read(wsgi_req, wsgi_req->post_cl, &body_len);
#ifdef UWSGI_DEBUG
uwsgi_log("%.*s\n", body_len, body);
#endif
xmlDoc *doc = xmlReadMemory(body, body_len, NULL, NULL, 0);
if (!doc) goto end;
xmlFreeDoc(doc);
}
}
// lockable methods ...
// check for locking
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "PUT", 3)) {
return uwsgi_wevdav_manage_put(wsgi_req);
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "DELETE", 6)) {
return uwsgi_wevdav_manage_delete(wsgi_req);
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "MKCOL", 5)) {
return uwsgi_wevdav_manage_mkcol(wsgi_req);
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "MKCALENDAR", 10)) {
if (wsgi_req->post_cl == 0)
goto end;
ssize_t body_len = 0;
char *body = uwsgi_request_body_read(wsgi_req, wsgi_req->post_cl, &body_len);
#ifdef UWSGI_DEBUG
uwsgi_log("%.*s\n", body_len, body);
#endif
xmlDoc *doc = xmlReadMemory(body, body_len, NULL, NULL, 0);
if (!doc) goto end;
uwsgi_wevdav_manage_mkcalendar(wsgi_req, doc);
xmlFreeDoc(doc);
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "COPY", 4)) {
return uwsgi_wevdav_manage_copy(wsgi_req);
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "MOVE", 4)) {
return uwsgi_wevdav_manage_move(wsgi_req);
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "LOCK", 4)) {
if (wsgi_req->post_cl > 0) {
ssize_t body_len = 0;
char *body = uwsgi_request_body_read(wsgi_req, wsgi_req->post_cl, &body_len);
#ifdef UWSGI_DEBUG
uwsgi_log("%.*s\n", body_len, body);
#endif
xmlDoc *doc = xmlReadMemory(body, body_len, NULL, NULL, 0);
if (!doc) goto end;
xmlFreeDoc(doc);
}
return uwsgi_wevdav_manage_lock(wsgi_req);
}
if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "PROPPATCH", 9)) {
if (wsgi_req->post_cl == 0)
goto end;
ssize_t body_len = 0;
char *body = uwsgi_request_body_read(wsgi_req, wsgi_req->post_cl, &body_len);
#ifdef UWSGI_DEBUG
uwsgi_log("%.*s\n", body_len, body);
#endif
xmlDoc *doc = xmlReadMemory(body, body_len, NULL, NULL, 0);
if (!doc) goto end;
uwsgi_wevdav_manage_proppatch(wsgi_req, doc);
xmlFreeDoc(doc);
}
end:
return UWSGI_OK;
}
static void uwsgi_webdav_mount() {
struct uwsgi_string_list *usl = udav.mountpoints;
while(usl) {
if (uwsgi_apps_cnt >= uwsgi.max_apps) {
uwsgi_log("ERROR: you cannot load more than %d apps in a worker\n", uwsgi.max_apps);
exit(1);
}
int id = uwsgi_apps_cnt;
char *mountpoint = "";
int mountpoint_len = 0;
char *docroot = usl->value;
char *equal = strchr(usl->value, '=');
if (equal) {
*equal = 0;
docroot = equal+1;
mountpoint = usl->value;
mountpoint_len = strlen(mountpoint);
}
char *wd_docroot = uwsgi_calloc(PATH_MAX);
if (!realpath(docroot, wd_docroot)) {
uwsgi_error("uwsgi_webdav_mount()/realpath()");
exit(1);
}
if (equal) {
*equal = '=';
}
struct uwsgi_app *ua = uwsgi_add_app(id, webdav_plugin.modifier1, mountpoint, mountpoint_len, wd_docroot, wd_docroot);
uwsgi_emulate_cow_for_apps(id);
uwsgi_log("WebDAV mountpoint \"%.*s\" (%d) added: docroot=%s\n", ua->mountpoint_len, ua->mountpoint, id, wd_docroot );
usl = usl->next;
}
}
static void uwsgi_webdav_after_request(struct wsgi_request *wsgi_req) {
if (!udav.mountpoints) return;
log_request(wsgi_req);
}
struct uwsgi_plugin webdav_plugin = {
.modifier1 = 35,
.name = "webdav",
.options = uwsgi_webdav_options,
.init_apps = uwsgi_webdav_mount,
.request = uwsgi_webdav_request,
.after_request = uwsgi_webdav_after_request,
};