~ubuntu-branches/ubuntu/hardy/lighttpd/hardy

« back to all changes in this revision

Viewing changes to src/mod_webdav.c

  • Committer: Bazaar Package Importer
  • Author(s): Jeremie Corbier
  • Date: 2006-09-22 19:16:08 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20060922191608-i9jngvf1wtf3j5rd
Tags: 1.4.12~20060907-1ubuntu1
Merge from debian unstable:
-> Keep the additional dependency on libterm-readline-perl-perl.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
#include <ctype.h>
4
4
#include <stdlib.h>
5
5
#include <string.h>
6
 
#include <dirent.h>
7
6
#include <errno.h>
8
 
#include <unistd.h>
9
7
#include <fcntl.h>
10
8
#include <stdio.h>
11
9
#include <assert.h>
12
 
#include <sys/mman.h>
 
10
 
 
11
#include <unistd.h>
 
12
#include <dirent.h>
13
13
 
14
14
#ifdef HAVE_CONFIG_H
15
15
#include "config.h"
23
23
#include <sqlite3.h>
24
24
#endif
25
25
 
 
26
#if defined(HAVE_LIBXML_H) && defined(HAVE_SQLITE3_H) && defined(HAVE_UUID_H)
 
27
#define USE_LOCKS
 
28
#include <uuid/uuid.h>
 
29
#endif
 
30
 
26
31
#include "base.h"
27
32
#include "log.h"
28
33
#include "buffer.h"
33
38
#include "stream.h"
34
39
#include "stat_cache.h"
35
40
 
 
41
#include "sys-mmap.h"
36
42
 
37
43
/**
38
44
 * this is a webdav for a lighttpd plugin
39
45
 *
40
 
 * at least a very basic one. 
 
46
 * at least a very basic one.
41
47
 * - for now it is read-only and we only support PROPFIND
42
 
 * 
 
48
 *
43
49
 */
44
50
 
45
51
 
58
64
        sqlite3_stmt *stmt_delete_prop;
59
65
        sqlite3_stmt *stmt_select_prop;
60
66
        sqlite3_stmt *stmt_select_propnames;
61
 
        
 
67
 
62
68
        sqlite3_stmt *stmt_delete_uri;
63
69
        sqlite3_stmt *stmt_move_uri;
64
70
        sqlite3_stmt *stmt_copy_uri;
 
71
 
 
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;
65
77
#endif
66
78
} plugin_config;
67
79
 
68
80
typedef struct {
69
81
        PLUGIN_DATA;
70
 
        
 
82
 
71
83
        buffer *tmp_buf;
72
84
        request_uri uri;
73
85
        physical physical;
74
86
 
75
87
        plugin_config **config_storage;
76
 
        
77
 
        plugin_config conf; 
 
88
 
 
89
        plugin_config conf;
78
90
} plugin_data;
79
91
 
80
92
/* init the plugin data */
81
93
INIT_FUNC(mod_webdav_init) {
82
94
        plugin_data *p;
83
 
        
 
95
 
84
96
        p = calloc(1, sizeof(*p));
85
 
        
 
97
 
86
98
        p->tmp_buf = buffer_init();
87
99
 
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();
92
 
        
 
104
 
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();
97
 
        
 
109
 
98
110
        return p;
99
111
}
100
112
 
101
113
/* detroy the plugin data */
102
114
FREE_FUNC(mod_webdav_free) {
103
115
        plugin_data *p = p_d;
104
 
        
 
116
 
105
117
        UNUSED(srv);
106
118
 
107
119
        if (!p) return HANDLER_GO_ON;
108
 
        
 
120
 
109
121
        if (p->config_storage) {
110
122
                size_t i;
111
123
                for (i = 0; i < srv->config_context->used; i++) {
112
124
                        plugin_config *s = p->config_storage[i];
113
125
 
114
126
                        if (!s) continue;
115
 
        
 
127
 
116
128
                        buffer_free(s->sqlite_db_name);
117
129
#ifdef USE_PROPPATCH
118
 
                        if (s->sql) {   
 
130
                        if (s->sql) {
119
131
                                sqlite3_finalize(s->stmt_delete_prop);
120
132
                                sqlite3_finalize(s->stmt_delete_uri);
121
133
                                sqlite3_finalize(s->stmt_copy_uri);
123
135
                                sqlite3_finalize(s->stmt_update_prop);
124
136
                                sqlite3_finalize(s->stmt_select_prop);
125
137
                                sqlite3_finalize(s->stmt_select_propnames);
 
138
 
 
139
                                sqlite3_finalize(s->stmt_read_lock);
 
140
                                sqlite3_finalize(s->stmt_read_lock_by_uri);
 
141
                                sqlite3_finalize(s->stmt_create_lock);
 
142
                                sqlite3_finalize(s->stmt_remove_lock);
 
143
                                sqlite3_finalize(s->stmt_refresh_lock);
126
144
                                sqlite3_close(s->sql);
127
145
                        }
128
 
#endif  
 
146
#endif
129
147
                        free(s);
130
148
                }
131
149
                free(p->config_storage);
135
153
        buffer_free(p->uri.path_raw);
136
154
        buffer_free(p->uri.path);
137
155
        buffer_free(p->uri.authority);
138
 
        
 
156
 
139
157
        buffer_free(p->physical.path);
140
158
        buffer_free(p->physical.rel_path);
141
159
        buffer_free(p->physical.doc_root);
142
160
        buffer_free(p->physical.basedir);
143
 
        
 
161
 
144
162
        buffer_free(p->tmp_buf);
145
 
        
 
163
 
146
164
        free(p);
147
 
        
 
165
 
148
166
        return HANDLER_GO_ON;
149
167
}
150
168
 
153
171
SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
154
172
        plugin_data *p = p_d;
155
173
        size_t i = 0;
156
 
        
157
 
        config_values_t cv[] = { 
 
174
 
 
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 }
163
181
        };
164
 
        
 
182
 
165
183
        if (!p) return HANDLER_ERROR;
166
 
        
 
184
 
167
185
        p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
168
 
        
 
186
 
169
187
        for (i = 0; i < srv->config_context->used; i++) {
170
188
                plugin_config *s;
171
 
                
 
189
 
172
190
                s = calloc(1, sizeof(plugin_config));
173
191
                s->sqlite_db_name = buffer_init();
174
 
                
 
192
 
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);
179
 
                
 
197
 
180
198
                p->config_storage[i] = s;
181
 
        
 
199
 
182
200
                if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
183
201
                        return HANDLER_ERROR;
184
202
                }
193
211
                                return HANDLER_ERROR;
194
212
                        }
195
213
 
196
 
                        if (SQLITE_OK != sqlite3_prepare(s->sql, 
197
 
                                CONST_STR_LEN("SELECT value FROM properties WHERE resource = ? AND prop = ? AND ns = ?"), 
198
 
                                &(s->stmt_select_prop), &next_stmt)) {
199
 
                                /* prepare failed */
200
 
 
201
 
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql));
202
 
                                return HANDLER_ERROR;
203
 
                        }
204
 
 
205
 
                        if (SQLITE_OK != sqlite3_prepare(s->sql, 
206
 
                                CONST_STR_LEN("SELECT ns, prop FROM properties WHERE resource = ?"), 
207
 
                                &(s->stmt_select_propnames), &next_stmt)) {
208
 
                                /* prepare failed */
209
 
 
210
 
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql));
211
 
                                return HANDLER_ERROR;
212
 
                        }
213
 
 
214
 
                        if (SQLITE_OK != sqlite3_exec(s->sql, 
 
214
                        if (SQLITE_OK != sqlite3_exec(s->sql,
215
215
                                        "CREATE TABLE properties ("
216
216
                                        "  resource TEXT NOT NULL,"
217
217
                                        "  prop TEXT NOT NULL,"
228
228
                                }
229
229
                                sqlite3_free(err);
230
230
                        }
231
 
        
232
 
                        if (SQLITE_OK != sqlite3_prepare(s->sql, 
233
 
                                CONST_STR_LEN("REPLACE INTO properties (resource, prop, ns, value) VALUES (?, ?, ?, ?)"), 
 
231
 
 
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)) {
 
235
                                /* prepare failed */
 
236
 
 
237
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql));
 
238
                                return HANDLER_ERROR;
 
239
                        }
 
240
 
 
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)) {
 
244
                                /* prepare failed */
 
245
 
 
246
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql));
 
