[mod_webdav] improve PROPFIND,PROPPATCH; map COPY/MOVE Destination

improve PROPFIND,PROPPATCH
map COPY/MOVE Destination to aliases

Merge branch 'bug-1787-webdav-alias-destination' into master

github: closes #61
personal/stbuehler/mod-csrf-old
Glenn Strauss 7 years ago
commit 9b14e017c8

@ -215,7 +215,7 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
}
if (SQLITE_OK != sqlite3_exec(s->sql,
"CREATE TABLE properties ("
"CREATE TABLE IF NOT EXISTS properties ("
" resource TEXT NOT NULL,"
" prop TEXT NOT NULL,"
" ns TEXT NOT NULL,"
@ -232,6 +232,27 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
sqlite3_free(err);
}
if (SQLITE_OK != sqlite3_exec(s->sql,
"CREATE TABLE IF NOT EXISTS locks ("
" locktoken TEXT NOT NULL,"
" resource TEXT NOT NULL,"
" lockscope TEXT NOT NULL,"
" locktype TEXT NOT NULL,"
" owner TEXT NOT NULL,"
" depth INT NOT NULL,"
" timeout TIMESTAMP NOT NULL,"
" PRIMARY KEY(locktoken))",
NULL, NULL, &err)) {
if (0 != strcmp(err, "table locks already exists")) {
log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err);
sqlite3_free(err);
return HANDLER_ERROR;
}
sqlite3_free(err);
}
if (SQLITE_OK != sqlite3_prepare(s->sql,
CONST_STR_LEN("SELECT value FROM properties WHERE resource = ? AND prop = ? AND ns = ?"),
&(s->stmt_select_prop), &next_stmt)) {
@ -288,7 +309,7 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
}
if (SQLITE_OK != sqlite3_prepare(s->sql,
CONST_STR_LEN("UPDATE properties SET resource = ? WHERE resource = ?"),
CONST_STR_LEN("UPDATE OR REPLACE properties SET resource = ? WHERE resource = ?"),
&(s->stmt_move_uri), &next_stmt)) {
/* prepare failed */
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
@ -298,27 +319,6 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
/* LOCKS */
if (SQLITE_OK != sqlite3_exec(s->sql,
"CREATE TABLE locks ("
" locktoken TEXT NOT NULL,"
" resource TEXT NOT NULL,"
" lockscope TEXT NOT NULL,"
" locktype TEXT NOT NULL,"
" owner TEXT NOT NULL,"
" depth INT NOT NULL,"
" timeout TIMESTAMP NOT NULL,"
" PRIMARY KEY(locktoken))",
NULL, NULL, &err)) {
if (0 != strcmp(err, "table locks already exists")) {
log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err);
sqlite3_free(err);
return HANDLER_ERROR;
}
sqlite3_free(err);
}
if (SQLITE_OK != sqlite3_prepare(s->sql,
CONST_STR_LEN("INSERT INTO locks (locktoken, resource, lockscope, locktype, owner, depth, timeout) VALUES (?,?,?,?,?,?, CURRENT_TIME + 600)"),
&(s->stmt_create_lock), &next_stmt)) {
@ -901,6 +901,16 @@ static int webdav_get_live_property(server *srv, connection *con, plugin_data *p
buffer_append_string_len(b, CONST_STR_LEN("en"));
buffer_append_string_len(b, CONST_STR_LEN("</D:getcontentlanguage>"));
found = 1;
#ifdef USE_LOCKS
} else if (0 == strcmp(prop_name, "supportedlock")) {
buffer_append_string_len(b,CONST_STR_LEN("<D:supportedlock>"));
buffer_append_string_len(b,CONST_STR_LEN("<D:lockentry>"));
buffer_append_string_len(b,CONST_STR_LEN("<D:lockscope><D:exclusive/></D:lockscope>"));
buffer_append_string_len(b,CONST_STR_LEN("<D:locktype><D:write/></D:locktype>"));
buffer_append_string_len(b,CONST_STR_LEN("</D:lockentry>"));
buffer_append_string_len(b, CONST_STR_LEN("</D:supportedlock>"));
found = 1;
#endif
}
}
@ -965,7 +975,9 @@ static webdav_property live_properties[] = {
{ "DAV:", "resourcetype" },
{ "DAV:", "lockdiscovery" },
{ "DAV:", "source" },
#ifdef USE_LOCKS
{ "DAV:", "supportedlock" },
#endif
{ NULL, NULL }
};
@ -980,7 +992,7 @@ typedef struct {
static int webdav_get_props(server *srv, connection *con, plugin_data *p, physical *dst, webdav_properties *props, buffer *b_200, buffer *b_404) {
size_t i;
if (props) {
if (props && props->used) {
for (i = 0; i < props->used; i++) {
webdav_property *prop;
@ -1274,6 +1286,10 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
break;
}
if (S_ISDIR(sce->st.st_mode) && con->physical.path->ptr[buffer_string_length(con->physical.path)-1] != '/') {
http_response_redirect_to_directory(srv, con);
return HANDLER_FINISHED;
}
#ifdef USE_PROPPATCH
/* any special requests or just allprop ? */
@ -1459,6 +1475,10 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
buffer_append_string_len(b,CONST_STR_LEN("://"));
buffer_append_string_buffer(b, con->uri.authority);
buffer_append_string_encoded(b, CONST_BUF_LEN(d.rel_path), ENCODING_REL_URI);
if (0 == stat(d.path->ptr, &st) && S_ISDIR(st.st_mode)) {
/* Append a '/' on subdirectories */
buffer_append_string_len(b,CONST_STR_LEN("/"));
}
buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n"));
if (!buffer_string_is_empty(prop_200)) {
@ -1580,7 +1600,14 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
break;
}
} else if (S_ISDIR(st.st_mode)) {
buffer *multi_status_resp = buffer_init();
buffer *multi_status_resp;
if (con->physical.path->ptr[buffer_string_length(con->physical.path)-1] != '/') {
http_response_redirect_to_directory(srv, con);
return HANDLER_FINISHED;
}
multi_status_resp = buffer_init();
if (webdav_delete_dir(srv, con, p, &(con->physical), multi_status_resp)) {
/* we got an error somewhere in between, build a 207 */
@ -1920,15 +1947,62 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
buffer_to_lower(p->physical.rel_path);
}
buffer_copy_buffer(p->physical.path, p->physical.doc_root);
buffer_append_slash(p->physical.path);
buffer_copy_buffer(p->physical.basedir, p->physical.path);
/* don't add a second / */
if (p->physical.rel_path->ptr[0] == '/') {
buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr + 1, buffer_string_length(p->physical.rel_path) - 1);
} else {
buffer_append_string_buffer(p->physical.path, p->physical.rel_path);
/* Destination physical path
* src con->physical.path might have been remapped with mod_alias.
* (but mod_alias does not modify con->physical.rel_path)
* Find matching prefix to support use of mod_alias to remap webdav root.
* Aliasing of paths underneath the webdav root might not work.
* Likewise, mod_rewrite URL rewriting might thwart this comparison.
* Use mod_redirect instead of mod_alias to remap paths *under* webdav root.
* Use mod_redirect instead of mod_rewrite on *any* parts of path to webdav.
* (Related, use mod_auth to protect webdav root, but avoid attempting to
* use mod_auth on paths underneath webdav root, as Destination is not
* validated with mod_auth)
*
* tl;dr: webdav paths and webdav properties are managed by mod_webdav,
* so do not modify paths externally or else undefined behavior
* or corruption may occur
*/
{
/* find matching URI prefix
* check if remaining con->physical.rel_path matches suffix
* of con->physical.basedir so that we can use it to
* remap Destination physical path */
size_t i, remain;
sep = con->uri.path->ptr;
sep2 = p->uri.path->ptr;
for (i = 0; sep[i] && sep[i] == sep2[i]; ++i) ;
if (sep[i] == '\0' && (sep2[i] == '\0' || sep2[i] == '/' || (i > 0 && sep[i-1] == '/'))) {
/* src and dst URI match or dst is nested inside src; invalid COPY or MOVE */
con->http_status = 403;
return HANDLER_FINISHED;
}
while (i != 0 && sep[--i] != '/') ; /* find matching directory path */
remain = buffer_string_length(con->uri.path) - i;
if (!con->conf.force_lowercase_filenames
? buffer_is_equal_right_len(con->physical.path, con->physical.rel_path, remain)
:(buffer_string_length(con->physical.path) >= remain
&& 0 == strncasecmp(con->physical.path->ptr+buffer_string_length(con->physical.path)-remain, con->physical.rel_path->ptr+i, remain))) {
/* (at this point, p->physical.rel_path is identical to (or lowercased version of) p->uri.path) */
buffer_copy_string_len(p->physical.path, con->physical.path->ptr, buffer_string_length(con->physical.path)-remain);
buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr+i, buffer_string_length(p->physical.rel_path)-i);
buffer_copy_buffer(p->physical.basedir, con->physical.basedir);
buffer_append_slash(p->physical.basedir);
} else {
/* unable to perform physical path remap here;
* assume doc_root/rel_path and no remapping */
buffer_copy_buffer(p->physical.path, p->physical.doc_root);
buffer_append_slash(p->physical.path);
buffer_copy_buffer(p->physical.basedir, p->physical.path);
/* don't add a second / */
if (p->physical.rel_path->ptr[0] == '/') {
buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr + 1, buffer_string_length(p->physical.rel_path) - 1);
} else {
buffer_append_string_buffer(p->physical.path, p->physical.rel_path);
}
}
}
/* let's see if the source is a directory
@ -1948,6 +2022,11 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
int r;
/* src is a directory */
if (con->physical.path->ptr[buffer_string_length(con->physical.path)-1] != '/') {
http_response_redirect_to_directory(srv, con);
return HANDLER_FINISHED;
}
if (-1 == stat(p->physical.path->ptr, &st)) {
if (-1 == mkdir(p->physical.path->ptr, WEBDAV_DIR_MODE)) {
con->http_status = 403;
@ -2028,21 +2107,6 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
#ifdef USE_PROPPATCH
sqlite3_stmt *stmt;
stmt = p->conf.stmt_delete_uri;
if (stmt) {
sqlite3_reset(stmt);
/* bind the values to the insert */
sqlite3_bind_text(stmt, 1,
CONST_BUF_LEN(con->uri.path),
SQLITE_TRANSIENT);
if (SQLITE_DONE != sqlite3_step(stmt)) {
log_error_write(srv, __FILE__, __LINE__, "ss", "sql-move(delete old) failed:", sqlite3_errmsg(p->conf.sql));
}
}
stmt = p->conf.stmt_move_uri;
if (stmt) {
@ -2103,6 +2167,11 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
}
}
if (S_ISDIR(st.st_mode) && con->physical.path->ptr[buffer_string_length(con->physical.path)-1] != '/') {
http_response_redirect_to_directory(srv, con);
return HANDLER_FINISHED;
}
#ifdef USE_PROPPATCH
if (con->request.content_length) {
xmlDocPtr xml;
@ -2145,6 +2214,7 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
for (props = cmd->children; props; props = props->next) {
if (0 == xmlStrcmp(props->name, BAD_CAST "prop")) {
xmlNode *prop;
char *propval = NULL;
int r;
prop = props->children;
@ -2183,9 +2253,13 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
SQLITE_TRANSIENT);
}
if (stmt == p->conf.stmt_update_prop) {
propval = prop->children
? (char *)xmlNodeListGetString(xml, prop->children, 0)
: NULL;
sqlite3_bind_text(stmt, 4,
(char *)xmlNodeGetContent(prop),
strlen((char *)xmlNodeGetContent(prop)),
propval ? propval : "",
propval ? strlen(propval) : 0,
SQLITE_TRANSIENT);
}
@ -2193,6 +2267,8 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"sql-set failed:", sqlite3_errmsg(p->conf.sql));
}
if (propval) xmlFree(propval);
}
}
if (empty_ns) break;

Loading…
Cancel
Save