--- embedaddon/lighttpd/src/mod_webdav.c 2014/06/15 20:20:06 1.1.1.2
+++ embedaddon/lighttpd/src/mod_webdav.c 2016/11/02 10:35:00 1.1.1.3
@@ -1,7 +1,10 @@
+#include "first.h"
+
#include "base.h"
#include "log.h"
#include "buffer.h"
#include "response.h"
+#include "connections.h"
#include "plugin.h"
@@ -120,7 +123,7 @@ FREE_FUNC(mod_webdav_free) {
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s = p->config_storage[i];
- if (!s) continue;
+ if (NULL == s) continue;
buffer_free(s->sqlite_db_name);
#ifdef USE_PROPPATCH
@@ -182,6 +185,7 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
for (i = 0; i < srv->config_context->used; i++) {
+ data_config const* config = (data_config const*)srv->config_context->data[i];
plugin_config *s;
s = calloc(1, sizeof(plugin_config));
@@ -194,11 +198,11 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
p->config_storage[i] = s;
- if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+ if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
return HANDLER_ERROR;
}
- if (!buffer_is_empty(s->sqlite_db_name)) {
+ if (!buffer_string_is_empty(s->sqlite_db_name)) {
#ifdef USE_PROPPATCH
const char *next_stmt;
char *err;
@@ -211,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,"
@@ -228,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)) {
@@ -284,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));
@@ -294,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)) {
@@ -334,7 +338,7 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
}
if (SQLITE_OK != sqlite3_prepare(s->sql,
- CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout FROM locks WHERE locktoken = ?"),
+ CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout-CURRENT_TIME FROM locks WHERE locktoken = ?"),
&(s->stmt_read_lock), &next_stmt)) {
/* prepare failed */
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
@@ -343,7 +347,7 @@ SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
}
if (SQLITE_OK != sqlite3_prepare(s->sql,
- CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout FROM locks WHERE resource = ?"),
+ CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout-CURRENT_TIME FROM locks WHERE resource = ?"),
&(s->stmt_read_lock_by_uri), &next_stmt)) {
/* prepare failed */
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
@@ -446,7 +450,7 @@ URIHANDLER_FUNC(mod_webdav_uri_handler) {
UNUSED(srv);
- if (con->uri.path->used == 0) return HANDLER_GO_ON;
+ if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
mod_webdav_patch_connection(srv, con, p);
@@ -519,7 +523,7 @@ static int webdav_gen_response_status_tag(server *srv,
} else {
buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 "));
}
- buffer_append_long(b, status);
+ buffer_append_int(b, status);
buffer_append_string_len(b, CONST_STR_LEN(" "));
buffer_append_string(b, get_http_status_name(status));
@@ -558,9 +562,8 @@ static int webdav_delete_file(server *srv, connection
/* bind the values to the insert */
sqlite3_bind_text(stmt, 1,
- dst->rel_path->ptr,
- dst->rel_path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(dst->rel_path),
+ SQLITE_TRANSIENT);
if (SQLITE_DONE != sqlite3_step(stmt)) {
/* */
@@ -590,17 +593,17 @@ static int webdav_delete_dir(server *srv, connection *
int status = 0;
if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
- (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) {
+ (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) {
continue;
/* ignore the parent dir */
}
- buffer_copy_string_buffer(d.path, dst->path);
- BUFFER_APPEND_SLASH(d.path);
+ buffer_copy_buffer(d.path, dst->path);
+ buffer_append_slash(d.path);
buffer_append_string(d.path, de->d_name);
- buffer_copy_string_buffer(d.rel_path, dst->rel_path);
- BUFFER_APPEND_SLASH(d.rel_path);
+ buffer_copy_buffer(d.rel_path, dst->rel_path);
+ buffer_append_slash(d.rel_path);
buffer_append_string(d.rel_path, de->d_name);
/* stat and unlink afterwards */
@@ -636,9 +639,8 @@ static int webdav_delete_dir(server *srv, connection *
/* bind the values to the insert */
sqlite3_bind_text(stmt, 1,
- d.rel_path->ptr,
- d.rel_path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(d.rel_path),
+ SQLITE_TRANSIENT);
if (SQLITE_DONE != sqlite3_step(stmt)) {
/* */
@@ -659,13 +661,25 @@ static int webdav_delete_dir(server *srv, connection *
return have_multi_status;
}
+/* don't want to block when open()ing a fifo */
+#if defined(O_NONBLOCK)
+# define FIFO_NONBLOCK O_NONBLOCK
+#else
+# define FIFO_NONBLOCK 0
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
static int webdav_copy_file(server *srv, connection *con, plugin_data *p, physical *src, physical *dst, int overwrite) {
- stream s;
- int status = 0, ofd;
+ char *data;
+ ssize_t rd, wr, offset;
+ int status = 0, ifd, ofd;
UNUSED(srv);
UNUSED(con);
- if (stream_open(&s, src->path)) {
+ if (-1 == (ifd = open(src->path->ptr, O_RDONLY | O_BINARY | FIFO_NONBLOCK))) {
return 403;
}
@@ -686,23 +700,33 @@ static int webdav_copy_file(server *srv, connection *c
status = 403;
break;
}
- stream_close(&s);
+ close(ifd);
return status;
}
- if (-1 == write(ofd, s.start, s.size)) {
- switch(errno) {
- case ENOSPC:
- status = 507;
+ data = malloc(131072);
+ force_assert(data);
+
+ while (0 < (rd = read(ifd, data, 131072))) {
+ offset = 0;
+ do {
+ wr = write(ofd, data+offset, (size_t)(rd-offset));
+ } while (wr >= 0 ? (offset += wr) != rd : (errno == EINTR));
+ if (-1 == wr) {
+ status = (errno == ENOSPC) ? 507 : 403;
break;
- default:
- status = 403;
- break;
}
+
}
+ if (0 != rd && 0 == status) status = 403;
- stream_close(&s);
- close(ofd);
+ free(data);
+ close(ifd);
+ if (0 != close(ofd)) {
+ if (0 == status) status = (errno == ENOSPC) ? 507 : 403;
+ log_error_write(srv, __FILE__, __LINE__, "sbss",
+ "close ", dst->path, "failed: ", strerror(errno));
+ }
#ifdef USE_PROPPATCH
if (0 == status) {
@@ -714,14 +738,12 @@ static int webdav_copy_file(server *srv, connection *c
/* bind the values to the insert */
sqlite3_bind_text(stmt, 1,
- dst->rel_path->ptr,
- dst->rel_path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(dst->rel_path),
+ SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2,
- src->rel_path->ptr,
- src->rel_path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(src->rel_path),
+ SQLITE_TRANSIENT);
if (SQLITE_DONE != sqlite3_step(stmt)) {
/* */
@@ -751,25 +773,25 @@ static int webdav_copy_dir(server *srv, connection *co
while (NULL != (de = readdir(srcdir))) {
struct stat st;
- if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
- (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) {
+ if ((de->d_name[0] == '.' && de->d_name[1] == '\0')
+ || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) {
continue;
}
- buffer_copy_string_buffer(s.path, src->path);
- BUFFER_APPEND_SLASH(s.path);
+ buffer_copy_buffer(s.path, src->path);
+ buffer_append_slash(s.path);
buffer_append_string(s.path, de->d_name);
- buffer_copy_string_buffer(d.path, dst->path);
- BUFFER_APPEND_SLASH(d.path);
+ buffer_copy_buffer(d.path, dst->path);
+ buffer_append_slash(d.path);
buffer_append_string(d.path, de->d_name);
- buffer_copy_string_buffer(s.rel_path, src->rel_path);
- BUFFER_APPEND_SLASH(s.rel_path);
+ buffer_copy_buffer(s.rel_path, src->rel_path);
+ buffer_append_slash(s.rel_path);
buffer_append_string(s.rel_path, de->d_name);
- buffer_copy_string_buffer(d.rel_path, dst->rel_path);
- BUFFER_APPEND_SLASH(d.rel_path);
+ buffer_copy_buffer(d.rel_path, dst->rel_path);
+ buffer_append_slash(d.rel_path);
buffer_append_string(d.rel_path, de->d_name);
if (-1 == stat(s.path->ptr, &st)) {
@@ -793,14 +815,12 @@ static int webdav_copy_dir(server *srv, connection *co
/* bind the values to the insert */
sqlite3_bind_text(stmt, 1,
- dst->rel_path->ptr,
- dst->rel_path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(dst->rel_path),
+ SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2,
- src->rel_path->ptr,
- src->rel_path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(src->rel_path),
+ SQLITE_TRANSIENT);
if (SQLITE_DONE != sqlite3_step(stmt)) {
/* */
@@ -827,6 +847,84 @@ static int webdav_copy_dir(server *srv, connection *co
return status;
}
+#ifdef USE_LOCKS
+static void webdav_activelock(buffer *b,
+ const buffer *locktoken, const char *lockscope, const char *locktype, int depth, int timeout) {
+ buffer_append_string_len(b, CONST_STR_LEN("\n"));
+
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string_len(b, CONST_STR_LEN("\n"));
+
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string_len(b, CONST_STR_LEN("\n"));
+
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string(b, depth == 0 ? "0" : "infinity");
+ buffer_append_string_len(b, CONST_STR_LEN("\n"));
+
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string_len(b, CONST_STR_LEN("Second-"));
+ buffer_append_int(b, timeout);
+ buffer_append_string_len(b, CONST_STR_LEN("\n"));
+
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string_len(b, CONST_STR_LEN("\n"));
+
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string_buffer(b, locktoken);
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string_len(b, CONST_STR_LEN("\n"));
+
+ buffer_append_string_len(b, CONST_STR_LEN("\n"));
+}
+
+static void webdav_get_live_property_lockdiscovery(server *srv, connection *con, plugin_data *p, physical *dst, buffer *b) {
+
+ sqlite3_stmt *stmt = p->conf.stmt_read_lock_by_uri;
+ if (!stmt) { /*(should not happen)*/
+ buffer_append_string_len(b, CONST_STR_LEN("\n\n"));
+ return;
+ }
+ UNUSED(srv);
+ UNUSED(con);
+
+ /* SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout
+ * FROM locks
+ * WHERE resource = ? */
+
+ sqlite3_reset(stmt);
+
+ sqlite3_bind_text(stmt, 1,
+ CONST_BUF_LEN(dst->rel_path),
+ SQLITE_TRANSIENT);
+
+ buffer_append_string_len(b, CONST_STR_LEN("\n"));
+ while (SQLITE_ROW == sqlite3_step(stmt)) {
+ const char *lockscope = (const char *)sqlite3_column_text(stmt, 2);
+ const char *locktype = (const char *)sqlite3_column_text(stmt, 3);
+ const int depth = sqlite3_column_int(stmt, 5);
+ const int timeout = sqlite3_column_int(stmt, 6);
+ buffer locktoken = { NULL, 0, 0 };
+ locktoken.ptr = (char *)sqlite3_column_text(stmt, 0);
+ locktoken.used = sqlite3_column_bytes(stmt, 0);
+ if (locktoken.used) ++locktoken.used;
+ locktoken.size = locktoken.used;
+
+ if (timeout > 0) {
+ webdav_activelock(b, &locktoken, lockscope, locktype, depth, timeout);
+ }
+ }
+ buffer_append_string_len(b, CONST_STR_LEN("\n"));
+}
+#endif
+
static int webdav_get_live_property(server *srv, connection *con, plugin_data *p, physical *dst, char *prop_name, buffer *b) {
stat_cache_entry *sce = NULL;
int found = 0;
@@ -841,8 +939,10 @@ static int webdav_get_live_property(server *srv, conne
if (0 == strcmp(prop_name, "resourcetype")) {
if (S_ISDIR(sce->st.st_mode)) {
buffer_append_string_len(b, CONST_STR_LEN(""));
- found = 1;
+ } else {
+ buffer_append_string_len(b, CONST_STR_LEN(""));
}
+ found = 1;
} else if (0 == strcmp(prop_name, "getcontenttype")) {
if (S_ISDIR(sce->st.st_mode)) {
buffer_append_string_len(b, CONST_STR_LEN("httpd/unix-directory"));
@@ -851,9 +951,9 @@ static int webdav_get_live_property(server *srv, conne
for (k = 0; k < con->conf.mimetypes->used; k++) {
data_string *ds = (data_string *)con->conf.mimetypes->data[k];
- if (ds->key->used == 0) continue;
+ if (buffer_is_empty(ds->key)) continue;
- if (buffer_is_equal_right_len(dst->path, ds->key, ds->key->used - 1)) {
+ if (buffer_is_equal_right_len(dst->path, ds->key, buffer_string_length(ds->key))) {
buffer_append_string_len(b,CONST_STR_LEN(""));
buffer_append_string_buffer(b, ds->value);
buffer_append_string_len(b, CONST_STR_LEN(""));
@@ -877,7 +977,7 @@ static int webdav_get_live_property(server *srv, conne
found = 1;
} else if (0 == strcmp(prop_name, "getcontentlength")) {
buffer_append_string_len(b,CONST_STR_LEN(""));
- buffer_append_off_t(b, sce->st.st_size);
+ buffer_append_int(b, sce->st.st_size);
buffer_append_string_len(b, CONST_STR_LEN(""));
found = 1;
} else if (0 == strcmp(prop_name, "getcontentlanguage")) {
@@ -885,6 +985,26 @@ static int webdav_get_live_property(server *srv, conne
buffer_append_string_len(b, CONST_STR_LEN("en"));
buffer_append_string_len(b, CONST_STR_LEN(""));
found = 1;
+ } else if (0 == strcmp(prop_name, "getetag")) {
+ etag_create(con->physical.etag, &sce->st, con->etag_flags);
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_append_string_buffer(b, con->physical.etag);
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ buffer_reset(con->physical.etag);
+ found = 1;
+ #ifdef USE_LOCKS
+ } else if (0 == strcmp(prop_name, "lockdiscovery")) {
+ webdav_get_live_property_lockdiscovery(srv, con, p, dst, b);
+ found = 1;
+ } else if (0 == strcmp(prop_name, "supportedlock")) {
+ buffer_append_string_len(b,CONST_STR_LEN(""));
+ buffer_append_string_len(b,CONST_STR_LEN(""));
+ buffer_append_string_len(b,CONST_STR_LEN(""));
+ buffer_append_string_len(b,CONST_STR_LEN(""));
+ buffer_append_string_len(b,CONST_STR_LEN(""));
+ buffer_append_string_len(b, CONST_STR_LEN(""));
+ found = 1;
+ #endif
}
}
@@ -907,17 +1027,16 @@ static int webdav_get_property(server *srv, connection
/* bind the values to the insert */
sqlite3_bind_text(stmt, 1,
- dst->rel_path->ptr,
- dst->rel_path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(dst->rel_path),
+ SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2,
- prop_name,
- strlen(prop_name),
- SQLITE_TRANSIENT);
+ prop_name,
+ strlen(prop_name),
+ SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3,
- prop_ns,
- strlen(prop_ns),
- SQLITE_TRANSIENT);
+ prop_ns,
+ strlen(prop_ns),
+ SQLITE_TRANSIENT);
/* it is the PK */
while (SQLITE_ROW == sqlite3_step(stmt)) {
@@ -941,16 +1060,18 @@ typedef struct {
static webdav_property live_properties[] = {
{ "DAV:", "creationdate" },
- { "DAV:", "displayname" },
+ /*{ "DAV:", "displayname" },*//*(not implemented)*/
{ "DAV:", "getcontentlanguage" },
{ "DAV:", "getcontentlength" },
{ "DAV:", "getcontenttype" },
{ "DAV:", "getetag" },
{ "DAV:", "getlastmodified" },
{ "DAV:", "resourcetype" },
+ /*{ "DAV:", "source" },*//*(not implemented)*/
+ #ifdef USE_LOCKS
{ "DAV:", "lockdiscovery" },
- { "DAV:", "source" },
{ "DAV:", "supportedlock" },
+ #endif
{ NULL, NULL }
};
@@ -965,7 +1086,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;
@@ -1003,6 +1124,8 @@ static int webdav_parse_chunkqueue(server *srv, connec
for (c = cq->first; cq->bytes_out != cq->bytes_in; c = cq->first) {
size_t weWant = cq->bytes_out - cq->bytes_in;
size_t weHave;
+ int mapped;
+ void *data;
switch(c->type) {
case FILE_CHUNK:
@@ -1011,7 +1134,10 @@ static int webdav_parse_chunkqueue(server *srv, connec
if (weHave > weWant) weHave = weWant;
/* xml chunks are always memory, mmap() is our friend */
- if (c->file.mmap.start == MAP_FAILED) {
+ mapped = (c->file.mmap.start != MAP_FAILED);
+ if (mapped) {
+ data = c->file.mmap.start + c->offset;
+ } else {
if (-1 == c->file.fd && /* open the file if not already open */
-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
@@ -1019,34 +1145,38 @@ static int webdav_parse_chunkqueue(server *srv, connec
return -1;
}
- if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
- log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
- strerror(errno), c->file.name, c->file.fd);
- close(c->file.fd);
- c->file.fd = -1;
-
- return -1;
+ if (MAP_FAILED != (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_PRIVATE, c->file.fd, 0))) {
+ /* chunk_reset() or chunk_free() will cleanup for us */
+ c->file.mmap.length = c->file.length;
+ data = c->file.mmap.start + c->offset;
+ mapped = 1;
+ } else {
+ ssize_t rd;
+ if (weHave > 65536) weHave = 65536;
+ data = malloc(weHave);
+ force_assert(data);
+ if (-1 == lseek(c->file.fd, c->file.start + c->offset, SEEK_SET)
+ || 0 > (rd = read(c->file.fd, data, weHave))) {
+ log_error_write(srv, __FILE__, __LINE__, "ssbd", "lseek/read failed: ",
+ strerror(errno), c->file.name, c->file.fd);
+ free(data);
+ return -1;
+ }
+ weHave = (size_t)rd;
}
-
- close(c->file.fd);
- c->file.fd = -1;
-
- c->file.mmap.length = c->file.length;
-
- /* chunk_reset() or chunk_free() will cleanup for us */
}
- if (XML_ERR_OK != (err = xmlParseChunk(ctxt, c->file.mmap.start + c->offset, weHave, 0))) {
+ if (XML_ERR_OK != (err = xmlParseChunk(ctxt, data, weHave, 0))) {
log_error_write(srv, __FILE__, __LINE__, "sodd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err);
}
- c->offset += weHave;
- cq->bytes_out += weHave;
+ chunkqueue_mark_written(cq, weHave);
+ if (!mapped) free(data);
break;
case MEM_CHUNK:
/* append to the buffer */
- weHave = c->mem->used - 1 - c->offset;
+ weHave = buffer_string_length(c->mem) - c->offset;
if (weHave > weWant) weHave = weWant;
@@ -1058,17 +1188,12 @@ static int webdav_parse_chunkqueue(server *srv, connec
log_error_write(srv, __FILE__, __LINE__, "sodd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err);
}
- c->offset += weHave;
- cq->bytes_out += weHave;
+ chunkqueue_mark_written(cq, weHave);
break;
- case UNUSED_CHUNK:
- break;
}
- chunkqueue_remove_finished_chunks(cq);
}
-
switch ((err = xmlParseChunk(ctxt, 0, 0, 1))) {
case XML_ERR_DOCUMENT_END:
case XML_ERR_OK:
@@ -1096,7 +1221,7 @@ static int webdav_parse_chunkqueue(server *srv, connec
static int webdav_lockdiscovery(server *srv, connection *con,
buffer *locktoken, const char *lockscope, const char *locktype, int depth) {
- buffer *b;
+ buffer *b = buffer_init();
response_header_overwrite(srv, con, CONST_STR_LEN("Lock-Token"), CONST_BUF_LEN(locktoken));
@@ -1104,47 +1229,17 @@ static int webdav_lockdiscovery(server *srv, connectio
CONST_STR_LEN("Content-Type"),
CONST_STR_LEN("text/xml; charset=\"utf-8\""));
- b = chunkqueue_get_append_buffer(con->write_queue);
-
buffer_copy_string_len(b, CONST_STR_LEN("\n"));
buffer_append_string_len(b,CONST_STR_LEN("\n"));
buffer_append_string_len(b,CONST_STR_LEN("\n"));
- buffer_append_string_len(b,CONST_STR_LEN("\n"));
-
- buffer_append_string_len(b,CONST_STR_LEN(""));
- buffer_append_string_len(b,CONST_STR_LEN(""));
- buffer_append_string_len(b,CONST_STR_LEN("\n"));
-
- buffer_append_string_len(b,CONST_STR_LEN(""));
- buffer_append_string_len(b,CONST_STR_LEN(""));
- buffer_append_string_len(b,CONST_STR_LEN("\n"));
-
- buffer_append_string_len(b,CONST_STR_LEN(""));
- buffer_append_string(b, depth == 0 ? "0" : "infinity");
- buffer_append_string_len(b,CONST_STR_LEN("\n"));
-
- buffer_append_string_len(b,CONST_STR_LEN(""));
- buffer_append_string_len(b, CONST_STR_LEN("Second-600"));
- buffer_append_string_len(b,CONST_STR_LEN("\n"));
-
- buffer_append_string_len(b,CONST_STR_LEN(""));
- buffer_append_string_len(b,CONST_STR_LEN("\n"));
-
- buffer_append_string_len(b,CONST_STR_LEN(""));
- buffer_append_string_len(b, CONST_STR_LEN(""));
- buffer_append_string_buffer(b, locktoken);
- buffer_append_string_len(b, CONST_STR_LEN(""));
- buffer_append_string_len(b,CONST_STR_LEN("\n"));
-
- buffer_append_string_len(b,CONST_STR_LEN("\n"));
+ webdav_activelock(b, locktoken, lockscope, locktype, depth, 600);
buffer_append_string_len(b,CONST_STR_LEN("\n"));
buffer_append_string_len(b,CONST_STR_LEN("\n"));
+ chunkqueue_append_buffer(con->write_queue, b);
+ buffer_free(b);
+
return 0;
}
#endif
@@ -1191,8 +1286,8 @@ static int webdav_has_lock(server *srv, connection *co
sqlite3_reset(stmt);
sqlite3_bind_text(stmt, 1,
- CONST_BUF_LEN(uri),
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(uri),
+ SQLITE_TRANSIENT);
while (SQLITE_ROW == sqlite3_step(stmt)) {
has_lock = 0;
@@ -1208,12 +1303,13 @@ static int webdav_has_lock(server *srv, connection *co
return has_lock;
}
-URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
+
+SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) {
plugin_data *p = p_d;
buffer *b;
DIR *dir;
data_string *ds;
- int depth = -1;
+ int depth = -1; /* (Depth: infinity) */
struct stat st;
buffer *prop_200;
buffer *prop_404;
@@ -1224,12 +1320,16 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
if (!p->conf.enabled) return HANDLER_GO_ON;
/* physical path is setup */
- if (con->physical.path->used == 0) return HANDLER_GO_ON;
+ if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
/* PROPFIND need them */
- if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Depth"))) {
- depth = strtol(ds->value->ptr, NULL, 10);
- }
+ if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Depth")) && 1 == buffer_string_length(ds->value)) {
+ if ('0' == *ds->value->ptr) {
+ depth = 0;
+ } else if ('1' == *ds->value->ptr) {
+ depth = 1;
+ }
+ } /* else treat as Depth: infinity */
switch (con->request.http_method) {
case HTTP_METHOD_PROPFIND:
@@ -1249,12 +1349,21 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
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 ? */
if (con->request.content_length) {
xmlDocPtr xml;
+ if (con->state == CON_STATE_READ_POST) {
+ handler_t r = connection_handle_read_post_state(srv, con);
+ if (r != HANDLER_GO_ON) return r;
+ }
+
if (1 == webdav_parse_chunkqueue(srv, con, p, con->request_content_queue, &xml)) {
xmlNode *rootnode = xmlDocGetRootElement(xml);
@@ -1275,7 +1384,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
if (prop->type == XML_TEXT_NODE) continue; /* ignore WS */
if (prop->ns &&
- (0 == xmlStrcmp(prop->ns->href, BAD_CAST "")) &&
+ (0 == xmlStrcmp(prop->ns->href, BAD_CAST "")) &&
(0 != xmlStrcmp(prop->ns->prefix, BAD_CAST ""))) {
size_t i;
log_error_write(srv, __FILE__, __LINE__, "ss",
@@ -1319,9 +1428,8 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
/* bind the values to the insert */
sqlite3_bind_text(stmt, 1,
- con->uri.path->ptr,
- con->uri.path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(con->uri.path),
+ SQLITE_TRANSIENT);
if (SQLITE_DONE != sqlite3_step(stmt)) {
}
@@ -1343,7 +1451,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\""));
- b = chunkqueue_get_append_buffer(con->write_queue);
+ b = buffer_init();
buffer_copy_string_len(b, CONST_STR_LEN("\n"));
@@ -1354,9 +1462,8 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
prop_200 = buffer_init();
prop_404 = buffer_init();
- switch(depth) {
- case 0:
- /* Depth: 0 */
+ {
+ /* Depth: 0 or Depth: 1 */
webdav_get_props(srv, con, p, &(con->physical), req_props, prop_200, prop_404);
buffer_append_string_len(b,CONST_STR_LEN("\n"));
@@ -1367,7 +1474,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
buffer_append_string_encoded(b, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI);
buffer_append_string_len(b,CONST_STR_LEN("\n"));
- if (!buffer_is_empty(prop_200)) {
+ if (!buffer_string_is_empty(prop_200)) {
buffer_append_string_len(b,CONST_STR_LEN("\n"));
buffer_append_string_len(b,CONST_STR_LEN("\n"));
@@ -1379,7 +1486,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
buffer_append_string_len(b,CONST_STR_LEN("\n"));
}
- if (!buffer_is_empty(prop_404)) {
+ if (!buffer_string_is_empty(prop_404)) {
buffer_append_string_len(b,CONST_STR_LEN("\n"));
buffer_append_string_len(b,CONST_STR_LEN("\n"));
@@ -1393,9 +1500,10 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
}
buffer_append_string_len(b,CONST_STR_LEN("\n"));
+ }
- break;
- case 1:
+ if (depth == 1) {
+
if (NULL != (dir = opendir(con->physical.path->ptr))) {
struct dirent *de;
physical d;
@@ -1405,23 +1513,19 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
d.rel_path = buffer_init();
while(NULL != (de = readdir(dir))) {
- if (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0') {
+ if (de->d_name[0] == '.' && (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))) {
continue;
- /* ignore the parent dir */
+ /* ignore the parent and target dir */
}
- buffer_copy_string_buffer(d.path, dst->path);
- BUFFER_APPEND_SLASH(d.path);
+ buffer_copy_buffer(d.path, dst->path);
+ buffer_append_slash(d.path);
- buffer_copy_string_buffer(d.rel_path, dst->rel_path);
- BUFFER_APPEND_SLASH(d.rel_path);
+ buffer_copy_buffer(d.rel_path, dst->rel_path);
+ buffer_append_slash(d.rel_path);
- if (de->d_name[0] == '.' && de->d_name[1] == '\0') {
- /* don't append the . */
- } else {
- buffer_append_string(d.path, de->d_name);
- buffer_append_string(d.rel_path, de->d_name);
- }
+ buffer_append_string(d.path, de->d_name);
+ buffer_append_string(d.rel_path, de->d_name);
buffer_reset(prop_200);
buffer_reset(prop_404);
@@ -1434,9 +1538,13 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
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("\n"));
- if (!buffer_is_empty(prop_200)) {
+ if (!buffer_string_is_empty(prop_200)) {
buffer_append_string_len(b,CONST_STR_LEN("\n"));
buffer_append_string_len(b,CONST_STR_LEN("\n"));
@@ -1448,7 +1556,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
buffer_append_string_len(b,CONST_STR_LEN("\n"));
}
- if (!buffer_is_empty(prop_404)) {
+ if (!buffer_string_is_empty(prop_404)) {
buffer_append_string_len(b,CONST_STR_LEN("\n"));
buffer_append_string_len(b,CONST_STR_LEN("\n"));
@@ -1467,7 +1575,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
buffer_free(d.path);
buffer_free(d.rel_path);
}
- break;
+
}
if (req_props) {
@@ -1489,6 +1597,10 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
if (p->conf.log_xml) {
log_error_write(srv, __FILE__, __LINE__, "sb", "XML-response-body:", b);
}
+
+ chunkqueue_append_buffer(con->write_queue, b);
+ buffer_free(b);
+
con->file_finished = 1;
return HANDLER_FINISHED;
@@ -1551,13 +1663,20 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
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 */
response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\""));
- b = chunkqueue_get_append_buffer(con->write_queue);
+ b = buffer_init();
buffer_copy_string_len(b, CONST_STR_LEN("\n"));
@@ -1571,6 +1690,9 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
log_error_write(srv, __FILE__, __LINE__, "sb", "XML-response-body:", b);
}
+ chunkqueue_append_buffer(con->write_queue, b);
+ buffer_free(b);
+
con->http_status = 207;
con->file_finished = 1;
} else {
@@ -1578,6 +1700,9 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
if (-1 == rmdir(con->physical.path->ptr)) {
switch(errno) {
+ case EPERM:
+ con->http_status = 403;
+ break;
case ENOENT:
con->http_status = 404;
break;
@@ -1619,14 +1744,17 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
}
/* is a exclusive lock set on the source */
- if (!webdav_has_lock(srv, con, p, con->uri.path)) {
+ /* (check for lock once before potentially reading large input) */
+ if (0 == cq->bytes_in && !webdav_has_lock(srv, con, p, con->uri.path)) {
con->http_status = 423;
return HANDLER_FINISHED;
}
+ if (con->state == CON_STATE_READ_POST) {
+ handler_t r = connection_handle_read_post_state(srv, con);
+ if (r != HANDLER_GO_ON) return r;
+ }
- assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
-
/* RFC2616 Section 9.6 PUT requires us to send 501 on all Content-* we don't support
* - most important Content-Range
*
@@ -1708,12 +1836,19 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
for (c = cq->first; c; c = cq->first) {
int r = 0;
+ int mapped;
+ void *data;
+ size_t dlen;
/* copy all chunks */
switch(c->type) {
case FILE_CHUNK:
- if (c->file.mmap.start == MAP_FAILED) {
+ mapped = (c->file.mmap.start != MAP_FAILED);
+ dlen = c->file.length - c->offset;
+ if (mapped) {
+ data = c->file.mmap.start + c->offset;
+ } else {
if (-1 == c->file.fd && /* open the file if not already open */
-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
@@ -1721,24 +1856,30 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
return HANDLER_ERROR;
}
- if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
- log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
- strerror(errno), c->file.name, c->file.fd);
- close(c->file.fd);
- c->file.fd = -1;
- close(fd);
- return HANDLER_ERROR;
+ if (MAP_FAILED != (c->file.mmap.start = mmap(NULL, c->file.length, PROT_READ, MAP_PRIVATE, c->file.fd, 0))) {
+ /* chunk_reset() or chunk_free() will cleanup for us */
+ c->file.mmap.length = c->file.length;
+ data = c->file.mmap.start + c->offset;
+ mapped = 1;
+ } else {
+ ssize_t rd;
+ if (dlen > 65536) dlen = 65536;
+ data = malloc(dlen);
+ force_assert(data);
+ if (-1 == lseek(c->file.fd, c->file.start + c->offset, SEEK_SET)
+ || 0 > (rd = read(c->file.fd, data, dlen))) {
+ log_error_write(srv, __FILE__, __LINE__, "ssbd", "lseek/read failed: ",
+ strerror(errno), c->file.name, c->file.fd);
+ free(data);
+ close(fd);
+ return HANDLER_ERROR;
+ }
+ dlen = (size_t)rd;
}
- c->file.mmap.length = c->file.length;
-
- close(c->file.fd);
- c->file.fd = -1;
-
- /* chunk_reset() or chunk_free() will cleanup for us */
}
- if ((r = write(fd, c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
+ if ((r = write(fd, data, dlen)) < 0) {
switch(errno) {
case ENOSPC:
con->http_status = 507;
@@ -1749,9 +1890,11 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
break;
}
}
+
+ if (!mapped) free(data);
break;
case MEM_CHUNK:
- if ((r = write(fd, c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
+ if ((r = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) {
switch(errno) {
case ENOSPC:
con->http_status = 507;
@@ -1763,19 +1906,19 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
}
}
break;
- case UNUSED_CHUNK:
- break;
}
if (r > 0) {
- c->offset += r;
- cq->bytes_out += r;
+ chunkqueue_mark_written(cq, r);
} else {
break;
}
- chunkqueue_remove_finished_chunks(cq);
}
- close(fd);
+ if (0 != close(fd)) {
+ log_error_write(srv, __FILE__, __LINE__, "sbss",
+ "close ", con->physical.path, "failed: ", strerror(errno));
+ return HANDLER_ERROR;
+ }
return HANDLER_FINISHED;
}
@@ -1806,7 +1949,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
}
if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Overwrite"))) {
- if (ds->value->used != 2 ||
+ if (buffer_string_length(ds->value) != 1 ||
(ds->value->ptr[0] != 'F' &&
ds->value->ptr[0] != 'T') ) {
con->http_status = 400;
@@ -1862,27 +2005,74 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
return HANDLER_FINISHED;
}
- buffer_copy_string_buffer(p->tmp_buf, p->uri.path_raw);
+ buffer_copy_buffer(p->tmp_buf, p->uri.path_raw);
buffer_urldecode_path(p->tmp_buf);
buffer_path_simplify(p->uri.path, p->tmp_buf);
/* we now have a URI which is clean. transform it into a physical path */
- buffer_copy_string_buffer(p->physical.doc_root, con->physical.doc_root);
- buffer_copy_string_buffer(p->physical.rel_path, p->uri.path);
+ buffer_copy_buffer(p->physical.doc_root, con->physical.doc_root);
+ buffer_copy_buffer(p->physical.rel_path, p->uri.path);
if (con->conf.force_lowercase_filenames) {
buffer_to_lower(p->physical.rel_path);
}
- buffer_copy_string_buffer(p->physical.path, p->physical.doc_root);
- BUFFER_APPEND_SLASH(p->physical.path);
- buffer_copy_string_buffer(p->physical.basedir, p->physical.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);
- /* 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, p->physical.rel_path->used - 2);
- } else {
- buffer_append_string_buffer(p->physical.path, p->physical.rel_path);
+ 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
@@ -1900,13 +2090,20 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
}
} else if (S_ISDIR(st.st_mode)) {
int r;
+ int created = 0;
/* 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;
return HANDLER_FINISHED;
}
+ created = 1;
} else if (!S_ISDIR(st.st_mode)) {
if (overwrite == 0) {
/* copying into a non-dir ? */
@@ -1918,6 +2115,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
con->http_status = 403;
return HANDLER_FINISHED;
}
+ created = 1;
}
}
@@ -1933,11 +2131,12 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
rmdir(con->physical.path->ptr);
}
- con->http_status = 201;
+ con->http_status = created ? 201 : 204;
con->file_finished = 1;
} else {
/* it is just a file, good */
int r;
+ int destdir = 0;
/* does the client have a lock for this connection ? */
if (!webdav_has_lock(srv, con, p, p->uri.path)) {
@@ -1950,6 +2149,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
if (S_ISDIR(st.st_mode)) {
/* file to dir/
* append basename to physical path */
+ destdir = 1;
if (NULL != (sep = strrchr(con->physical.path->ptr, '/'))) {
buffer_append_string(p->physical.path, sep);
@@ -1959,7 +2159,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
}
if (-1 == r) {
- con->http_status = 201; /* we will create a new one */
+ con->http_status = destdir ? 204 : 201; /* we will create a new one */
con->file_finished = 1;
switch(errno) {
@@ -1982,22 +2182,6 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
#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,
- con->uri.path->ptr,
- con->uri.path->used - 1,
- 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) {
@@ -2005,14 +2189,12 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
/* bind the values to the insert */
sqlite3_bind_text(stmt, 1,
- p->uri.path->ptr,
- p->uri.path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(p->uri.path),
+ SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2,
- con->uri.path->ptr,
- con->uri.path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(con->uri.path),
+ SQLITE_TRANSIENT);
if (SQLITE_DONE != sqlite3_step(stmt)) {
log_error_write(srv, __FILE__, __LINE__, "ss", "sql-move failed:", sqlite3_errmsg(p->conf.sql));
@@ -2060,10 +2242,20 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
}
}
+ 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;
+ if (con->state == CON_STATE_READ_POST) {
+ handler_t r = connection_handle_read_post_state(srv, con);
+ if (r != HANDLER_GO_ON) return r;
+ }
+
if (1 == webdav_parse_chunkqueue(srv, con, p, con->request_content_queue, &xml)) {
xmlNode *rootnode = xmlDocGetRootElement(xml);
@@ -2097,6 +2289,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
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;
@@ -2117,35 +2310,40 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
/* bind the values to the insert */
sqlite3_bind_text(stmt, 1,
- con->uri.path->ptr,
- con->uri.path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(con->uri.path),
+ SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2,
- (char *)prop->name,
- strlen((char *)prop->name),
- SQLITE_TRANSIENT);
+ (char *)prop->name,
+ strlen((char *)prop->name),
+ SQLITE_TRANSIENT);
if (prop->ns) {
sqlite3_bind_text(stmt, 3,
- (char *)prop->ns->href,
- strlen((char *)prop->ns->href),
- SQLITE_TRANSIENT);
+ (char *)prop->ns->href,
+ strlen((char *)prop->ns->href),
+ SQLITE_TRANSIENT);
} else {
sqlite3_bind_text(stmt, 3,
- "",
- 0,
- SQLITE_TRANSIENT);
+ "",
+ 0,
+ 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)),
- SQLITE_TRANSIENT);
+ propval ? propval : "",
+ propval ? strlen(propval) : 0,
+ SQLITE_TRANSIENT);
}
if (SQLITE_DONE != (r = sqlite3_step(stmt))) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"sql-set failed:", sqlite3_errmsg(p->conf.sql));
}
+
+ if (propval) xmlFree(propval);
}
}
if (empty_ns) break;
@@ -2172,6 +2370,7 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
}
con->file_finished = 1;
+ xmlFreeDoc(xml);
return HANDLER_FINISHED;
}
@@ -2220,16 +2419,38 @@ propmatch_cleanup:
if (con->request.content_length) {
xmlDocPtr xml;
buffer *hdr_if = NULL;
+ int created = 0;
+ if (con->state == CON_STATE_READ_POST) {
+ handler_t r = connection_handle_read_post_state(srv, con);
+ if (r != HANDLER_GO_ON) return r;
+ }
+
if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) {
hdr_if = ds->value;
}
- /* we don't support Depth: Infinity on locks */
- if (hdr_if == NULL && depth == -1) {
- con->http_status = 409; /* Conflict */
+ if (0 != stat(con->physical.path->ptr, &st)) {
+ if (errno == ENOENT) {
+ int fd = open(con->physical.path->ptr, O_WRONLY|O_CREAT|O_APPEND|O_BINARY|FIFO_NONBLOCK, WEBDAV_FILE_MODE);
+ if (fd >= 0) {
+ close(fd);
+ created = 1;
+ } else {
+ log_error_write(srv, __FILE__, __LINE__, "sBss",
+ "create file", con->physical.path, ":", strerror(errno));
+ con->http_status = 403; /* Forbidden */
- return HANDLER_FINISHED;
+ return HANDLER_FINISHED;
+ }
+ }
+ } else if (hdr_if == NULL && depth == -1) {
+ /* we don't support Depth: Infinity on directories */
+ if (S_ISDIR(st.st_mode)) {
+ con->http_status = 409; /* Conflict */
+
+ return HANDLER_FINISHED;
+ }
}
if (1 == webdav_parse_chunkqueue(srv, con, p, con->request_content_queue, &xml)) {
@@ -2286,9 +2507,8 @@ propmatch_cleanup:
sqlite3_reset(stmt);
sqlite3_bind_text(stmt, 1,
- p->uri.path->ptr,
- p->uri.path->used - 1,
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(p->uri.path),
+ SQLITE_TRANSIENT);
/* it is the PK */
while (SQLITE_ROW == sqlite3_step(stmt)) {
@@ -2335,32 +2555,32 @@ propmatch_cleanup:
sqlite3_reset(stmt);
sqlite3_bind_text(stmt, 1,
- CONST_BUF_LEN(p->tmp_buf),
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(p->tmp_buf),
+ SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2,
- CONST_BUF_LEN(con->uri.path),
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(con->uri.path),
+ SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3,
- (const char *)lockscope,
- xmlStrlen(lockscope),
- SQLITE_TRANSIENT);
+ (const char *)lockscope,
+ xmlStrlen(lockscope),
+ SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 4,
- (const char *)locktype,
- xmlStrlen(locktype),
- SQLITE_TRANSIENT);
+ (const char *)locktype,
+ xmlStrlen(locktype),
+ SQLITE_TRANSIENT);
/* owner */
sqlite3_bind_text(stmt, 5,
- "",
- 0,
- SQLITE_TRANSIENT);
+ "",
+ 0,
+ SQLITE_TRANSIENT);
/* depth */
sqlite3_bind_int(stmt, 6,
- depth);
+ depth);
if (SQLITE_DONE != sqlite3_step(stmt)) {
@@ -2371,7 +2591,7 @@ propmatch_cleanup:
/* looks like we survived */
webdav_lockdiscovery(srv, con, p->tmp_buf, (const char *)lockscope, (const char *)locktype, depth);
- con->http_status = 201;
+ con->http_status = created ? 201 : 200;
con->file_finished = 1;
}
}
@@ -2390,19 +2610,19 @@ propmatch_cleanup:
sqlite3_stmt *stmt = p->conf.stmt_refresh_lock;
/* remove the < > around the token */
- if (locktoken->used < 6) {
+ if (buffer_string_length(locktoken) < 5) {
con->http_status = 400;
return HANDLER_FINISHED;
}
- buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 2, locktoken->used - 5);
+ buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 2, buffer_string_length(locktoken) - 4);
sqlite3_reset(stmt);
sqlite3_bind_text(stmt, 1,
- CONST_BUF_LEN(p->tmp_buf),
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(p->tmp_buf),
+ SQLITE_TRANSIENT);
if (SQLITE_DONE != sqlite3_step(stmt)) {
log_error_write(srv, __FILE__, __LINE__, "ss",
@@ -2433,7 +2653,7 @@ propmatch_cleanup:
sqlite3_stmt *stmt = p->conf.stmt_remove_lock;
/* remove the < > around the token */
- if (locktoken->used < 4) {
+ if (buffer_string_length(locktoken) < 3) {
con->http_status = 400;
return HANDLER_FINISHED;
@@ -2449,18 +2669,14 @@ propmatch_cleanup:
* - 412
* */
- buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 1, locktoken->used - 3);
+ buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 1, buffer_string_length(locktoken) - 2);
sqlite3_reset(stmt);
sqlite3_bind_text(stmt, 1,
- CONST_BUF_LEN(p->tmp_buf),
- SQLITE_TRANSIENT);
+ CONST_BUF_LEN(p->tmp_buf),
+ SQLITE_TRANSIENT);
- sqlite3_bind_text(stmt, 2,
- CONST_BUF_LEN(con->uri.path),
- SQLITE_TRANSIENT);
-
if (SQLITE_DONE != sqlite3_step(stmt)) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"remove lock:", sqlite3_errmsg(p->conf.sql));
@@ -2492,6 +2708,47 @@ propmatch_cleanup:
}
+SUBREQUEST_FUNC(mod_webdav_subrequest_handler) {
+ handler_t r;
+ plugin_data *p = p_d;
+ if (con->mode != p->id) return HANDLER_GO_ON;
+
+ r = mod_webdav_subrequest_handler_huge(srv, con, p_d);
+ if (con->http_status >= 400) con->mode = DIRECT;
+ return r;
+}
+
+
+PHYSICALPATH_FUNC(mod_webdav_physical_handler) {
+ plugin_data *p = p_d;
+ if (!p->conf.enabled) return HANDLER_GO_ON;
+
+ /* physical path is setup */
+ if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
+
+ UNUSED(srv);
+
+ switch (con->request.http_method) {
+ case HTTP_METHOD_PROPFIND:
+ case HTTP_METHOD_PROPPATCH:
+ case HTTP_METHOD_PUT:
+ case HTTP_METHOD_COPY:
+ case HTTP_METHOD_MOVE:
+ case HTTP_METHOD_MKCOL:
+ case HTTP_METHOD_DELETE:
+ case HTTP_METHOD_LOCK:
+ case HTTP_METHOD_UNLOCK:
+ con->conf.stream_request_body = 0;
+ con->mode = p->id;
+ break;
+ default:
+ break;
+ }
+
+ return HANDLER_GO_ON;
+}
+
+
/* this function is called at dlopen() time and inits the callbacks */
int mod_webdav_plugin_init(plugin *p);
@@ -2501,7 +2758,8 @@ int mod_webdav_plugin_init(plugin *p) {
p->init = mod_webdav_init;
p->handle_uri_clean = mod_webdav_uri_handler;
- p->handle_physical = mod_webdav_subrequest_handler;
+ p->handle_physical = mod_webdav_physical_handler;
+ p->handle_subrequest = mod_webdav_subrequest_handler;
p->set_defaults = mod_webdav_set_defaults;
p->cleanup = mod_webdav_free;