247
                                return HANDLER_ERROR;
 
248
                        }
 
249
 
 
250
 
 
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 */
236
255
 
238
257
                                return HANDLER_ERROR;
239
258
                        }
240
259
 
241
 
                        if (SQLITE_OK != sqlite3_prepare(s->sql, 
242
 
                                CONST_STR_LEN("DELETE FROM properties WHERE resource = ? AND prop = ? AND ns = ?"), 
 
260
                        if (SQLITE_OK != sqlite3_prepare(s->sql,
 
261
                                CONST_STR_LEN("DELETE FROM properties WHERE resource = ? AND prop = ? AND ns = ?"),
243
262
                                &(s->stmt_delete_prop), &next_stmt)) {
244
263
                                /* prepare failed */
245
264
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
247
266
                                return HANDLER_ERROR;
248
267
                        }
249
268
 
250
 
                        if (SQLITE_OK != sqlite3_prepare(s->sql, 
251
 
                                CONST_STR_LEN("DELETE FROM properties WHERE resource = ?"), 
 
269
                        if (SQLITE_OK != sqlite3_prepare(s->sql,
 
270
                                CONST_STR_LEN("DELETE FROM properties WHERE resource = ?"),
252
271
                                &(s->stmt_delete_uri), &next_stmt)) {
253
272
                                /* prepare failed */
254
273
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
256
275
                                return HANDLER_ERROR;
257
276
                        }
258
277
 
259
 
                        if (SQLITE_OK != sqlite3_prepare(s->sql, 
260
 
                                CONST_STR_LEN("INSERT INTO properties SELECT ?, prop, ns, value FROM properties WHERE resource = ?"), 
 
278
                        if (SQLITE_OK != sqlite3_prepare(s->sql,
 
279
                                CONST_STR_LEN("INSERT INTO properties SELECT ?, prop, ns, value FROM properties WHERE resource = ?"),
261
280
                                &(s->stmt_copy_uri), &next_stmt)) {
262
281
                                /* prepare failed */
263
282
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
265
284
                                return HANDLER_ERROR;
266
285
                        }
267
286
 
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));
273
292
 
274
293
                                return HANDLER_ERROR;
275
294
                        }
 
295
 
 
296
                        /* LOCKS */
 
297
 
 
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))",
 
308
                                        NULL, NULL, &err)) {
 
309
 
 
310
                                if (0 != strcmp(err, "table locks already exists")) {
 
311
                                        log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err);
 
312
                                        sqlite3_free(err);
 
313
 
 
314
                                        return HANDLER_ERROR;
 
315
                                }
 
316
                                sqlite3_free(err);
 
317
                        }
 
318
 
 
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)) {
 
322
                                /* prepare failed */
 
323
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
 
324
 
 
325
                                return HANDLER_ERROR;
 
326
                        }
 
327
 
 
328
                        if (SQLITE_OK != sqlite3_prepare(s->sql,
 
329
                                CONST_STR_LEN("DELETE FROM locks WHERE locktoken = ?"),
 
330
                                &(s->stmt_remove_lock), &next_stmt)) {
 
331
                                /* prepare failed */
 
332
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
 
333
 
 
334
                                return HANDLER_ERROR;
 
335
                        }
 
336
 
 
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)) {
 
340
                                /* prepare failed */
 
341
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
 
342
 
 
343
                                return HANDLER_ERROR;
 
344
                        }
 
345
 
 
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)) {
 
349
                                /* prepare failed */
 
350
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
 
351
 
 
352
                                return HANDLER_ERROR;
 
353
                        }
 
354
 
 
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)) {
 
358
                                /* prepare failed */
 
359
                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
 
360
 
 
361
                                return HANDLER_ERROR;
 
362
                        }
 
363
 
 
364
 
276
365
#else
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;
279
368
#endif
280
369
                }
281
370
        }
282
 
        
 
371
 
283
372
        return HANDLER_GO_ON;
284
373
}
285
374
 
286
 
#define PATCH(x) \
 
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) {
289
378
        size_t i, j;
290
379
        plugin_config *s = p->config_storage[0];
291
 
        
292
 
        PATCH(enabled);
293
 
        PATCH(is_readonly);
294
 
        PATCH(log_xml);
295
 
        
 
380
 
 
381
        PATCH_OPTION(enabled);
 
382
        PATCH_OPTION(is_readonly);
 
383
        PATCH_OPTION(log_xml);
 
384
 
296
385
#ifdef USE_PROPPATCH
297
 
        PATCH(sql);
298
 
        PATCH(stmt_update_prop);
299
 
        PATCH(stmt_delete_prop);
300
 
        PATCH(stmt_select_prop);
301
 
        PATCH(stmt_select_propnames);
302
 
 
303
 
        PATCH(stmt_delete_uri);
304
 
        PATCH(stmt_move_uri);
305
 
        PATCH(stmt_copy_uri);
 
386
        PATCH_OPTION(sql);
 
387
        PATCH_OPTION(stmt_update_prop);
 
388
        PATCH_OPTION(stmt_delete_prop);
 
389
        PATCH_OPTION(stmt_select_prop);
 
390
        PATCH_OPTION(stmt_select_propnames);
 
391
 
 
392
        PATCH_OPTION(stmt_delete_uri);
 
393
        PATCH_OPTION(stmt_move_uri);
 
394
        PATCH_OPTION(stmt_copy_uri);
 
395
 
 
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);
306
401
#endif
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];
311
 
                
 
406
 
312
407
                /* condition didn't match */
313
408
                if (!config_check_cond(srv, con, dc)) continue;
314
 
                
 
409
 
315
410
                /* merge config */
316
411
                for (j = 0; j < dc->value->used; j++) {
317
412
                        data_unset *du = dc->value->data[j];
318
 
                        
 
413
 
319
414
                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.activate"))) {
320
 
                                PATCH(enabled);
 
415
                                PATCH_OPTION(enabled);
321
416
                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.is-readonly"))) {
322
 
                                PATCH(is_readonly);
 
417
                                PATCH_OPTION(is_readonly);
323
418
                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.log-xml"))) {
324
 
                                PATCH(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
327
 
                                PATCH(sql);
328
 
                                PATCH(stmt_update_prop);
329
 
                                PATCH(stmt_delete_prop);
330
 
                                PATCH(stmt_select_prop);
331
 
                                PATCH(stmt_select_propnames);
332
 
                                
333
 
                                PATCH(stmt_delete_uri);
334
 
                                PATCH(stmt_move_uri);
335
 
                                PATCH(stmt_copy_uri);
 
422
                                PATCH_OPTION(sql);
 
423
                                PATCH_OPTION(stmt_update_prop);
 
424
                                PATCH_OPTION(stmt_delete_prop);
 
425
                                PATCH_OPTION(stmt_select_prop);
 
426
                                PATCH_OPTION(stmt_select_propnames);
 
427
 
 
428
                                PATCH_OPTION(stmt_delete_uri);
 
429
                                PATCH_OPTION(stmt_move_uri);
 
430
                                PATCH_OPTION(stmt_copy_uri);
 
431
 
 
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);
336
437
#endif
337
438
                        }
338
439
                }
339
440
        }
340
 
        
 
441
 
341
442
        return 0;
342
443
}
343
 
#undef PATCH
344
444
 
345
445
URIHANDLER_FUNC(mod_webdav_uri_handler) {
346
446
        plugin_data *p = p_d;
347
 
        
 
447
 
348
448
        UNUSED(srv);
349
449
 
350
450
        if (con->uri.path->used == 0) return HANDLER_GO_ON;
351
 
        
 
451
 
352
452
        mod_webdav_patch_connection(srv, con, p);
353
453
 
354
454
        if (!p->conf.enabled) return HANDLER_GO_ON;
362
462
                if (p->conf.is_readonly) {
363
463
                        response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND"));
364
464
                } else {
365
 
                        response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY, PROPPATCH"));
 
465
                        response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY, PROPPATCH, LOCK, UNLOCK"));
366
466
                }
367
467
                break;
368
468
        default:
369
469
                break;
370
470
        }
371
 
        
 
471
 
372
472
        /* not found */
373
473
        return HANDLER_GO_ON;
374
474
}
375
 
