[mod_webdav] improve PROPFIND,PROPPATCH (#1818, #1953)

fix "allprop" propfind request to report all 'live' properties
add "supportedlock" 'live' property, if ./configure --with-webdav-locks
report collections (directory) paths with trailing slash ('/') on path
redirect operations on collections without trailing slash ('/') to URI
  with trailing slash ('/')

fix PROPPATCH to work properly and eliminate PROPPATCH memory leak
fix property update after MOVE
move CREATE TABLE statements *before* any prepare statements to avoid
  invalidating the prepare statements when the tables are first created
  **thx Uranus Zhou for the explanation:
    https://zohead.com/archives/lighty-sqlite-err/?lang=en

x-ref:
  "Improve DAV support to be able to handle git as a client"
  https://redmine.lighttpd.net/issues/1953
  "add RFC-compliant LOCK support to mod_webdav"  (still not compliant)
  https://redmine.lighttpd.net/issues/1818

Note: this has not been tested whether or not mod_webdav works with git
The (highly) recommended method to support git via HTTP is to use
git-http-backend via CGI.  gitolite and gitosis provide other good
alternative ways to access git.

This patch does result in more WebDAV 'Litmus' tests passing, even
though mod_webdav still pretends to implement "If" conditional locking,
granting locks to all requestors and not strictly enforcing locks.
personal/stbuehler/mod-csrf-old
Glenn Strauss 7 years ago
parent e0115208ec
commit 80bb42266e

@ -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 */
@ -1995,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;
@ -2075,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) {
@ -2150,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;
@ -2192,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;
@ -2230,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);
}
@ -2240,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