58
64
sqlite3_stmt *stmt_delete_prop;
59
65
sqlite3_stmt *stmt_select_prop;
60
66
sqlite3_stmt *stmt_select_propnames;
62
68
sqlite3_stmt *stmt_delete_uri;
63
69
sqlite3_stmt *stmt_move_uri;
64
70
sqlite3_stmt *stmt_copy_uri;
72
sqlite3_stmt *stmt_remove_lock;
73
sqlite3_stmt *stmt_create_lock;
74
sqlite3_stmt *stmt_read_lock;
75
sqlite3_stmt *stmt_read_lock_by_uri;
76
sqlite3_stmt *stmt_refresh_lock;
75
87
plugin_config **config_storage;
80
92
/* init the plugin data */
81
93
INIT_FUNC(mod_webdav_init) {
84
96
p = calloc(1, sizeof(*p));
86
98
p->tmp_buf = buffer_init();
88
100
p->uri.scheme = buffer_init();
89
101
p->uri.path_raw = buffer_init();
90
102
p->uri.path = buffer_init();
91
103
p->uri.authority = buffer_init();
93
105
p->physical.path = buffer_init();
94
106
p->physical.rel_path = buffer_init();
95
107
p->physical.doc_root = buffer_init();
96
108
p->physical.basedir = buffer_init();
101
113
/* detroy the plugin data */
102
114
FREE_FUNC(mod_webdav_free) {
103
115
plugin_data *p = p_d;
107
119
if (!p) return HANDLER_GO_ON;
109
121
if (p->config_storage) {
111
123
for (i = 0; i < srv->config_context->used; i++) {
112
124
plugin_config *s = p->config_storage[i];
114
126
if (!s) continue;
116
128
buffer_free(s->sqlite_db_name);
117
129
#ifdef USE_PROPPATCH
119
131
sqlite3_finalize(s->stmt_delete_prop);
120
132
sqlite3_finalize(s->stmt_delete_uri);
121
133
sqlite3_finalize(s->stmt_copy_uri);
153
171
SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
154
172
plugin_data *p = p_d;
157
config_values_t cv[] = {
175
config_values_t cv[] = {
158
176
{ "webdav.activate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
159
177
{ "webdav.is-readonly", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
160
178
{ "webdav.sqlite-db-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
161
179
{ "webdav.log-xml", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
162
180
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
165
183
if (!p) return HANDLER_ERROR;
167
185
p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
169
187
for (i = 0; i < srv->config_context->used; i++) {
170
188
plugin_config *s;
172
190
s = calloc(1, sizeof(plugin_config));
173
191
s->sqlite_db_name = buffer_init();
175
193
cv[0].destination = &(s->enabled);
176
194
cv[1].destination = &(s->is_readonly);
177
195
cv[2].destination = s->sqlite_db_name;
178
196
cv[3].destination = &(s->log_xml);
180
198
p->config_storage[i] = s;
182
200
if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
183
201
return HANDLER_ERROR;
229
229
sqlite3_free(err);
232
if (SQLITE_OK != sqlite3_prepare(s->sql,
233
CONST_STR_LEN("REPLACE INTO properties (resource, prop, ns, value) VALUES (?, ?, ?, ?)"),
232
if (SQLITE_OK != sqlite3_prepare(s->sql,
233
CONST_STR_LEN("SELECT value FROM properties WHERE resource = ? AND prop = ? AND ns = ?"),
234
&(s->stmt_select_prop), &next_stmt)) {
237
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql));
238
return HANDLER_ERROR;
241
if (SQLITE_OK != sqlite3_prepare(s->sql,
242
CONST_STR_LEN("SELECT ns, prop FROM properties WHERE resource = ?"),
243
&(s->stmt_select_propnames), &next_stmt)) {
246
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql));
247
return HANDLER_ERROR;
251
if (SQLITE_OK != sqlite3_prepare(s->sql,
252
CONST_STR_LEN("REPLACE INTO properties (resource, prop, ns, value) VALUES (?, ?, ?, ?)"),
234
253
&(s->stmt_update_prop), &next_stmt)) {
235
254
/* prepare failed */
265
284
return HANDLER_ERROR;
268
if (SQLITE_OK != sqlite3_prepare(s->sql,
269
CONST_STR_LEN("UPDATE properties SET resource = ? WHERE resource = ?"),
287
if (SQLITE_OK != sqlite3_prepare(s->sql,
288
CONST_STR_LEN("UPDATE properties SET resource = ? WHERE resource = ?"),
270
289
&(s->stmt_move_uri), &next_stmt)) {
271
290
/* prepare failed */
272
291
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
274
293
return HANDLER_ERROR;
298
if (SQLITE_OK != sqlite3_exec(s->sql,
299
"CREATE TABLE locks ("
300
" locktoken TEXT NOT NULL,"
301
" resource TEXT NOT NULL,"
302
" lockscope TEXT NOT NULL,"
303
" locktype TEXT NOT NULL,"
304
" owner TEXT NOT NULL,"
305
" depth INT NOT NULL,"
306
" timeout TIMESTAMP NOT NULL,"
307
" PRIMARY KEY(locktoken))",
310
if (0 != strcmp(err, "table locks already exists")) {
311
log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err);
314
return HANDLER_ERROR;
319
if (SQLITE_OK != sqlite3_prepare(s->sql,
320
CONST_STR_LEN("INSERT INTO locks (locktoken, resource, lockscope, locktype, owner, depth, timeout) VALUES (?,?,?,?,?,?, CURRENT_TIME + 600)"),
321
&(s->stmt_create_lock), &next_stmt)) {
323
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
325
return HANDLER_ERROR;
328
if (SQLITE_OK != sqlite3_prepare(s->sql,
329
CONST_STR_LEN("DELETE FROM locks WHERE locktoken = ?"),
330
&(s->stmt_remove_lock), &next_stmt)) {
332
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
334
return HANDLER_ERROR;
337
if (SQLITE_OK != sqlite3_prepare(s->sql,
338
CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout FROM locks WHERE locktoken = ?"),
339
&(s->stmt_read_lock), &next_stmt)) {
341
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
343
return HANDLER_ERROR;
346
if (SQLITE_OK != sqlite3_prepare(s->sql,
347
CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout FROM locks WHERE resource = ?"),
348
&(s->stmt_read_lock_by_uri), &next_stmt)) {
350
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
352
return HANDLER_ERROR;
355
if (SQLITE_OK != sqlite3_prepare(s->sql,
356
CONST_STR_LEN("UPDATE locks SET timeout = CURRENT_TIME + 600 WHERE locktoken = ?"),
357
&(s->stmt_refresh_lock), &next_stmt)) {
359
log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
361
return HANDLER_ERROR;
277
366
log_error_write(srv, __FILE__, __LINE__, "s", "Sorry, no sqlite3 and libxml2 support include, compile with --with-webdav-props");
278
367
return HANDLER_ERROR;
283
372
return HANDLER_GO_ON;
375
#define PATCH_OPTION(x) \
287
376
p->conf.x = s->x;
288
377
static int mod_webdav_patch_connection(server *srv, connection *con, plugin_data *p) {
290
379
plugin_config *s = p->config_storage[0];
381
PATCH_OPTION(enabled);
382
PATCH_OPTION(is_readonly);
383
PATCH_OPTION(log_xml);
296
385
#ifdef USE_PROPPATCH
298
PATCH(stmt_update_prop);
299
PATCH(stmt_delete_prop);
300
PATCH(stmt_select_prop);
301
PATCH(stmt_select_propnames);
303
PATCH(stmt_delete_uri);
304
PATCH(stmt_move_uri);
305
PATCH(stmt_copy_uri);
387
PATCH_OPTION(stmt_update_prop);
388
PATCH_OPTION(stmt_delete_prop);
389
PATCH_OPTION(stmt_select_prop);
390
PATCH_OPTION(stmt_select_propnames);
392
PATCH_OPTION(stmt_delete_uri);
393
PATCH_OPTION(stmt_move_uri);
394
PATCH_OPTION(stmt_copy_uri);
396
PATCH_OPTION(stmt_remove_lock);
397
PATCH_OPTION(stmt_refresh_lock);
398
PATCH_OPTION(stmt_create_lock);
399
PATCH_OPTION(stmt_read_lock);
400
PATCH_OPTION(stmt_read_lock_by_uri);
307
402
/* skip the first, the global context */
308
403
for (i = 1; i < srv->config_context->used; i++) {
309
404
data_config *dc = (data_config *)srv->config_context->data[i];
310
405
s = p->config_storage[i];
312
407
/* condition didn't match */
313
408
if (!config_check_cond(srv, con, dc)) continue;
315
410
/* merge config */
316
411
for (j = 0; j < dc->value->used; j++) {
317
412
data_unset *du = dc->value->data[j];
319
414
if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.activate"))) {
415
PATCH_OPTION(enabled);
321
416
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.is-readonly"))) {
417
PATCH_OPTION(is_readonly);
323
418
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.log-xml"))) {
419
PATCH_OPTION(log_xml);
325
420
} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.sqlite-db-name"))) {
326
421
#ifdef USE_PROPPATCH
328
PATCH(stmt_update_prop);
329
PATCH(stmt_delete_prop);
330
PATCH(stmt_select_prop);
331
PATCH(stmt_select_propnames);
333
PATCH(stmt_delete_uri);
334
PATCH(stmt_move_uri);
335
PATCH(stmt_copy_uri);
423
PATCH_OPTION(stmt_update_prop);
424
PATCH_OPTION(stmt_delete_prop);
425
PATCH_OPTION(stmt_select_prop);
426
PATCH_OPTION(stmt_select_propnames);
428
PATCH_OPTION(stmt_delete_uri);
429
PATCH_OPTION(stmt_move_uri);
430
PATCH_OPTION(stmt_copy_uri);
432
PATCH_OPTION(stmt_remove_lock);
433
PATCH_OPTION(stmt_refresh_lock);
434
PATCH_OPTION(stmt_create_lock);
435
PATCH_OPTION(stmt_read_lock);
436
PATCH_OPTION(stmt_read_lock_by_uri);
345
445
URIHANDLER_FUNC(mod_webdav_uri_handler) {
346
446
plugin_data *p = p_d;
350
450
if (con->uri.path->used == 0) return HANDLER_GO_ON;
352
452
mod_webdav_patch_connection(srv, con, p);
354
454
if (!p->conf.enabled) return HANDLER_GO_ON;
1090
int webdav_lockdiscovery(server *srv, connection *con,
1091
buffer *locktoken, const char *lockscope, const char *locktype, int depth) {
1095
response_header_overwrite(srv, con, CONST_STR_LEN("Lock-Token"), CONST_BUF_LEN(locktoken));
1097
response_header_overwrite(srv, con,
1098
CONST_STR_LEN("Content-Type"),
1099
CONST_STR_LEN("text/xml; charset=\"utf-8\""));
1101
b = chunkqueue_get_append_buffer(con->write_queue);
1103
buffer_copy_string(b, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
1105
buffer_append_string(b,"<D:prop xmlns:D=\"DAV:\" xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n");
1106
buffer_append_string(b,"<D:lockdiscovery>\n");
1107
buffer_append_string(b,"<D:activelock>\n");
1109
buffer_append_string(b,"<D:lockscope>");
1110
buffer_append_string(b,"<D:");
1111
buffer_append_string(b, lockscope);
1112
buffer_append_string(b, "/>");
1113
buffer_append_string(b,"</D:lockscope>\n");
1115
buffer_append_string(b,"<D:locktype>");
1116
buffer_append_string(b,"<D:");
1117
buffer_append_string(b, locktype);
1118
buffer_append_string(b, "/>");
1119
buffer_append_string(b,"</D:locktype>\n");
1121
buffer_append_string(b,"<D:depth>");
1122
buffer_append_string(b, depth == 0 ? "0" : "infinity");
1123
buffer_append_string(b,"</D:depth>\n");
1125
buffer_append_string(b,"<D:timeout>");
1126
buffer_append_string(b, "Second-600");
1127
buffer_append_string(b,"</D:timeout>\n");
1129
buffer_append_string(b,"<D:owner>");
1130
buffer_append_string(b,"</D:owner>\n");
1132
buffer_append_string(b,"<D:locktoken>");
1133
buffer_append_string(b, "<D:href>");
1134
buffer_append_string_buffer(b, locktoken);
1135
buffer_append_string(b, "</D:href>");
1136
buffer_append_string(b,"</D:locktoken>\n");
1138
buffer_append_string(b,"</D:activelock>\n");
1139
buffer_append_string(b,"</D:lockdiscovery>\n");
1140
buffer_append_string(b,"</D:prop>\n");
1145
* check if resource is having the right locks to access to resource
1150
int webdav_has_lock(server *srv, connection *con, plugin_data *p, buffer *uri) {
1161
* there is NOT, AND and OR
1162
* and a list can be tagged
1164
* (<lock-token>) is untagged
1165
* <tag> (<lock-token>) is tagged
1167
* as long as we don't handle collections it is simple. :)
1169
* X-Litmus: locks: 11 (owner_modify)
1170
* If: <http://127.0.0.1:1025/dav/litmus/lockme> (<opaquelocktoken:2165478d-0611-49c4-be92-e790d68a38f1>)
1172
* X-Litmus: locks: 16 (fail_cond_put)
1173
* If: (<DAV:no-lock> ["-1622396671"])
1175
if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) {
1177
/* we didn't provided a lock-token -> */
1178
/* if the resource is locked -> 423 */
1180
sqlite3_stmt *stmt = p->conf.stmt_read_lock_by_uri;
1182
sqlite3_reset(stmt);
1184
sqlite3_bind_text(stmt, 1,
1188
while (SQLITE_ROW == sqlite3_step(stmt)) {
994
1197
URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
995
1198
plugin_data *p = p_d;
1375
1596
case HTTP_METHOD_PUT: {
1377
1598
chunkqueue *cq = con->request_content_queue;
1600
data_string *ds_range;
1379
1602
if (p->conf.is_readonly) {
1380
1603
con->http_status = 403;
1381
1604
return HANDLER_FINISHED;
1607
/* is a exclusive lock set on the source */
1608
if (!webdav_has_lock(srv, con, p, con->uri.path)) {
1609
con->http_status = 423;
1610
return HANDLER_FINISHED;
1384
1614
assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
1386
/* taken what we have in the request-body and write it to a file */
1387
if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY|O_CREAT|O_TRUNC, 0600))) {
1388
/* we can't open the file */
1389
con->http_status = 403;
1616
/* RFC2616 Section 9.6 PUT requires us to send 501 on all Content-* we don't support
1617
* - most important Content-Range
1620
* Example: Content-Range: bytes 100-1037/1038 */
1622
if (NULL != (ds_range = (data_string *)array_get_element(con->request.headers, "Content-Range"))) {
1623
const char *num = ds_range->value->ptr;
1627
if (0 != strncmp(num, "bytes ", 6)) {
1628
con->http_status = 501; /* not implemented */
1630
return HANDLER_FINISHED;
1633
/* we only support <num>- ... */
1638
while (*num == ' ' || *num == '\t') num++;
1641
con->http_status = 501; /* not implemented */
1643
return HANDLER_FINISHED;
1646
offset = strtoll(num, &err, 10);
1648
if (*err != '-' || offset < 0) {
1649
con->http_status = 501; /* not implemented */
1651
return HANDLER_FINISHED;
1654
if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY, 0600))) {
1657
con->http_status = 404; /* not found */
1660
con->http_status = 403; /* not found */
1663
return HANDLER_FINISHED;
1666
if (-1 == lseek(fd, offset, SEEK_SET)) {
1667
con->http_status = 501; /* not implemented */
1671
return HANDLER_FINISHED;
1673
con->http_status = 200; /* modified */
1393
con->http_status = 201; /* created */
1394
con->file_finished = 1;
1396
for (c = cq->first; c; c = cq->first) {
1399
/* copy all chunks */
1403
if (c->file.mmap.start == MAP_FAILED) {
1404
if (-1 == c->file.fd && /* open the file if not already open */
1405
-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
1406
log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
1411
if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
1412
log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
1413
strerror(errno), c->file.name, c->file.fd);
1418
c->file.mmap.length = c->file.length;
1423
/* chunk_reset() or chunk_free() will cleanup for us */
1426
if ((r = write(fd, c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
1429
con->http_status = 507;
1433
con->http_status = 403;
1439
if ((r = write(fd, c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
1442
con->http_status = 507;
1446
con->http_status = 403;
1675
/* take what we have in the request-body and write it to a file */
1677
/* if the file doesn't exist, create it */
1678
if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY|O_TRUNC, 0600))) {
1679
if (errno == ENOENT &&
1680
-1 == (fd = open(con->physical.path->ptr, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0600))) {
1681
/* we can't open the file */
1682
con->http_status = 403;
1684
return HANDLER_FINISHED;
1461
chunkqueue_remove_finished_chunks(cq);
1686
con->http_status = 201; /* created */
1689
con->http_status = 200; /* modified */
1693
con->file_finished = 1;
1695
for (c = cq->first; c; c = cq->first) {
1698
/* copy all chunks */
1702
if (c->file.mmap.start == MAP_FAILED) {
1703
if (-1 == c->file.fd && /* open the file if not already open */
1704
-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
1705
log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
1710
if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
1711
log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
1712
strerror(errno), c->file.name, c->file.fd);
1717
c->file.mmap.length = c->file.length;
1722
/* chunk_reset() or chunk_free() will cleanup for us */
1725
if ((r = write(fd, c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
1728
con->http_status = 507;
1732
con->http_status = 403;
1738
if ((r = write(fd, c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
1741
con->http_status = 507;
1745
con->http_status = 403;
1760
chunkqueue_remove_finished_chunks(cq);
1466
1764
return HANDLER_FINISHED;
1468
case HTTP_METHOD_MOVE:
1766
case HTTP_METHOD_MOVE:
1469
1767
case HTTP_METHOD_COPY: {
1470
1768
buffer *destination = NULL;
1471
1769
char *sep, *start;
1763
2080
/* bind the values to the insert */
1765
sqlite3_bind_text(stmt, 1,
2082
sqlite3_bind_text(stmt, 1,
1767
2084
con->uri.path->used - 1,
1768
2085
SQLITE_TRANSIENT);
1769
sqlite3_bind_text(stmt, 2,
2086
sqlite3_bind_text(stmt, 2,
1770
2087
(char *)prop->name,
1771
2088
strlen((char *)prop->name),
1772
2089
SQLITE_TRANSIENT);
1773
2090
if (prop->ns) {
1774
sqlite3_bind_text(stmt, 3,
2091
sqlite3_bind_text(stmt, 3,
1775
2092
(char *)prop->ns->href,
1776
2093
strlen((char *)prop->ns->href),
1777
2094
SQLITE_TRANSIENT);
1779
sqlite3_bind_text(stmt, 3,
2096
sqlite3_bind_text(stmt, 3,
1782
2099
SQLITE_TRANSIENT);
1784
2101
if (stmt == p->conf.stmt_update_prop) {
1785
sqlite3_bind_text(stmt, 4,
2102
sqlite3_bind_text(stmt, 4,
1786
2103
(char *)xmlNodeGetContent(prop),
1787
2104
strlen((char *)xmlNodeGetContent(prop)),
1788
2105
SQLITE_TRANSIENT);
1791
2108
if (SQLITE_DONE != (r = sqlite3_step(stmt))) {
1792
log_error_write(srv, __FILE__, __LINE__, "ss", "sql-set failed:", sqlite3_errmsg(p->conf.sql));
2109
log_error_write(srv, __FILE__, __LINE__, "ss",
2110
"sql-set failed:", sqlite3_errmsg(p->conf.sql));
1823
2141
propmatch_cleanup:
1826
con->http_status = 400;
1827
return HANDLER_FINISHED;
1831
con->http_status = 501;
1832
return HANDLER_FINISHED;
2145
con->http_status = 400;
2146
return HANDLER_FINISHED;
2150
con->http_status = 501;
2151
return HANDLER_FINISHED;
2152
case HTTP_METHOD_LOCK:
2154
* a mac wants to write
2156
* LOCK /dav/expire.txt HTTP/1.1\r\n
2157
* User-Agent: WebDAVFS/1.3 (01308000) Darwin/8.1.0 (Power Macintosh)\r\n
2160
* Timeout: Second-600\r\n
2161
* Content-Type: text/xml; charset=\"utf-8\"\r\n
2162
* Content-Length: 229\r\n
2163
* Connection: keep-alive\r\n
2164
* Host: 192.168.178.23:1025\r\n
2166
* <?xml version=\"1.0\" encoding=\"utf-8\"?>\n
2167
* <D:lockinfo xmlns:D=\"DAV:\">\n
2168
* <D:lockscope><D:exclusive/></D:lockscope>\n
2169
* <D:locktype><D:write/></D:locktype>\n
2171
* <D:href>http://www.apple.com/webdav_fs/</D:href>\n
2176
if (depth != 0 && depth != -1) {
2177
con->http_status = 400;
2179
return HANDLER_FINISHED;
2183
if (con->request.content_length) {
2185
buffer *hdr_if = NULL;
2187
if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) {
2191
/* we don't support Depth: Infinity on locks */
2192
if (hdr_if == NULL && depth == -1) {
2193
con->http_status = 409; /* Conflict */
2195
return HANDLER_FINISHED;
2198
if (1 == webdav_parse_chunkqueue(srv, con, p, con->request_content_queue, &xml)) {
2199
xmlNode *rootnode = xmlDocGetRootElement(xml);
2203
if (0 == xmlStrcmp(rootnode->name, BAD_CAST "lockinfo")) {
2205
const xmlChar *lockscope = NULL, *locktype = NULL, *owner = NULL;
2207
for (lockinfo = rootnode->children; lockinfo; lockinfo = lockinfo->next) {
2208
if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "lockscope")) {
2210
for (value = lockinfo->children; value; value = value->next) {
2211
if ((0 == xmlStrcmp(value->name, BAD_CAST "exclusive")) ||
2212
(0 == xmlStrcmp(value->name, BAD_CAST "shared"))) {
2213
lockscope = value->name;
2215
con->http_status = 400;
2218
return HANDLER_FINISHED;
2221
} else if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "locktype")) {
2223
for (value = lockinfo->children; value; value = value->next) {
2224
if ((0 == xmlStrcmp(value->name, BAD_CAST "write"))) {
2225
locktype = value->name;
2227
con->http_status = 400;
2230
return HANDLER_FINISHED;
2234
} else if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "owner")) {
2238
if (lockscope && locktype) {
2239
sqlite3_stmt *stmt = p->conf.stmt_read_lock_by_uri;
2241
/* is this resourse already locked ? */
2243
/* SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout
2245
* WHERE resource = ? */
2249
sqlite3_reset(stmt);
2251
sqlite3_bind_text(stmt, 1,
2253
p->uri.path->used - 1,
2257
while (SQLITE_ROW == sqlite3_step(stmt)) {
2259
* 1. is it compatible ?
2261
char *sql_lockscope = (char *)sqlite3_column_text(stmt, 2);
2263
if (strcmp(sql_lockscope, "exclusive")) {
2264
con->http_status = 423;
2265
} else if (0 == xmlStrcmp(lockscope, BAD_CAST "exclusive")) {
2266
/* resourse is locked with a shared lock
2267
* client wants exclusive */
2268
con->http_status = 423;
2271
if (con->http_status == 423) {
2273
return HANDLER_FINISHED;
2277
stmt = p->conf.stmt_create_lock;
2279
/* create a lock-token */
2281
char uuid[37] /* 36 + \0 */;
2284
uuid_unparse(id, uuid);
2286
buffer_copy_string(p->tmp_buf, "opaquelocktoken:");
2287
buffer_append_string(p->tmp_buf, uuid);
2289
/* "CREATE TABLE locks ("
2290
* " locktoken TEXT NOT NULL,"
2291
* " resource TEXT NOT NULL,"
2292
* " lockscope TEXT NOT NULL,"
2293
* " locktype TEXT NOT NULL,"
2294
* " owner TEXT NOT NULL,"
2295
* " depth INT NOT NULL,"
2298
sqlite3_reset(stmt);
2300
sqlite3_bind_text(stmt, 1,
2301
CONST_BUF_LEN(p->tmp_buf),
2304
sqlite3_bind_text(stmt, 2,
2305
CONST_BUF_LEN(con->uri.path),
2308
sqlite3_bind_text(stmt, 3,
2310
xmlStrlen(lockscope),
2313
sqlite3_bind_text(stmt, 4,
2315
xmlStrlen(locktype),
2319
sqlite3_bind_text(stmt, 5,
2325
sqlite3_bind_int(stmt, 6,
2329
if (SQLITE_DONE != sqlite3_step(stmt)) {
2330
log_error_write(srv, __FILE__, __LINE__, "ss",
2331
"create lock:", sqlite3_errmsg(p->conf.sql));
2334
/* looks like we survived */
2335
webdav_lockdiscovery(srv, con, p->tmp_buf, lockscope, locktype, depth);
2337
con->http_status = 201;
2338
con->file_finished = 1;
2344
return HANDLER_FINISHED;
2346
con->http_status = 400;
2347
return HANDLER_FINISHED;
2351
if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) {
2352
buffer *locktoken = ds->value;
2353
sqlite3_stmt *stmt = p->conf.stmt_refresh_lock;
2355
/* remove the < > around the token */
2356
if (locktoken->used < 6) {
2357
con->http_status = 400;
2359
return HANDLER_FINISHED;
2362
buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 2, locktoken->used - 5);
2364
sqlite3_reset(stmt);
2366
sqlite3_bind_text(stmt, 1,
2367
CONST_BUF_LEN(p->tmp_buf),
2370
if (SQLITE_DONE != sqlite3_step(stmt)) {
2371
log_error_write(srv, __FILE__, __LINE__, "ss",
2372
"refresh lock:", sqlite3_errmsg(p->conf.sql));
2375
webdav_lockdiscovery(srv, con, p->tmp_buf, "exclusive", "write", 0);
2377
con->http_status = 200;
2378
con->file_finished = 1;
2379
return HANDLER_FINISHED;
2381
/* we need a lock-token to refresh */
2382
con->http_status = 400;
2384
return HANDLER_FINISHED;
2389
con->http_status = 501;
2390
return HANDLER_FINISHED;
2392
case HTTP_METHOD_UNLOCK:
2394
if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Lock-Token"))) {
2395
buffer *locktoken = ds->value;
2396
sqlite3_stmt *stmt = p->conf.stmt_remove_lock;
2398
/* remove the < > around the token */
2399
if (locktoken->used < 4) {
2400
con->http_status = 400;
2402
return HANDLER_FINISHED;
2408
* if the resourse is locked:
2410
* - by someone else: 401
2411
* if the resource is not locked:
2415
buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 1, locktoken->used - 3);
2417
sqlite3_reset(stmt);
2419
sqlite3_bind_text(stmt, 1,
2420
CONST_BUF_LEN(p->tmp_buf),
2423
sqlite3_bind_text(stmt, 2,
2424
CONST_BUF_LEN(con->uri.path),
2427
if (SQLITE_DONE != sqlite3_step(stmt)) {
2428
log_error_write(srv, __FILE__, __LINE__, "ss",
2429
"remove lock:", sqlite3_errmsg(p->conf.sql));
2432
if (0 == sqlite3_changes(p->conf.sql)) {
2433
con->http_status = 401;
2435
con->http_status = 204;
2437
return HANDLER_FINISHED;
2439
/* we need a lock-token to unlock */
2440
con->http_status = 400;
2442
return HANDLER_FINISHED;
2446
con->http_status = 501;
2447
return HANDLER_FINISHED;
1838
2453
/* not found */
1839
2454
return HANDLER_GO_ON;