static int webdav_gen_prop_tag(server *srv, connection *con, 
376
 
                char *prop_name, 
377
 
                char *prop_ns, 
378
 
                char *value, 
 
475
static int webdav_gen_prop_tag(server *srv, connection *con,
 
476
                char *prop_name,
 
477
                char *prop_ns,
 
478
                char *value,
379
479
                buffer *b) {
380
480
 
381
481
        UNUSED(srv);
414
514
        buffer_append_string_buffer(b, dst->rel_path);
415
515
        buffer_append_string(b,"</D:href>\n");
416
516
        buffer_append_string(b,"<D:status>\n");
417
 
        
 
517
 
418
518
        if (con->request.http_version == HTTP_VERSION_1_1) {
419
519
                BUFFER_COPY_STRING_CONST(b, "HTTP/1.1 ");
420
520
        } else {
458
558
 
459
559
                        /* bind the values to the insert */
460
560
 
461
 
                        sqlite3_bind_text(stmt, 1, 
462
 
                                          dst->rel_path->ptr, 
 
561
                        sqlite3_bind_text(stmt, 1,
 
562
                                          dst->rel_path->ptr,
463
563
                                          dst->rel_path->used - 1,
464
564
                                          SQLITE_TRANSIENT);
465
 
                                                                        
 
565
 
466
566
                        if (SQLITE_DONE != sqlite3_step(stmt)) {
467
567
                                /* */
468
 
                                WP();
469
568
                        }
470
569
                }
471
570
#endif
493
592
                            (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) {
494
593
                                continue;
495
594
                                /* ignore the parent dir */
496
 
                        } 
 
595
                        }
497
596
 
498
597
                        buffer_copy_string_buffer(d.path, dst->path);
499
598
                        BUFFER_APPEND_SLASH(d.path);
500
599
                        buffer_append_string(d.path, de->d_name);
501
 
                        
 
600
 
502
601
                        buffer_copy_string_buffer(d.rel_path, dst->rel_path);
503
602
                        BUFFER_APPEND_SLASH(d.rel_path);
504
603
                        buffer_append_string(d.rel_path, de->d_name);
508
607
                                /* don't about it yet, rmdir will fail too */
509
608
                        } else if (S_ISDIR(st.st_mode)) {
510
609
                                have_multi_status = webdav_delete_dir(srv, con, p, &d, b);
511
 
                                        
 
610
 
512
611
                                /* try to unlink it */
513
612
                                if (-1 == rmdir(d.path->ptr)) {
514
613
                                        switch(errno) {
535
634
 
536
635
                                                /* bind the values to the insert */
537
636
 
538
 
                                                sqlite3_bind_text(stmt, 1, 
539
 
                                                                  d.rel_path->ptr, 
 
637
                                                sqlite3_bind_text(stmt, 1,
 
638
                                                                  d.rel_path->ptr,
540
639
                                                                  d.rel_path->used - 1,
541
640
                                                                  SQLITE_TRANSIENT);
542
 
                                                                                                        
 
641
 
543
642
                                                if (SQLITE_DONE != sqlite3_step(stmt)) {
544
643
                                                        /* */
545
 
                                                        WP();
546
644
                                                }
547
645
                                        }
548
646
#endif
569
667
        if (stream_open(&s, src->path)) {
570
668
                return 403;
571
669
        }
572
 
                        
 
670
 
573
671
        if (-1 == (ofd = open(dst->path->ptr, O_WRONLY|O_TRUNC|O_CREAT|(overwrite ? 0 : O_EXCL), 0600))) {
574
672
                /* opening the destination failed for some reason */
575
673
                switch(errno) {
601
699
                        break;
602
700
                }
603
701
        }
604
 
        
 
702
 
605
703
        stream_close(&s);
606
704
        close(ofd);
607
705
 
614
712
                        sqlite3_reset(stmt);
615
713
 
616
714
                        /* bind the values to the insert */
617
 
                        sqlite3_bind_text(stmt, 1, 
618
 
                                          dst->rel_path->ptr, 
 
715
                        sqlite3_bind_text(stmt, 1,
 
716
                                          dst->rel_path->ptr,
619
717
                                          dst->rel_path->used - 1,
620
718
                                          SQLITE_TRANSIENT);
621
719
 
622
 
                        sqlite3_bind_text(stmt, 2, 
623
 
                                          src->rel_path->ptr, 
 
720
                        sqlite3_bind_text(stmt, 2,
 
721
                                          src->rel_path->ptr,
624
722
                                          src->rel_path->used - 1,
625
723
                                          SQLITE_TRANSIENT);
626
 
                                                                                                        
 
724
 
627
725
                        if (SQLITE_DONE != sqlite3_step(stmt)) {
628
726
                                /* */
629
 
                                WP();
630
727
                        }
631
728
                }
632
729
        }
655
752
                            (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) {
656
753
                                continue;
657
754
                        }
658
 
                        
 
755
 
659
756
                        buffer_copy_string_buffer(s.path, src->path);
660
757
                        BUFFER_APPEND_SLASH(s.path);
661
758
                        buffer_append_string(s.path, de->d_name);
692
789
                                                sqlite3_reset(stmt);
693
790
 
694
791
                                                /* bind the values to the insert */
695
 
                                                sqlite3_bind_text(stmt, 1, 
696
 
                                                          dst->rel_path->ptr, 
 
792
                                                sqlite3_bind_text(stmt, 1,
 
793
                                                          dst->rel_path->ptr,
697
794
                                                          dst->rel_path->used - 1,
698
795
                                                          SQLITE_TRANSIENT);
699
796
 
700
 
                                                sqlite3_bind_text(stmt, 2, 
701
 
                                                          src->rel_path->ptr, 
 
797
                                                sqlite3_bind_text(stmt, 2,
 
798
                                                          src->rel_path->ptr,
702
799
                                                          src->rel_path->used - 1,
703
800
                                                          SQLITE_TRANSIENT);
704
 
                                                                                                        
 
801
 
705
802
                                                if (SQLITE_DONE != sqlite3_step(stmt)) {
706
803
                                                        /* */
707
 
                                                        WP();
708
804
                                                }
709
805
                                        }
710
806
#endif
721
817
                buffer_free(s.rel_path);
722
818
                buffer_free(d.path);
723
819
                buffer_free(d.rel_path);
724
 
                
 
820
 
725
821
                closedir(srcdir);
726
822
        }
727
823
 
748
844
                        if (S_ISDIR(sce->st.st_mode)) {
749
845
                                buffer_append_string(b, "<D:getcontenttype>httpd/unix-directory</D:getcontenttype>");
750
846
                                found = 1;
751
 
                        } else if(S_ISREG(sce->st.st_mode)) { 
 
847
                        } else if(S_ISREG(sce->st.st_mode)) {
752
848
                                for (k = 0; k < con->conf.mimetypes->used; k++) {
753
849
                                        data_string *ds = (data_string *)con->conf.mimetypes->data[k];
754
 
                
 
850
 
755
851
                                        if (ds->key->used == 0) continue;
756
 
                                
 
852
 
757
853
                                        if (buffer_is_equal_right_len(dst->path, ds->key, ds->key->used - 1)) {
758
854
                                                buffer_append_string(b,"<D:getcontenttype>");
759
855
                                                buffer_append_string_buffer(b, ds->value);
807
903
 
808
904
                        /* bind the values to the insert */
809
905
 
810
 
                        sqlite3_bind_text(stmt, 1, 
811
 
                                          dst->rel_path->ptr, 
 
906
                        sqlite3_bind_text(stmt, 1,
 
907
                                          dst->rel_path->ptr,
812
908
                                          dst->rel_path->used - 1,
813
909
                                          SQLITE_TRANSIENT);
814
 
                        sqlite3_bind_text(stmt, 2, 
 
910
                        sqlite3_bind_text(stmt, 2,
815
911
                                          prop_name,
816
912
                                          strlen(prop_name),
817
913
                                          SQLITE_TRANSIENT);
818
 
                        sqlite3_bind_text(stmt, 3, 
 
914
                        sqlite3_bind_text(stmt, 3,
819
915
                                          prop_ns,
820
916
                                          strlen(prop_ns),
821
917
                                          SQLITE_TRANSIENT);
822
918
 
823
919
                        /* it is the PK */
824
 
                        while (SQLITE_ROW == sqlite3_step(p->conf.stmt_select_prop)) {
 
920
                        while (SQLITE_ROW == sqlite3_step(stmt)) {
825
921
                                /* there is a row for us, we only expect a single col 'value' */
826
 
                                webdav_gen_prop_tag(srv, con, prop_name, prop_ns, (char *)sqlite3_column_text(p->conf.stmt_select_prop, 0), b);
 
922
                                webdav_gen_prop_tag(srv, con, prop_name, prop_ns, (char *)sqlite3_column_text(stmt, 0), b);
827
923
                                found = 1;
828
924
                        }
829
925
                }
840
936
        char *prop;
841
937
} webdav_property;
842
938
 
843
 
webdav_property live_properties[] = { 
 
939
webdav_property live_properties[] = {
844
940
        { "DAV:", "creationdate" },
845
941
        { "DAV:", "displayname" },
846
942
        { "DAV:", "getcontentlanguage" },
871
967
                        webdav_property *prop;
872
968
 
873
969
                        prop = props->ptr[i];
874
 
                        
875
 
                        if (0 != webdav_get_property(srv, con, p, 
 
970
 
 
971
                        if (0 != webdav_get_property(srv, con, p,
876
972
                                dst, prop->prop, prop->ns, b_200)) {
877
973
                                webdav_gen_prop_tag(srv, con, prop->prop, prop->ns, NULL, b_404);
878
974
                        }
916
1012
                                if (-1 == c->file.fd &&  /* open the file if not already open */
917
1013
                                    -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
918
1014
                                        log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
919
 
                
 
1015
 
920
1016
                                        return -1;
921
1017
                                }
922
 
        
 
1018
 
923
1019
                                if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
924
 
                                        log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", 
 
1020
                                        log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
925
1021
                                                        strerror(errno), c->file.name,  c->file.fd);
926
1022
 
927
1023
                                        return -1;
938
1034
                        if (XML_ERR_OK != (err = xmlParseChunk(ctxt, c->file.mmap.start + c->offset, weHave, 0))) {
939
1035
                                log_error_write(srv, __FILE__, __LINE__, "sddd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err);
940
1036
                        }
941
 
                        
 
1037
 
942
1038
                        c->offset += weHave;
943
1039
                        cq->bytes_out += weHave;
944
1040
 
956
1052
                        if (XML_ERR_OK != (err = xmlParseChunk(ctxt, c->mem->ptr + c->offset, weHave, 0))) {
957
1053
                                log_error_write(srv, __FILE__, __LINE__, "sddd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err);
958
1054
                        }
959
 
                        
 
1055
 
960
1056
                        c->offset += weHave;
961
1057
                        cq->bytes_out += weHave;
962
1058
 
991
1087
}
992
1088
#endif
993
1089
 
 
1090
int webdav_lockdiscovery(server *srv, connection *con,
 
1091
                buffer *locktoken, const char *lockscope, const char *locktype, int depth) {
 
1092
 
 
1093
        buffer *b;
 
1094
 
 
1095
        response_header_overwrite(srv, con, CONST_STR_LEN("Lock-Token"), CONST_BUF_LEN(locktoken));
 
1096
 
 
1097
        response_header_overwrite(srv, con,
 
1098
                CONST_STR_LEN("Content-Type"),
 
1099
                CONST_STR_LEN("text/xml; charset=\"utf-8\""));
 
1100
 
 
1101
        b = chunkqueue_get_append_buffer(con->write_queue);
 
1102
 
 
1103
        buffer_copy_string(b, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
 
1104
 
 
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");
 
1108
 
 
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");
 
1114
 
 
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");
 
1120
 
 
1121
        buffer_append_string(b,"<D:depth>");
 
1122
        buffer_append_string(b, depth == 0 ? "0" : "infinity");
 
1123
        buffer_append_string(b,"</D:depth>\n");
 
1124
 
 
1125
        buffer_append_string(b,"<D:timeout>");
 
1126
        buffer_append_string(b, "Second-600");
 
1127
        buffer_append_string(b,"</D:timeout>\n");
 
1128
 
 
1129
        buffer_append_string(b,"<D:owner>");
 
1130
        buffer_append_string(b,"</D:owner>\n");
 
1131
 
 
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");
 
1137
 
 
1138
        buffer_append_string(b,"</D:activelock>\n");
 
1139
        buffer_append_string(b,"</D:lockdiscovery>\n");
 
1140
        buffer_append_string(b,"</D:prop>\n");
 
1141
 
 
1142
        return 0;
 
1143
}
 
1144
/**
 
1145
 * check if resource is having the right locks to access to resource
 
1146
 *
 
1147
 *
 
1148
 *
 
1149
 */
 
1150
int webdav_has_lock(server *srv, connection *con, plugin_data *p, buffer *uri) {
 
1151
        int has_lock = 1;
 
1152
 
 
1153
#ifdef USE_LOCKS
 
1154
        data_string *ds;
 
1155
 
 
1156
        /**
 
1157
         * If can have
 
1158
         * - <lock-token>
 
1159
         * - [etag]
 
1160
         *
 
1161
         * there is NOT, AND and OR
 
1162
         * and a list can be tagged
 
1163
         *
 
1164
         * (<lock-token>) is untagged
 
1165
         * <tag> (<lock-token>) is tagged
 
1166
         *
 
1167
         * as long as we don't handle collections it is simple. :)
 
1168
         *
 
1169
         * X-Litmus: locks: 11 (owner_modify)
 
1170
         * If: <http://127.0.0.1:1025/dav/litmus/lockme> (<opaquelocktoken:2165478d-0611-49c4-be92-e790d68a38f1>)
 
1171
         *
 
1172
         * X-Litmus: locks: 16 (fail_cond_put)
 
1173
         * If: (<DAV:no-lock> ["-1622396671"])
 
1174
         */
 
1175
        if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) {
 
1176
        } else {
 
1177
                /* we didn't provided a lock-token -> */
 
1178
                /* if the resource is locked -> 423 */
 
1179
 
 
1180
                sqlite3_stmt *stmt = p->conf.stmt_read_lock_by_uri;
 
1181
 
 
1182
                sqlite3_reset(stmt);
 
1183
 
 
1184
                sqlite3_bind_text(stmt, 1,
 
1185
                          CONST_BUF_LEN(uri),
 
1186
                          SQLITE_TRANSIENT);
 
1187
 
 
1188
                while (SQLITE_ROW == sqlite3_step(stmt)) {
 
1189
                        has_lock = 0;
 
1190
                }
 
1191
        }
 
1192
#endif
 
1193
 
 
1194
        return has_lock;
 
1195
}
 
1196
 
994
1197
URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
995
1198
        plugin_data *p = p_d;
996
1199
        buffer *b;
1001
1204
        buffer *prop_200;
1002
1205
        buffer *prop_404;
1003
1206
        webdav_properties *req_props;
1004
 
        
 
1207
        stat_cache_entry *sce = NULL;
 
1208
 
1005
1209
        UNUSED(srv);
1006
1210
 
1007
1211
        if (!p->conf.enabled) return HANDLER_GO_ON;
1019
1223
                req_props = NULL;
1020
1224
 
1021
1225
                /* is there a content-body ? */
1022
 
        
 
1226
 
 
1227
                switch (stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
 
1228
                case HANDLER_ERROR:
 
1229
                        if (errno == ENOENT) {
 
1230
                                con->http_status = 404;
 
1231
                                return HANDLER_FINISHED;
 
1232
                        }
 
1233
                        break;
 
1234
                default:
 
1235
                        break;
 
1236
                }
 
1237
 
 
1238
 
1023
1239
#ifdef USE_PROPPATCH
1024
1240
                /* any special requests or just allprop ? */
1025
1241
                if (con->request.content_length) {
1087
1303
                                                                /* get all property names (EMPTY) */
1088
1304
                                                                sqlite3_reset(stmt);
1089
1305
                                                                /* bind the values to the insert */
1090
 
        
1091
 
                                                                sqlite3_bind_text(stmt, 1, 
1092
 
                                                                                  con->uri.path->ptr, 
 
1306
 
 
1307
                                                                sqlite3_bind_text(stmt, 1,
 
1308
                                                                                  con->uri.path->ptr,
1093
1309
                                                                                  con->uri.path->used - 1,
1094
1310
                                                                                  SQLITE_TRANSIENT);
1095
 
                                                
 
1311
 
1096
1312
                                                                if (SQLITE_DONE != sqlite3_step(stmt)) {
1097
 
                                                                        WP();
1098
1313
                                                                }
1099
1314
                                                        }
1100
1315
                                                } else if (0 == xmlStrcmp(cmd->name, BAD_CAST "allprop")) {
1115
1330
                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\""));
1116
1331
 
1117
1332
                b = chunkqueue_get_append_buffer(con->write_queue);
1118
 
                                
 
1333
 
1119
1334
                buffer_copy_string(b, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
1120
1335
 
1121
1336
                buffer_append_string(b,"<D:multistatus xmlns:D=\"DAV:\" xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n");
1122
1337
 
1123
1338
                /* allprop */
1124
 
                
 
1339
 
1125
1340
                prop_200 = buffer_init();
1126
1341
                prop_404 = buffer_init();
1127
1342
 
1129
1344
                case 0:
1130
1345
                        /* Depth: 0 */
1131
1346
                        webdav_get_props(srv, con, p, &(con->physical), req_props, prop_200, prop_404);
1132
 
        
 
1347
 
1133
1348
                        buffer_append_string(b,"<D:response>\n");
1134
1349
                        buffer_append_string(b,"<D:href>");
1135
1350
                        buffer_append_string_buffer(b, con->uri.scheme);
1145
1360
                                buffer_append_string_buffer(b, prop_200);
1146
1361
 
1147
1362
                                buffer_append_string(b,"</D:prop>\n");
1148
 
        
 
1363
 
1149
1364
                                buffer_append_string(b,"<D:status>HTTP/1.1 200 OK</D:status>\n");
1150
 
        
 
1365
 
1151
1366
                                buffer_append_string(b,"</D:propstat>\n");
1152
1367
                        }
1153
1368
                        if (!buffer_is_empty(prop_404)) {
1157
1372
                                buffer_append_string_buffer(b, prop_404);
1158
1373
 
1159
1374
                                buffer_append_string(b,"</D:prop>\n");
1160
 
        
 
1375
 
1161
1376
                                buffer_append_string(b,"<D:status>HTTP/1.1 404 Not Found</D:status>\n");
1162
 
        
 
1377
 
1163
1378
                                buffer_append_string(b,"</D:propstat>\n");
1164
1379
                        }
1165
1380
 
1166
1381
                        buffer_append_string(b,"</D:response>\n");
1167
1382
 
1168
1383
                        break;
1169
 
                case 1: 
 
1384
                case 1:
1170
1385
                        if (NULL != (dir = opendir(con->physical.path->ptr))) {
1171
1386
                                struct dirent *de;
1172
1387
                                physical d;
1179
1394
                                        if (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0') {
1180
1395
                                                continue;
1181
1396
                                                /* ignore the parent dir */
1182
 
                                        } 
 
1397
                                        }
1183
1398
 
1184
1399
                                        buffer_copy_string_buffer(d.path, dst->path);
1185
1400
                                        BUFFER_APPEND_SLASH(d.path);
1188
1403
                                        BUFFER_APPEND_SLASH(d.rel_path);
1189
1404
 
1190
1405
                                        if (de->d_name[0] == '.' && de->d_name[1] == '\0') {
1191
 
                                                /* don't append the . */ 
 
1406
                                                /* don't append the . */
1192
1407
                                        } else {
1193
1408
                                                buffer_append_string(d.path, de->d_name);
1194
1409
                                                buffer_append_string(d.rel_path, de->d_name);
1198
1413
                                        buffer_reset(prop_404);
1199
1414
 
1200
1415
                                        webdav_get_props(srv, con, p, &d, req_props, prop_200, prop_404);
1201
 
                                        
 
1416
 
1202
1417
                                        buffer_append_string(b,"<D:response>\n");
1203
1418
                                        buffer_append_string(b,"<D:href>");
1204
1419
                                        buffer_append_string_buffer(b, con->uri.scheme);
1214
1429
                                                buffer_append_string_buffer(b, prop_200);
1215
1430
 
1216
1431
                                                buffer_append_string(b,"</D:prop>\n");
1217
 
                        
 
1432
 
1218
1433
                                                buffer_append_string(b,"<D:status>HTTP/1.1 200 OK</D:status>\n");
1219
 
                        
 
1434
 
1220
1435
                                                buffer_append_string(b,"</D:propstat>\n");
1221
1436
                                        }
1222
1437
                                        if (!buffer_is_empty(prop_404)) {
1226
1441
                                                buffer_append_string_buffer(b, prop_404);
1227
1442
 
1228
1443
                                                buffer_append_string(b,"</D:prop>\n");
1229
 
        
 
1444
 
1230
1445
                                                buffer_append_string(b,"<D:status>HTTP/1.1 404 Not Found</D:status>\n");
1231
 
        
 
1446
 
1232
1447
                                                buffer_append_string(b,"</D:propstat>\n");
1233
1448
                                        }
1234
1449
 
1275
1490
 
1276
1491
                        return HANDLER_FINISHED;
1277
1492
                }
1278
 
        
 
1493
 
1279
1494
                /* let's create the directory */
1280
1495
 
1281
1496
                if (-1 == mkdir(con->physical.path->ptr, 0700)) {
1303
1518
                        con->http_status = 403;
1304
1519
                        return HANDLER_FINISHED;
1305
1520
                }
1306
 
                
 
1521
 
 
1522
                /* does the client have a lock for this connection ? */
 
1523
                if (!webdav_has_lock(srv, con, p, con->uri.path)) {
 
1524
                        con->http_status = 423;
 
1525
                        return HANDLER_FINISHED;
 
1526
                }
 
1527
 
1307
1528
                /* stat and unlink afterwards */
1308
1529
                if (-1 == stat(con->physical.path->ptr, &st)) {
1309
1530
                        /* don't about it yet, unlink will fail too */
1323
1544
                                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\""));
1324
1545
 
1325
1546
                                b = chunkqueue_get_append_buffer(con->write_queue);
1326
 
                        
 
1547
 
1327
1548
                                buffer_copy_string(b, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
1328
1549
 
1329
1550
                                buffer_append_string(b,"<D:multistatus xmlns:D=\"DAV:\">\n");
1331
1552
                                buffer_append_string_buffer(b, multi_status_resp);
1332
1553
 
1333
1554
                                buffer_append_string(b,"</D:multistatus>\n");
1334
 
                        
 
1555
 
1335
1556
                                if (p->conf.log_xml) {
1336
1557
                                        log_error_write(srv, __FILE__, __LINE__, "sb", "XML-response-body:", b);
1337
1558
                                }
1340
1561
                                con->file_finished = 1;
1341
1562
                        } else {
1342
1563
                                /* everything went fine, remove the directory */
1343
 
        
 
1564
 
1344
1565
                                if (-1 == rmdir(con->physical.path->ptr)) {
1345
1566
                                        switch(errno) {
1346
1567
                                        case ENOENT:
1375
1596
        case HTTP_METHOD_PUT: {
1376
1597
                int fd;
1377
1598
                chunkqueue *cq = con->request_content_queue;
 
1599
                chunk *c;
 
1600
                data_string *ds_range;
1378
1601
 
1379
1602
                if (p->conf.is_readonly) {
1380
1603
                        con->http_status = 403;
1381
1604
                        return HANDLER_FINISHED;
1382
1605
                }
1383
1606
 
 
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;
 
1611
                }
 
1612
 
 
1613
 
1384
1614
                assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
1385
1615
 
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
 
1618
                 *
 
1619
                 *
 
1620
                 * Example: Content-Range: bytes 100-1037/1038 */
 
1621
 
 
1622
                if (NULL != (ds_range = (data_string *)array_get_element(con->request.headers, "Content-Range"))) {
 
1623
                        const char *num = ds_range->value->ptr;
 
1624
                        off_t offset;
 
1625
                        char *err = NULL;
 
1626
 
 
1627
                        if (0 != strncmp(num, "bytes ", 6)) {
 
1628
                                con->http_status = 501; /* not implemented */
 
1629
 
 
1630
                                return HANDLER_FINISHED;
 
1631
                        }
 
1632
 
 
1633
                        /* we only support <num>- ... */
 
1634
 
 
1635
                        num += 6;
 
1636
 
 
1637
                        /* skip WS */
 
1638
                        while (*num == ' ' || *num == '\t') num++;
 
1639
 
 
1640
                        if (*num == '\0') {
 
1641
                                con->http_status = 501; /* not implemented */
 
1642
 
 
1643
                                return HANDLER_FINISHED;
 
1644
                        }
 
1645
 
 
1646
                        offset = strtoll(num, &err, 10);
 
1647
 
 
1648
                        if (*err != '-' || offset < 0) {
 
1649
                                con->http_status = 501; /* not implemented */
 
1650
 
 
1651
                                return HANDLER_FINISHED;
 
1652
                        }
 
1653
 
 
1654
                        if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY, 0600))) {
 
1655
                                switch (errno) {
 
1656
                                case ENOENT:
 
1657
                                        con->http_status = 404; /* not found */
 
1658
                                        break;
 
1659
                                default:
 
1660
                                        con->http_status = 403; /* not found */
 
1661
                                        break;
 
1662
                                }
 
1663
                                return HANDLER_FINISHED;
 
1664
                        }
 
1665
 
 
1666
                        if (-1 == lseek(fd, offset, SEEK_SET)) {
 
1667
                                con->http_status = 501; /* not implemented */
 
1668
 
 
1669
                                close(fd);
 
1670
 
 
1671
                                return HANDLER_FINISHED;
 
1672
                        }
 
1673
                        con->http_status = 200; /* modified */
1390
1674
                } else {
1391
 
                        chunk *c;
1392
 
 
1393
 
                        con->http_status = 201; /* created */
1394
 
                        con->file_finished = 1;
1395
 
 
1396
 
                        for (c = cq->first; c; c = cq->first) {
1397
 
                                int r = 0; 
1398
 
 
1399
 
                                /* copy all chunks */
1400
 
                                switch(c->type) {
1401
 
                                case FILE_CHUNK:
1402
 
 
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));
1407
 
                                        
1408
 
                                                        return -1;
1409
 
                                                }
1410
 
                                
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);
1414
 
 
1415
 
                                                        return -1;
1416
 
                                                }
1417
 
 
1418
 
                                                c->file.mmap.length = c->file.length;
1419
 
 
1420
 
                                                close(c->file.fd);
1421
 
                                                c->file.fd = -1;
1422
 
        
1423
 
                                                /* chunk_reset() or chunk_free() will cleanup for us */
1424
 
                                        }
1425
 
 
1426
 
                                        if ((r = write(fd, c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
1427
 
                                                switch(errno) {
1428
 
                                                case ENOSPC:
1429
 
                                                        con->http_status = 507;
1430
 
                
1431
 
                                                        break;
1432
 
                                                default:
1433
 
                                                        con->http_status = 403;
1434
 
                                                        break;
1435
 
                                                }
1436
 
                                        }
1437
 
                                        break;
1438
 
                                case MEM_CHUNK:
1439
 
                                        if ((r = write(fd, c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
1440
 
                                                switch(errno) {
1441
 
                                                case ENOSPC:
1442
 
                                                        con->http_status = 507;
1443
 
                
1444
 
                                                        break;
1445
 
                                                default:
1446
 
                                                        con->http_status = 403;
1447
 
                                                        break;
1448
 
                                                }
1449
 
                                        }
1450
 
                                        break;
1451
 
                                case UNUSED_CHUNK:
1452
 
                                        break;
1453
 
                                }
1454
 
 
1455
 
                                if (r > 0) {
1456
 
                                        c->offset += r;
1457
 
                                        cq->bytes_out += r;
 
1675
                        /* take what we have in the request-body and write it to a file */
 
1676
 
 
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;
 
1683
 
 
1684
                                        return HANDLER_FINISHED;
1458
1685
                                } else {
1459
 
                                        break;
1460
 
                                }
1461
 
                                chunkqueue_remove_finished_chunks(cq);
1462
 
                        }
1463
 
                        close(fd);
1464
 
 
1465
 
                }
 
1686
                                        con->http_status = 201; /* created */
 
1687
                                }
 
1688
                        } else {
 
1689
                                con->http_status = 200; /* modified */
 
1690
                        }
 
1691
                }
 
1692
 
 
1693
                con->file_finished = 1;
 
1694
 
 
1695
                for (c = cq->first; c; c = cq->first) {
 
1696
                        int r = 0;
 
1697
 
 
1698
                        /* copy all chunks */
 
1699
                        switch(c->type) {
 
1700
                        case FILE_CHUNK:
 
1701
 
 
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));
 
1706
 
 
1707
                                                return -1;
 
1708
                                        }
 
1709
 
 
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);
 
1713
 
 
1714
                                                return -1;
 
1715
                                        }
 
1716
 
 
1717
                                        c->file.mmap.length = c->file.length;
 
1718
 
 
1719
                                        close(c->file.fd);
 
1720
                                        c->file.fd = -1;
 
1721
 
 
1722
                                        /* chunk_reset() or chunk_free() will cleanup for us */
 
1723
                                }
 
1724
 
 
1725
                                if ((r = write(fd, c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
 
1726
                                        switch(errno) {
 
1727
                                        case ENOSPC:
 
1728
                                                con->http_status = 507;
 
1729
 
 
1730
                                                break;
 
1731
                                        default:
 
1732
                                                con->http_status = 403;
 
1733
                                                break;
 
1734
                                        }
 
1735
                                }
 
1736
                                break;
 
1737
                        case MEM_CHUNK:
 
1738
                                if ((r = write(fd, c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
 
1739
                                        switch(errno) {
 
1740
                                        case ENOSPC:
 
1741
                                                con->http_status = 507;
 
1742
 
 
1743
                                                break;
 
1744
                                        default:
 
1745
                                                con->http_status = 403;
 
1746
                                                break;
 
1747
                                        }
 
1748
                                }
 
1749
                                break;
 
1750
                        case UNUSED_CHUNK:
 
1751
                                break;
 
1752
                        }
 
1753
 
 
1754
                        if (r > 0) {
 
1755
                                c->offset += r;
 
1756
                                cq->bytes_out += r;
 
1757
                        } else {
 
1758
                                break;
 
1759
                        }
 
1760
                        chunkqueue_remove_finished_chunks(cq);
 
1761
                }
 
1762
                close(fd);
 
1763
 
1466
1764
                return HANDLER_FINISHED;
1467
1765
        }
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;
1475
1773
                        con->http_status = 403;
1476
1774
                        return HANDLER_FINISHED;
1477
1775
                }
1478
 
                
 
1776
 
 
1777
                /* is a exclusive lock set on the source */
 
1778
                if (con->request.http_method == HTTP_METHOD_MOVE) {
 
1779
                        if (!webdav_has_lock(srv, con, p, con->uri.path)) {
 
1780
                                con->http_status = 423;
 
1781
                                return HANDLER_FINISHED;
 
1782
                        }
 
1783
                }
 
1784
 
1479
1785
                if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Destination"))) {
1480
1786
                        destination = ds->value;
1481
1787
                } else {
1552
1858
                BUFFER_APPEND_SLASH(p->physical.path);
1553
1859
                buffer_copy_string_buffer(p->physical.basedir, p->physical.path);
1554
1860
 
1555
 
                /* don't add a second / */ 
 
1861
                /* don't add a second / */
1556
1862
                if (p->physical.rel_path->ptr[0] == '/') {
1557
1863
                        buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr + 1, p->physical.rel_path->used - 2);
1558
1864
                } else {
1613
1919
                        /* it is just a file, good */
1614
1920
                        int r;
1615
1921
 
 
1922
                        /* does the client have a lock for this connection ? */
 
1923
                        if (!webdav_has_lock(srv, con, p, p->uri.path)) {
 
1924
                                con->http_status = 423;
 
1925
                                return HANDLER_FINISHED;
 
1926
                        }
 
1927
 
1616
1928
                        /* destination exists */
1617
1929
                        if (0 == (r = stat(p->physical.path->ptr, &st))) {
1618
1930
                                if (S_ISDIR(st.st_mode)) {
1636
1948
                                        return HANDLER_FINISHED;
1637
1949
                                }
1638
1950
                        } else if (overwrite == 0) {
1639
 
                                /* destination exists, but overwrite is not set */ 
 
1951
                                /* destination exists, but overwrite is not set */
1640
1952
                                con->http_status = 412;
1641
1953
                                return HANDLER_FINISHED;
1642
1954
                        } else {
1655
1967
                                                sqlite3_reset(stmt);
1656
1968
 
1657
1969
                                                /* bind the values to the insert */
1658
 
                                                sqlite3_bind_text(stmt, 1, 
1659
 
                                                                  p->uri.path->ptr, 
 
1970
                                                sqlite3_bind_text(stmt, 1,
 
1971
                                                                  p->uri.path->ptr,
1660
1972
                                                                  p->uri.path->used - 1,
1661
1973
                                                                  SQLITE_TRANSIENT);
1662
1974
 
1663
 
                                                sqlite3_bind_text(stmt, 2, 
1664
 
                                                                  con->uri.path->ptr, 
 
1975
                                                sqlite3_bind_text(stmt, 2,
 
1976
                                                                  con->uri.path->ptr,
1665
1977
                                                                  con->uri.path->used - 1,
1666
1978
                                                                  SQLITE_TRANSIENT);
1667
 
                                                
 
1979
 
1668
1980
                                                if (SQLITE_DONE != sqlite3_step(stmt)) {
1669
1981
                                                        log_error_write(srv, __FILE__, __LINE__, "ss", "sql-move failed:", sqlite3_errmsg(p->conf.sql));
1670
1982
                                                }
1691
2003
 
1692
2004
                return HANDLER_FINISHED;
1693
2005
        }
1694
 
        case HTTP_METHOD_PROPPATCH: {
 
2006
        case HTTP_METHOD_PROPPATCH:
1695
2007
                if (p->conf.is_readonly) {
1696
2008
                        con->http_status = 403;
1697
2009
                        return HANDLER_FINISHED;
1698
2010
                }
1699
2011
 
 
2012
                if (!webdav_has_lock(srv, con, p, con->uri.path)) {
 
2013
                        con->http_status = 423;
 
2014
                        return HANDLER_FINISHED;
 
2015
                }
 
2016
 
1700
2017
                /* check if destination exists */
1701
2018
                if (-1 == stat(con->physical.path->ptr, &st)) {
1702
2019
                        switch(errno) {
1737
2054
 
1738
2055
                                                        sqlite3_stmt *stmt;
1739
2056
 
1740
 
                                                        stmt = (0 == xmlStrcmp(cmd->name, BAD_CAST "remove")) ? 
 
2057
                                                        stmt = (0 == xmlStrcmp(cmd->name, BAD_CAST "remove")) ?
1741
2058
                                                                p->conf.stmt_delete_prop : p->conf.stmt_update_prop;
1742
2059
 
1743
2060
                                                        for (props = cmd->children; props; props = props->next) {
1762
2079
 
1763
2080
                                                                        /* bind the values to the insert */
1764
2081
 
1765
 
                                                                        sqlite3_bind_text(stmt, 1, 
1766
 
                                                                                          con->uri.path->ptr, 
 
2082
                                                                        sqlite3_bind_text(stmt, 1,
 
2083
                                                                                          con->uri.path->ptr,
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);
1778
2095
                                                                        } else {
1779
 
                                                                                sqlite3_bind_text(stmt, 3, 
 
2096
                                                                                sqlite3_bind_text(stmt, 3,
1780
2097
                                                                                                  "",
1781
2098
                                                                                                  0,
1782
2099
                                                                                                  SQLITE_TRANSIENT);
1783
2100
                                                                        }
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);
1789
2106
                                                                        }
1790
 
                                                                
 
2107
 
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));
1793
2111
                                                                        }
1794
2112
                                                                }
1795
2113
                                                        }
1804
2122
 
1805
2123
                                                        goto propmatch_cleanup;
1806
2124
                                                }
1807
 
        
 
2125
 
1808
2126
                                                con->http_status = 400;
1809
2127
                                        } else {
1810
2128
                                                if (SQLITE_OK != sqlite3_exec(p->conf.sql, "COMMIT", NULL, NULL, &err)) {
1821
2139
                                }
1822
2140
 
1823
2141
propmatch_cleanup:
1824
 
                                xmlFreeDoc(xml);
1825
 
                        } else {
1826
 
                                con->http_status = 400;
1827
 
                                return HANDLER_FINISHED;
1828
 
                        }
1829
 
                }
1830
 
#endif
1831
 
                con->http_status = 501;
1832
 
                return HANDLER_FINISHED;
1833
 
        }
 
2142
 
 
2143
                                xmlFreeDoc(xml);
 
2144
                        } else {
 
2145
                                con->http_status = 400;
 
2146
                                return HANDLER_FINISHED;
 
2147
                        }
 
2148
                }
 
2149
#endif
 
2150
                con->http_status = 501;
 
2151
                return HANDLER_FINISHED;
 
2152
        case HTTP_METHOD_LOCK:
 
2153
                /**
 
2154
                 * a mac wants to write
 
2155
                 *
 
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
 
2158
                 * Accept: * / *\r\n
 
2159
                 * Depth: 0\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
 
2165
                 * \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
 
2170
                 *  <D:owner>\n
 
2171
                 *   <D:href>http://www.apple.com/webdav_fs/</D:href>\n
 
2172
                 *  </D:owner>\n
 
2173
                 * </D:lockinfo>\n
 
2174
                 */
 
2175
 
 
2176
                if (depth != 0 && depth != -1) {
 
2177
                        con->http_status = 400;
 
2178
 
 
2179
                        return HANDLER_FINISHED;
 
2180
                }
 
2181
 
 
2182
#ifdef USE_LOCKS
 
2183
                if (con->request.content_length) {
 
2184
                        xmlDocPtr xml;
 
2185
                        buffer *hdr_if = NULL;
 
2186
 
 
2187
                        if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) {
 
2188
                                hdr_if = ds->value;
 
2189
                        }
 
2190
 
 
2191
                        /* we don't support Depth: Infinity on locks */
 
2192
                        if (hdr_if == NULL && depth == -1) {
 
2193
                                con->http_status = 409; /* Conflict */
 
2194
 
 
2195
                                return HANDLER_FINISHED;
 
2196
                        }
 
2197
 
 
2198
                        if (1 == webdav_parse_chunkqueue(srv, con, p, con->request_content_queue, &xml)) {
 
2199
                                xmlNode *rootnode = xmlDocGetRootElement(xml);
 
2200
 
 
2201
                                assert(rootnode);
 
2202
 
 
2203
                                if (0 == xmlStrcmp(rootnode->name, BAD_CAST "lockinfo")) {
 
2204
                                        xmlNode *lockinfo;
 
2205
                                        const xmlChar *lockscope = NULL, *locktype = NULL, *owner = NULL;
 
2206
 
 
2207
                                        for (lockinfo = rootnode->children; lockinfo; lockinfo = lockinfo->next) {
 
2208
                                                if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "lockscope")) {
 
2209
                                                        xmlNode *value;
 
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;
 
2214
                                                                } else {
 
2215
                                                                        con->http_status = 400;
 
2216
 
 
2217
                                                                        xmlFreeDoc(xml);
 
2218
                                                                        return HANDLER_FINISHED;
 
2219
                                                                }
 
2220
                                                        }
 
2221
                                                } else if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "locktype")) {
 
2222
                                                        xmlNode *value;
 
2223
                                                        for (value = lockinfo->children; value; value = value->next) {
 
2224
                                                                if ((0 == xmlStrcmp(value->name, BAD_CAST "write"))) {
 
2225
                                                                        locktype = value->name;
 
2226
                                                                } else {
 
2227
                                                                        con->http_status = 400;
 
2228
 
 
2229
                                                                        xmlFreeDoc(xml);
 
2230
                                                                        return HANDLER_FINISHED;
 
2231
                                                                }
 
2232
                                                        }
 
2233
 
 
2234
                                                } else if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "owner")) {
 
2235
                                                }
 
2236
                                        }
 
2237
 
 
2238
                                        if (lockscope && locktype) {
 
2239
                                                sqlite3_stmt *stmt = p->conf.stmt_read_lock_by_uri;
 
2240
 
 
2241
                                                /* is this resourse already locked ? */
 
2242
 
 
2243
                                                /* SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout
 
2244
                                                 *   FROM locks
 
2245
                                                 *  WHERE resource = ? */
 
2246
 
 
2247
                                                if (stmt) {
 
2248
 
 
2249
                                                        sqlite3_reset(stmt);
 
2250
 
 
2251
                                                        sqlite3_bind_text(stmt, 1,
 
2252
                                                                          p->uri.path->ptr,
 
2253
                                                                          p->uri.path->used - 1,
 
2254
                                                                          SQLITE_TRANSIENT);
 
2255
 
 
2256
                                                        /* it is the PK */
 
2257
                                                        while (SQLITE_ROW == sqlite3_step(stmt)) {
 
2258
                                                                /* we found a lock
 
2259
                                                                 * 1. is it compatible ?
 
2260
                                                                 * 2. is it ours */
 
2261
                                                                char *sql_lockscope = (char *)sqlite3_column_text(stmt, 2);
 
2262
 
 
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;
 
2269
                                                                }
 
2270
                                                        }
 
2271
                                                        if (con->http_status == 423) {
 
2272
                                                                xmlFreeDoc(xml);
 
2273
                                                                return HANDLER_FINISHED;
 
2274
                                                        }
 
2275
                                                }
 
2276
 
 
2277
                                                stmt = p->conf.stmt_create_lock;
 
2278
                                                if (stmt) {
 
2279
                                                        /* create a lock-token */
 
2280
                                                        uuid_t id;
 
2281
                                                        char uuid[37] /* 36 + \0 */;
 
2282
 
 
2283
                                                        uuid_generate(id);
 
2284
                                                        uuid_unparse(id, uuid);
 
2285
 
 
2286
                                                        buffer_copy_string(p->tmp_buf, "opaquelocktoken:");
 
2287
                                                        buffer_append_string(p->tmp_buf, uuid);
 
2288
 
 
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,"
 
2296
                                                         */
 
2297
 
 
2298
                                                        sqlite3_reset(stmt);
 
2299
 
 
2300
                                                        sqlite3_bind_text(stmt, 1,
 
2301
                                                                          CONST_BUF_LEN(p->tmp_buf),
 
2302
                                                                          SQLITE_TRANSIENT);
 
2303
 
 
2304
                                                        sqlite3_bind_text(stmt, 2,
 
2305
                                                                          CONST_BUF_LEN(con->uri.path),
 
2306
                                                                          SQLITE_TRANSIENT);
 
2307
 
 
2308
                                                        sqlite3_bind_text(stmt, 3,
 
2309
                                                                          lockscope,
 
2310
                                                                          xmlStrlen(lockscope),
 
2311
                                                                          SQLITE_TRANSIENT);
 
2312
 
 
2313
                                                        sqlite3_bind_text(stmt, 4,
 
2314
                                                                          locktype,
 
2315
                                                                          xmlStrlen(locktype),
 
2316
                                                                          SQLITE_TRANSIENT);
 
2317
 
 
2318
                                                        /* owner */
 
2319
                                                        sqlite3_bind_text(stmt, 5,
 
2320
                                                                          "",
 
2321
                                                                          0,
 
2322
                                                                          SQLITE_TRANSIENT);
 
2323
 
 
2324
                                                        /* depth */
 
2325
                                                        sqlite3_bind_int(stmt, 6,
 
2326
                                                                         depth);
 
2327
 
 
2328
 
 
2329
                                                        if (SQLITE_DONE != sqlite3_step(stmt)) {
 
2330
                                                                log_error_write(srv, __FILE__, __LINE__, "ss",
 
2331
                                                                                "create lock:", sqlite3_errmsg(p->conf.sql));
 
2332
                                                        }
 
2333
 
 
2334
                                                        /* looks like we survived */
 
2335
                                                        webdav_lockdiscovery(srv, con, p->tmp_buf, lockscope, locktype, depth);
 
2336
 
 
2337
                                                        con->http_status = 201;
 
2338
                                                        con->file_finished = 1;
 
2339
                                                }
 
2340
                                        }
 
2341
                                }
 
2342
 
 
2343
                                xmlFreeDoc(xml);
 
2344
                                return HANDLER_FINISHED;
 
2345
                        } else {
 
2346
                                con->http_status = 400;
 
2347
                                return HANDLER_FINISHED;
 
2348
                        }
 
2349
                } else {
 
2350
 
 
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;
 
2354
 
 
2355
                                /* remove the < > around the token */
 
2356
                                if (locktoken->used < 6) {
 
2357
                                        con->http_status = 400;
 
2358
 
 
2359
                                        return HANDLER_FINISHED;
 
2360
                                }
 
2361
 
 
2362
                                buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 2, locktoken->used - 5);
 
2363
 
 
2364
                                sqlite3_reset(stmt);
 
2365
 
 
2366
                                sqlite3_bind_text(stmt, 1,
 
2367
                                          CONST_BUF_LEN(p->tmp_buf),
 
2368
                                          SQLITE_TRANSIENT);
 
2369
 
 
2370
                                if (SQLITE_DONE != sqlite3_step(stmt)) {
 
2371
                                        log_error_write(srv, __FILE__, __LINE__, "ss",
 
2372
                                                "refresh lock:", sqlite3_errmsg(p->conf.sql));
 
2373
                                }
 
2374
 
 
2375
                                webdav_lockdiscovery(srv, con, p->tmp_buf, "exclusive", "write", 0);
 
2376
 
 
2377
                                con->http_status = 200;
 
2378
                                con->file_finished = 1;
 
2379
                                return HANDLER_FINISHED;
 
2380
                        } else {
 
2381
                                /* we need a lock-token to refresh */
 
2382
                                con->http_status = 400;
 
2383
 
 
2384
                                return HANDLER_FINISHED;
 
2385
                        }
 
2386
                }
 
2387
                break;
 
2388
#else
 
2389
                con->http_status = 501;
 
2390
                return HANDLER_FINISHED;
 
2391
#endif
 
2392
        case HTTP_METHOD_UNLOCK:
 
2393
#ifdef USE_LOCKS
 
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;
 
2397
 
 
2398
                        /* remove the < > around the token */
 
2399
                        if (locktoken->used < 4) {
 
2400
                                con->http_status = 400;
 
2401
 
 
2402
                                return HANDLER_FINISHED;
 
2403
                        }
 
2404
 
 
2405
                        /**
 
2406
                         * FIXME:
 
2407
                         *
 
2408
                         * if the resourse is locked:
 
2409
                         * - by us: unlock
 
2410
                         * - by someone else: 401
 
2411
                         * if the resource is not locked:
 
2412
                         * - 412
 
2413
                         *  */
 
2414
 
 
2415
                        buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 1, locktoken->used - 3);
 
2416
 
 
2417
                        sqlite3_reset(stmt);
 
2418
 
 
2419
                        sqlite3_bind_text(stmt, 1,
 
2420
                                  CONST_BUF_LEN(p->tmp_buf),
 
2421
                                  SQLITE_TRANSIENT);
 
2422
 
 
2423
                        sqlite3_bind_text(stmt, 2,
 
2424
                                  CONST_BUF_LEN(con->uri.path),
 
2425
                                  SQLITE_TRANSIENT);
 
2426
 
 
2427
                        if (SQLITE_DONE != sqlite3_step(stmt)) {
 
2428
                                log_error_write(srv, __FILE__, __LINE__, "ss",
 
2429
                                        "remove lock:", sqlite3_errmsg(p->conf.sql));
 
2430
                        }
 
2431
 
 
2432
                        if (0 == sqlite3_changes(p->conf.sql)) {
 
2433
                                con->http_status = 401;
 
2434
                        } else {
 
2435
                                con->http_status = 204;
 
2436
                        }
 
2437
                        return HANDLER_FINISHED;
 
2438
                } else {
 
2439
                        /* we need a lock-token to unlock */
 
2440
                        con->http_status = 400;
 
2441
 
 
2442
                        return HANDLER_FINISHED;
 
2443
                }
 
2444
                break;
 
2445
#else
 
2446
                con->http_status = 501;
 
2447
                return HANDLER_FINISHED;
 
2448
#endif
1834
2449
        default:
1835
2450
                break;
1836
2451
        }
1837
 
        
 
2452
 
1838
2453
        /* not found */
1839
2454
        return HANDLER_GO_ON;
1840
2455
}
1845
2460
int mod_webdav_plugin_init(plugin *p) {
1846
2461
        p->version     = LIGHTTPD_VERSION_ID;
1847
2462
        p->name        = buffer_init_string("webdav");
1848
 
        
 
2463
 
1849
2464
        p->init        = mod_webdav_init;
1850
2465
        p->handle_uri_clean  = mod_webdav_uri_handler;
1851
2466
        p->handle_physical   = mod_webdav_subrequest_handler;
1852
2467
        p->set_defaults  = mod_webdav_set_defaults;
1853
2468
        p->cleanup     = mod_webdav_free;
1854
 
        
 
2469
 
1855
2470
        p->data        = NULL;
1856
 
        
 
2471
 
1857
2472
        return 0;
1858
2473
}