Annotation of embedaddon/lighttpd/src/mod_webdav.c, revision 1.1.1.1

1.1       misho       1: #include "base.h"
                      2: #include "log.h"
                      3: #include "buffer.h"
                      4: #include "response.h"
                      5: 
                      6: #include "plugin.h"
                      7: 
                      8: #include "stream.h"
                      9: #include "stat_cache.h"
                     10: 
                     11: #include "sys-mmap.h"
                     12: 
                     13: #include <sys/types.h>
                     14: #include <sys/stat.h>
                     15: #include <ctype.h>
                     16: #include <stdlib.h>
                     17: #include <string.h>
                     18: #include <errno.h>
                     19: #include <fcntl.h>
                     20: #include <stdio.h>
                     21: #include <assert.h>
                     22: 
                     23: #include <unistd.h>
                     24: #include <dirent.h>
                     25: 
                     26: #if defined(HAVE_LIBXML_H) && defined(HAVE_SQLITE3_H)
                     27: #define USE_PROPPATCH
                     28: #include <libxml/tree.h>
                     29: #include <libxml/parser.h>
                     30: 
                     31: #include <sqlite3.h>
                     32: #endif
                     33: 
                     34: #if defined(HAVE_LIBXML_H) && defined(HAVE_SQLITE3_H) && defined(HAVE_UUID_UUID_H)
                     35: #define USE_LOCKS
                     36: #include <uuid/uuid.h>
                     37: #endif
                     38: 
                     39: /**
                     40:  * this is a webdav for a lighttpd plugin
                     41:  *
                     42:  * at least a very basic one.
                     43:  * - for now it is read-only and we only support PROPFIND
                     44:  *
                     45:  */
                     46: 
                     47: #define WEBDAV_FILE_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
                     48: #define WEBDAV_DIR_MODE  S_IRWXU | S_IRWXG | S_IRWXO
                     49: 
                     50: /* plugin config for all request/connections */
                     51: 
                     52: typedef struct {
                     53:        unsigned short enabled;
                     54:        unsigned short is_readonly;
                     55:        unsigned short log_xml;
                     56: 
                     57:        buffer *sqlite_db_name;
                     58: #ifdef USE_PROPPATCH
                     59:        sqlite3 *sql;
                     60:        sqlite3_stmt *stmt_update_prop;
                     61:        sqlite3_stmt *stmt_delete_prop;
                     62:        sqlite3_stmt *stmt_select_prop;
                     63:        sqlite3_stmt *stmt_select_propnames;
                     64: 
                     65:        sqlite3_stmt *stmt_delete_uri;
                     66:        sqlite3_stmt *stmt_move_uri;
                     67:        sqlite3_stmt *stmt_copy_uri;
                     68: 
                     69:        sqlite3_stmt *stmt_remove_lock;
                     70:        sqlite3_stmt *stmt_create_lock;
                     71:        sqlite3_stmt *stmt_read_lock;
                     72:        sqlite3_stmt *stmt_read_lock_by_uri;
                     73:        sqlite3_stmt *stmt_refresh_lock;
                     74: #endif
                     75: } plugin_config;
                     76: 
                     77: typedef struct {
                     78:        PLUGIN_DATA;
                     79: 
                     80:        buffer *tmp_buf;
                     81:        request_uri uri;
                     82:        physical physical;
                     83: 
                     84:        plugin_config **config_storage;
                     85: 
                     86:        plugin_config conf;
                     87: } plugin_data;
                     88: 
                     89: /* init the plugin data */
                     90: INIT_FUNC(mod_webdav_init) {
                     91:        plugin_data *p;
                     92: 
                     93:        p = calloc(1, sizeof(*p));
                     94: 
                     95:        p->tmp_buf = buffer_init();
                     96: 
                     97:        p->uri.scheme = buffer_init();
                     98:        p->uri.path_raw = buffer_init();
                     99:        p->uri.path = buffer_init();
                    100:        p->uri.authority = buffer_init();
                    101: 
                    102:        p->physical.path = buffer_init();
                    103:        p->physical.rel_path = buffer_init();
                    104:        p->physical.doc_root = buffer_init();
                    105:        p->physical.basedir = buffer_init();
                    106: 
                    107:        return p;
                    108: }
                    109: 
                    110: /* detroy the plugin data */
                    111: FREE_FUNC(mod_webdav_free) {
                    112:        plugin_data *p = p_d;
                    113: 
                    114:        UNUSED(srv);
                    115: 
                    116:        if (!p) return HANDLER_GO_ON;
                    117: 
                    118:        if (p->config_storage) {
                    119:                size_t i;
                    120:                for (i = 0; i < srv->config_context->used; i++) {
                    121:                        plugin_config *s = p->config_storage[i];
                    122: 
                    123:                        if (!s) continue;
                    124: 
                    125:                        buffer_free(s->sqlite_db_name);
                    126: #ifdef USE_PROPPATCH
                    127:                        if (s->sql) {
                    128:                                sqlite3_finalize(s->stmt_delete_prop);
                    129:                                sqlite3_finalize(s->stmt_delete_uri);
                    130:                                sqlite3_finalize(s->stmt_copy_uri);
                    131:                                sqlite3_finalize(s->stmt_move_uri);
                    132:                                sqlite3_finalize(s->stmt_update_prop);
                    133:                                sqlite3_finalize(s->stmt_select_prop);
                    134:                                sqlite3_finalize(s->stmt_select_propnames);
                    135: 
                    136:                                sqlite3_finalize(s->stmt_read_lock);
                    137:                                sqlite3_finalize(s->stmt_read_lock_by_uri);
                    138:                                sqlite3_finalize(s->stmt_create_lock);
                    139:                                sqlite3_finalize(s->stmt_remove_lock);
                    140:                                sqlite3_finalize(s->stmt_refresh_lock);
                    141:                                sqlite3_close(s->sql);
                    142:                        }
                    143: #endif
                    144:                        free(s);
                    145:                }
                    146:                free(p->config_storage);
                    147:        }
                    148: 
                    149:        buffer_free(p->uri.scheme);
                    150:        buffer_free(p->uri.path_raw);
                    151:        buffer_free(p->uri.path);
                    152:        buffer_free(p->uri.authority);
                    153: 
                    154:        buffer_free(p->physical.path);
                    155:        buffer_free(p->physical.rel_path);
                    156:        buffer_free(p->physical.doc_root);
                    157:        buffer_free(p->physical.basedir);
                    158: 
                    159:        buffer_free(p->tmp_buf);
                    160: 
                    161:        free(p);
                    162: 
                    163:        return HANDLER_GO_ON;
                    164: }
                    165: 
                    166: /* handle plugin config and check values */
                    167: 
                    168: SETDEFAULTS_FUNC(mod_webdav_set_defaults) {
                    169:        plugin_data *p = p_d;
                    170:        size_t i = 0;
                    171: 
                    172:        config_values_t cv[] = {
                    173:                { "webdav.activate",            NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
                    174:                { "webdav.is-readonly",         NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
                    175:                { "webdav.sqlite-db-name",      NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION },       /* 2 */
                    176:                { "webdav.log-xml",             NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
                    177:                { NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
                    178:        };
                    179: 
                    180:        if (!p) return HANDLER_ERROR;
                    181: 
                    182:        p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
                    183: 
                    184:        for (i = 0; i < srv->config_context->used; i++) {
                    185:                plugin_config *s;
                    186: 
                    187:                s = calloc(1, sizeof(plugin_config));
                    188:                s->sqlite_db_name = buffer_init();
                    189: 
                    190:                cv[0].destination = &(s->enabled);
                    191:                cv[1].destination = &(s->is_readonly);
                    192:                cv[2].destination = s->sqlite_db_name;
                    193:                cv[3].destination = &(s->log_xml);
                    194: 
                    195:                p->config_storage[i] = s;
                    196: 
                    197:                if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
                    198:                        return HANDLER_ERROR;
                    199:                }
                    200: 
                    201:                if (!buffer_is_empty(s->sqlite_db_name)) {
                    202: #ifdef USE_PROPPATCH
                    203:                        const char *next_stmt;
                    204:                        char *err;
                    205: 
                    206:                        if (SQLITE_OK != sqlite3_open(s->sqlite_db_name->ptr, &(s->sql))) {
                    207:                                log_error_write(srv, __FILE__, __LINE__, "sbs", "sqlite3_open failed for",
                    208:                                                s->sqlite_db_name,
                    209:                                                sqlite3_errmsg(s->sql));
                    210:                                return HANDLER_ERROR;
                    211:                        }
                    212: 
                    213:                        if (SQLITE_OK != sqlite3_exec(s->sql,
                    214:                                        "CREATE TABLE properties ("
                    215:                                        "  resource TEXT NOT NULL,"
                    216:                                        "  prop TEXT NOT NULL,"
                    217:                                        "  ns TEXT NOT NULL,"
                    218:                                        "  value TEXT NOT NULL,"
                    219:                                        "  PRIMARY KEY(resource, prop, ns))",
                    220:                                        NULL, NULL, &err)) {
                    221: 
                    222:                                if (0 != strcmp(err, "table properties already exists")) {
                    223:                                        log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err);
                    224:                                        sqlite3_free(err);
                    225: 
                    226:                                        return HANDLER_ERROR;
                    227:                                }
                    228:                                sqlite3_free(err);
                    229:                        }
                    230: 
                    231:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    232:                                CONST_STR_LEN("SELECT value FROM properties WHERE resource = ? AND prop = ? AND ns = ?"),
                    233:                                &(s->stmt_select_prop), &next_stmt)) {
                    234:                                /* prepare failed */
                    235: 
                    236:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql));
                    237:                                return HANDLER_ERROR;
                    238:                        }
                    239: 
                    240:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    241:                                CONST_STR_LEN("SELECT ns, prop FROM properties WHERE resource = ?"),
                    242:                                &(s->stmt_select_propnames), &next_stmt)) {
                    243:                                /* prepare failed */
                    244: 
                    245:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql));
                    246:                                return HANDLER_ERROR;
                    247:                        }
                    248: 
                    249: 
                    250:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    251:                                CONST_STR_LEN("REPLACE INTO properties (resource, prop, ns, value) VALUES (?, ?, ?, ?)"),
                    252:                                &(s->stmt_update_prop), &next_stmt)) {
                    253:                                /* prepare failed */
                    254: 
                    255:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql));
                    256:                                return HANDLER_ERROR;
                    257:                        }
                    258: 
                    259:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    260:                                CONST_STR_LEN("DELETE FROM properties WHERE resource = ? AND prop = ? AND ns = ?"),
                    261:                                &(s->stmt_delete_prop), &next_stmt)) {
                    262:                                /* prepare failed */
                    263:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
                    264: 
                    265:                                return HANDLER_ERROR;
                    266:                        }
                    267: 
                    268:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    269:                                CONST_STR_LEN("DELETE FROM properties WHERE resource = ?"),
                    270:                                &(s->stmt_delete_uri), &next_stmt)) {
                    271:                                /* prepare failed */
                    272:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
                    273: 
                    274:                                return HANDLER_ERROR;
                    275:                        }
                    276: 
                    277:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    278:                                CONST_STR_LEN("INSERT INTO properties SELECT ?, prop, ns, value FROM properties WHERE resource = ?"),
                    279:                                &(s->stmt_copy_uri), &next_stmt)) {
                    280:                                /* prepare failed */
                    281:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
                    282: 
                    283:                                return HANDLER_ERROR;
                    284:                        }
                    285: 
                    286:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    287:                                CONST_STR_LEN("UPDATE properties SET resource = ? WHERE resource = ?"),
                    288:                                &(s->stmt_move_uri), &next_stmt)) {
                    289:                                /* prepare failed */
                    290:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
                    291: 
                    292:                                return HANDLER_ERROR;
                    293:                        }
                    294: 
                    295:                        /* LOCKS */
                    296: 
                    297:                        if (SQLITE_OK != sqlite3_exec(s->sql,
                    298:                                        "CREATE TABLE locks ("
                    299:                                        "  locktoken TEXT NOT NULL,"
                    300:                                        "  resource TEXT NOT NULL,"
                    301:                                        "  lockscope TEXT NOT NULL,"
                    302:                                        "  locktype TEXT NOT NULL,"
                    303:                                        "  owner TEXT NOT NULL,"
                    304:                                        "  depth INT NOT NULL,"
                    305:                                        "  timeout TIMESTAMP NOT NULL,"
                    306:                                        "  PRIMARY KEY(locktoken))",
                    307:                                        NULL, NULL, &err)) {
                    308: 
                    309:                                if (0 != strcmp(err, "table locks already exists")) {
                    310:                                        log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err);
                    311:                                        sqlite3_free(err);
                    312: 
                    313:                                        return HANDLER_ERROR;
                    314:                                }
                    315:                                sqlite3_free(err);
                    316:                        }
                    317: 
                    318:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    319:                                CONST_STR_LEN("INSERT INTO locks (locktoken, resource, lockscope, locktype, owner, depth, timeout) VALUES (?,?,?,?,?,?, CURRENT_TIME + 600)"),
                    320:                                &(s->stmt_create_lock), &next_stmt)) {
                    321:                                /* prepare failed */
                    322:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
                    323: 
                    324:                                return HANDLER_ERROR;
                    325:                        }
                    326: 
                    327:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    328:                                CONST_STR_LEN("DELETE FROM locks WHERE locktoken = ?"),
                    329:                                &(s->stmt_remove_lock), &next_stmt)) {
                    330:                                /* prepare failed */
                    331:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
                    332: 
                    333:                                return HANDLER_ERROR;
                    334:                        }
                    335: 
                    336:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    337:                                CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout FROM locks WHERE locktoken = ?"),
                    338:                                &(s->stmt_read_lock), &next_stmt)) {
                    339:                                /* prepare failed */
                    340:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
                    341: 
                    342:                                return HANDLER_ERROR;
                    343:                        }
                    344: 
                    345:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    346:                                CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout FROM locks WHERE resource = ?"),
                    347:                                &(s->stmt_read_lock_by_uri), &next_stmt)) {
                    348:                                /* prepare failed */
                    349:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
                    350: 
                    351:                                return HANDLER_ERROR;
                    352:                        }
                    353: 
                    354:                        if (SQLITE_OK != sqlite3_prepare(s->sql,
                    355:                                CONST_STR_LEN("UPDATE locks SET timeout = CURRENT_TIME + 600 WHERE locktoken = ?"),
                    356:                                &(s->stmt_refresh_lock), &next_stmt)) {
                    357:                                /* prepare failed */
                    358:                                log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql));
                    359: 
                    360:                                return HANDLER_ERROR;
                    361:                        }
                    362: 
                    363: 
                    364: #else
                    365:                        log_error_write(srv, __FILE__, __LINE__, "s", "Sorry, no sqlite3 and libxml2 support include, compile with --with-webdav-props");
                    366:                        return HANDLER_ERROR;
                    367: #endif
                    368:                }
                    369:        }
                    370: 
                    371:        return HANDLER_GO_ON;
                    372: }
                    373: 
                    374: #define PATCH_OPTION(x) \
                    375:        p->conf.x = s->x;
                    376: static int mod_webdav_patch_connection(server *srv, connection *con, plugin_data *p) {
                    377:        size_t i, j;
                    378:        plugin_config *s = p->config_storage[0];
                    379: 
                    380:        PATCH_OPTION(enabled);
                    381:        PATCH_OPTION(is_readonly);
                    382:        PATCH_OPTION(log_xml);
                    383: 
                    384: #ifdef USE_PROPPATCH
                    385:        PATCH_OPTION(sql);
                    386:        PATCH_OPTION(stmt_update_prop);
                    387:        PATCH_OPTION(stmt_delete_prop);
                    388:        PATCH_OPTION(stmt_select_prop);
                    389:        PATCH_OPTION(stmt_select_propnames);
                    390: 
                    391:        PATCH_OPTION(stmt_delete_uri);
                    392:        PATCH_OPTION(stmt_move_uri);
                    393:        PATCH_OPTION(stmt_copy_uri);
                    394: 
                    395:        PATCH_OPTION(stmt_remove_lock);
                    396:        PATCH_OPTION(stmt_refresh_lock);
                    397:        PATCH_OPTION(stmt_create_lock);
                    398:        PATCH_OPTION(stmt_read_lock);
                    399:        PATCH_OPTION(stmt_read_lock_by_uri);
                    400: #endif
                    401:        /* skip the first, the global context */
                    402:        for (i = 1; i < srv->config_context->used; i++) {
                    403:                data_config *dc = (data_config *)srv->config_context->data[i];
                    404:                s = p->config_storage[i];
                    405: 
                    406:                /* condition didn't match */
                    407:                if (!config_check_cond(srv, con, dc)) continue;
                    408: 
                    409:                /* merge config */
                    410:                for (j = 0; j < dc->value->used; j++) {
                    411:                        data_unset *du = dc->value->data[j];
                    412: 
                    413:                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.activate"))) {
                    414:                                PATCH_OPTION(enabled);
                    415:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.is-readonly"))) {
                    416:                                PATCH_OPTION(is_readonly);
                    417:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.log-xml"))) {
                    418:                                PATCH_OPTION(log_xml);
                    419:                        } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.sqlite-db-name"))) {
                    420: #ifdef USE_PROPPATCH
                    421:                                PATCH_OPTION(sql);
                    422:                                PATCH_OPTION(stmt_update_prop);
                    423:                                PATCH_OPTION(stmt_delete_prop);
                    424:                                PATCH_OPTION(stmt_select_prop);
                    425:                                PATCH_OPTION(stmt_select_propnames);
                    426: 
                    427:                                PATCH_OPTION(stmt_delete_uri);
                    428:                                PATCH_OPTION(stmt_move_uri);
                    429:                                PATCH_OPTION(stmt_copy_uri);
                    430: 
                    431:                                PATCH_OPTION(stmt_remove_lock);
                    432:                                PATCH_OPTION(stmt_refresh_lock);
                    433:                                PATCH_OPTION(stmt_create_lock);
                    434:                                PATCH_OPTION(stmt_read_lock);
                    435:                                PATCH_OPTION(stmt_read_lock_by_uri);
                    436: #endif
                    437:                        }
                    438:                }
                    439:        }
                    440: 
                    441:        return 0;
                    442: }
                    443: 
                    444: URIHANDLER_FUNC(mod_webdav_uri_handler) {
                    445:        plugin_data *p = p_d;
                    446: 
                    447:        UNUSED(srv);
                    448: 
                    449:        if (con->uri.path->used == 0) return HANDLER_GO_ON;
                    450: 
                    451:        mod_webdav_patch_connection(srv, con, p);
                    452: 
                    453:        if (!p->conf.enabled) return HANDLER_GO_ON;
                    454: 
                    455:        switch (con->request.http_method) {
                    456:        case HTTP_METHOD_OPTIONS:
                    457:                /* we fake a little bit but it makes MS W2k happy and it let's us mount the volume */
                    458:                response_header_overwrite(srv, con, CONST_STR_LEN("DAV"), CONST_STR_LEN("1,2"));
                    459:                response_header_overwrite(srv, con, CONST_STR_LEN("MS-Author-Via"), CONST_STR_LEN("DAV"));
                    460: 
                    461:                if (p->conf.is_readonly) {
                    462:                        response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND"));
                    463:                } else {
                    464:                        response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY, PROPPATCH, LOCK, UNLOCK"));
                    465:                }
                    466:                break;
                    467:        default:
                    468:                break;
                    469:        }
                    470: 
                    471:        /* not found */
                    472:        return HANDLER_GO_ON;
                    473: }
                    474: static int webdav_gen_prop_tag(server *srv, connection *con,
                    475:                char *prop_name,
                    476:                char *prop_ns,
                    477:                char *value,
                    478:                buffer *b) {
                    479: 
                    480:        UNUSED(srv);
                    481:        UNUSED(con);
                    482: 
                    483:        if (value) {
                    484:                buffer_append_string_len(b,CONST_STR_LEN("<"));
                    485:                buffer_append_string(b, prop_name);
                    486:                buffer_append_string_len(b, CONST_STR_LEN(" xmlns=\""));
                    487:                buffer_append_string(b, prop_ns);
                    488:                buffer_append_string_len(b, CONST_STR_LEN("\">"));
                    489: 
                    490:                buffer_append_string(b, value);
                    491: 
                    492:                buffer_append_string_len(b,CONST_STR_LEN("</"));
                    493:                buffer_append_string(b, prop_name);
                    494:                buffer_append_string_len(b, CONST_STR_LEN(">"));
                    495:        } else {
                    496:                buffer_append_string_len(b,CONST_STR_LEN("<"));
                    497:                buffer_append_string(b, prop_name);
                    498:                buffer_append_string_len(b, CONST_STR_LEN(" xmlns=\""));
                    499:                buffer_append_string(b, prop_ns);
                    500:                buffer_append_string_len(b, CONST_STR_LEN("\"/>"));
                    501:        }
                    502: 
                    503:        return 0;
                    504: }
                    505: 
                    506: 
                    507: static int webdav_gen_response_status_tag(server *srv, connection *con, physical *dst, int status, buffer *b) {
                    508:        UNUSED(srv);
                    509: 
                    510:        buffer_append_string_len(b,CONST_STR_LEN("<D:response xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n"));
                    511: 
                    512:        buffer_append_string_len(b,CONST_STR_LEN("<D:href>\n"));
                    513:        buffer_append_string_buffer(b, dst->rel_path);
                    514:        buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n"));
                    515:        buffer_append_string_len(b,CONST_STR_LEN("<D:status>\n"));
                    516: 
                    517:        if (con->request.http_version == HTTP_VERSION_1_1) {
                    518:                buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 "));
                    519:        } else {
                    520:                buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 "));
                    521:        }
                    522:        buffer_append_long(b, status);
                    523:        buffer_append_string_len(b, CONST_STR_LEN(" "));
                    524:        buffer_append_string(b, get_http_status_name(status));
                    525: 
                    526:        buffer_append_string_len(b,CONST_STR_LEN("</D:status>\n"));
                    527:        buffer_append_string_len(b,CONST_STR_LEN("</D:response>\n"));
                    528: 
                    529:        return 0;
                    530: }
                    531: 
                    532: static int webdav_delete_file(server *srv, connection *con, plugin_data *p, physical *dst, buffer *b) {
                    533:        int status = 0;
                    534: 
                    535:        /* try to unlink it */
                    536:        if (-1 == unlink(dst->path->ptr)) {
                    537:                switch(errno) {
                    538:                case EACCES:
                    539:                case EPERM:
                    540:                        /* 403 */
                    541:                        status = 403;
                    542:                        break;
                    543:                default:
                    544:                        status = 501;
                    545:                        break;
                    546:                }
                    547:                webdav_gen_response_status_tag(srv, con, dst, status, b);
                    548:        } else {
                    549: #ifdef USE_PROPPATCH
                    550:                sqlite3_stmt *stmt = p->conf.stmt_delete_uri;
                    551: 
                    552:                if (!stmt) {
                    553:                        status = 403;
                    554:                        webdav_gen_response_status_tag(srv, con, dst, status, b);
                    555:                } else {
                    556:                        sqlite3_reset(stmt);
                    557: 
                    558:                        /* bind the values to the insert */
                    559: 
                    560:                        sqlite3_bind_text(stmt, 1,
                    561:                                          dst->rel_path->ptr,
                    562:                                          dst->rel_path->used - 1,
                    563:                                          SQLITE_TRANSIENT);
                    564: 
                    565:                        if (SQLITE_DONE != sqlite3_step(stmt)) {
                    566:                                /* */
                    567:                        }
                    568:                }
                    569: #else
                    570:                UNUSED(p);
                    571: #endif
                    572:        }
                    573: 
                    574:        return (status != 0);
                    575: }
                    576: 
                    577: static int webdav_delete_dir(server *srv, connection *con, plugin_data *p, physical *dst, buffer *b) {
                    578:        DIR *dir;
                    579:        int have_multi_status = 0;
                    580:        physical d;
                    581: 
                    582:        d.path = buffer_init();
                    583:        d.rel_path = buffer_init();
                    584: 
                    585:        if (NULL != (dir = opendir(dst->path->ptr))) {
                    586:                struct dirent *de;
                    587: 
                    588:                while(NULL != (de = readdir(dir))) {
                    589:                        struct stat st;
                    590:                        int status = 0;
                    591: 
                    592:                        if ((de->d_name[0] == '.' && de->d_name[1] == '\0')  ||
                    593:                            (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) {
                    594:                                continue;
                    595:                                /* ignore the parent dir */
                    596:                        }
                    597: 
                    598:                        buffer_copy_string_buffer(d.path, dst->path);
                    599:                        BUFFER_APPEND_SLASH(d.path);
                    600:                        buffer_append_string(d.path, de->d_name);
                    601: 
                    602:                        buffer_copy_string_buffer(d.rel_path, dst->rel_path);
                    603:                        BUFFER_APPEND_SLASH(d.rel_path);
                    604:                        buffer_append_string(d.rel_path, de->d_name);
                    605: 
                    606:                        /* stat and unlink afterwards */
                    607:                        if (-1 == stat(d.path->ptr, &st)) {
                    608:                                /* don't about it yet, rmdir will fail too */
                    609:                        } else if (S_ISDIR(st.st_mode)) {
                    610:                                have_multi_status = webdav_delete_dir(srv, con, p, &d, b);
                    611: 
                    612:                                /* try to unlink it */
                    613:                                if (-1 == rmdir(d.path->ptr)) {
                    614:                                        switch(errno) {
                    615:                                        case EACCES:
                    616:                                        case EPERM:
                    617:                                                /* 403 */
                    618:                                                status = 403;
                    619:                                                break;
                    620:                                        default:
                    621:                                                status = 501;
                    622:                                                break;
                    623:                                        }
                    624:                                        have_multi_status = 1;
                    625: 
                    626:                                        webdav_gen_response_status_tag(srv, con, &d, status, b);
                    627:                                } else {
                    628: #ifdef USE_PROPPATCH
                    629:                                        sqlite3_stmt *stmt = p->conf.stmt_delete_uri;
                    630: 
                    631:                                        status = 0;
                    632: 
                    633:                                        if (stmt) {
                    634:                                                sqlite3_reset(stmt);
                    635: 
                    636:                                                /* bind the values to the insert */
                    637: 
                    638:                                                sqlite3_bind_text(stmt, 1,
                    639:                                                                  d.rel_path->ptr,
                    640:                                                                  d.rel_path->used - 1,
                    641:                                                                  SQLITE_TRANSIENT);
                    642: 
                    643:                                                if (SQLITE_DONE != sqlite3_step(stmt)) {
                    644:                                                        /* */
                    645:                                                }
                    646:                                        }
                    647: #endif
                    648:                                }
                    649:                        } else {
                    650:                                have_multi_status = webdav_delete_file(srv, con, p, &d, b);
                    651:                        }
                    652:                }
                    653:                closedir(dir);
                    654: 
                    655:                buffer_free(d.path);
                    656:                buffer_free(d.rel_path);
                    657:        }
                    658: 
                    659:        return have_multi_status;
                    660: }
                    661: 
                    662: static int webdav_copy_file(server *srv, connection *con, plugin_data *p, physical *src, physical *dst, int overwrite) {
                    663:        stream s;
                    664:        int status = 0, ofd;
                    665:        UNUSED(srv);
                    666:        UNUSED(con);
                    667: 
                    668:        if (stream_open(&s, src->path)) {
                    669:                return 403;
                    670:        }
                    671: 
                    672:        if (-1 == (ofd = open(dst->path->ptr, O_WRONLY|O_TRUNC|O_CREAT|(overwrite ? 0 : O_EXCL), WEBDAV_FILE_MODE))) {
                    673:                /* opening the destination failed for some reason */
                    674:                switch(errno) {
                    675:                case EEXIST:
                    676:                        status = 412;
                    677:                        break;
                    678:                case EISDIR:
                    679:                        status = 409;
                    680:                        break;
                    681:                case ENOENT:
                    682:                        /* at least one part in the middle wasn't existing */
                    683:                        status = 409;
                    684:                        break;
                    685:                default:
                    686:                        status = 403;
                    687:                        break;
                    688:                }
                    689:                stream_close(&s);
                    690:                return status;
                    691:        }
                    692: 
                    693:        if (-1 == write(ofd, s.start, s.size)) {
                    694:                switch(errno) {
                    695:                case ENOSPC:
                    696:                        status = 507;
                    697:                        break;
                    698:                default:
                    699:                        status = 403;
                    700:                        break;
                    701:                }
                    702:        }
                    703: 
                    704:        stream_close(&s);
                    705:        close(ofd);
                    706: 
                    707: #ifdef USE_PROPPATCH
                    708:        if (0 == status) {
                    709:                /* copy worked fine, copy connected properties */
                    710:                sqlite3_stmt *stmt = p->conf.stmt_copy_uri;
                    711: 
                    712:                if (stmt) {
                    713:                        sqlite3_reset(stmt);
                    714: 
                    715:                        /* bind the values to the insert */
                    716:                        sqlite3_bind_text(stmt, 1,
                    717:                                          dst->rel_path->ptr,
                    718:                                          dst->rel_path->used - 1,
                    719:                                          SQLITE_TRANSIENT);
                    720: 
                    721:                        sqlite3_bind_text(stmt, 2,
                    722:                                          src->rel_path->ptr,
                    723:                                          src->rel_path->used - 1,
                    724:                                          SQLITE_TRANSIENT);
                    725: 
                    726:                        if (SQLITE_DONE != sqlite3_step(stmt)) {
                    727:                                /* */
                    728:                        }
                    729:                }
                    730:        }
                    731: #else
                    732:        UNUSED(p);
                    733: #endif
                    734:        return status;
                    735: }
                    736: 
                    737: static int webdav_copy_dir(server *srv, connection *con, plugin_data *p, physical *src, physical *dst, int overwrite) {
                    738:        DIR *srcdir;
                    739:        int status = 0;
                    740: 
                    741:        if (NULL != (srcdir = opendir(src->path->ptr))) {
                    742:                struct dirent *de;
                    743:                physical s, d;
                    744: 
                    745:                s.path = buffer_init();
                    746:                s.rel_path = buffer_init();
                    747: 
                    748:                d.path = buffer_init();
                    749:                d.rel_path = buffer_init();
                    750: 
                    751:                while (NULL != (de = readdir(srcdir))) {
                    752:                        struct stat st;
                    753: 
                    754:                        if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
                    755:                            (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) {
                    756:                                continue;
                    757:                        }
                    758: 
                    759:                        buffer_copy_string_buffer(s.path, src->path);
                    760:                        BUFFER_APPEND_SLASH(s.path);
                    761:                        buffer_append_string(s.path, de->d_name);
                    762: 
                    763:                        buffer_copy_string_buffer(d.path, dst->path);
                    764:                        BUFFER_APPEND_SLASH(d.path);
                    765:                        buffer_append_string(d.path, de->d_name);
                    766: 
                    767:                        buffer_copy_string_buffer(s.rel_path, src->rel_path);
                    768:                        BUFFER_APPEND_SLASH(s.rel_path);
                    769:                        buffer_append_string(s.rel_path, de->d_name);
                    770: 
                    771:                        buffer_copy_string_buffer(d.rel_path, dst->rel_path);
                    772:                        BUFFER_APPEND_SLASH(d.rel_path);
                    773:                        buffer_append_string(d.rel_path, de->d_name);
                    774: 
                    775:                        if (-1 == stat(s.path->ptr, &st)) {
                    776:                                /* why ? */
                    777:                        } else if (S_ISDIR(st.st_mode)) {
                    778:                                /* a directory */
                    779:                                if (-1 == mkdir(d.path->ptr, WEBDAV_DIR_MODE) &&
                    780:                                    errno != EEXIST) {
                    781:                                        /* WTH ? */
                    782:                                } else {
                    783: #ifdef USE_PROPPATCH
                    784:                                        sqlite3_stmt *stmt = p->conf.stmt_copy_uri;
                    785: 
                    786:                                        if (0 != (status = webdav_copy_dir(srv, con, p, &s, &d, overwrite))) {
                    787:                                                break;
                    788:                                        }
                    789:                                        /* directory is copied, copy the properties too */
                    790: 
                    791:                                        if (stmt) {
                    792:                                                sqlite3_reset(stmt);
                    793: 
                    794:                                                /* bind the values to the insert */
                    795:                                                sqlite3_bind_text(stmt, 1,
                    796:                                                          dst->rel_path->ptr,
                    797:                                                          dst->rel_path->used - 1,
                    798:                                                          SQLITE_TRANSIENT);
                    799: 
                    800:                                                sqlite3_bind_text(stmt, 2,
                    801:                                                          src->rel_path->ptr,
                    802:                                                          src->rel_path->used - 1,
                    803:                                                          SQLITE_TRANSIENT);
                    804: 
                    805:                                                if (SQLITE_DONE != sqlite3_step(stmt)) {
                    806:                                                        /* */
                    807:                                                }
                    808:                                        }
                    809: #endif
                    810:                                }
                    811:                        } else if (S_ISREG(st.st_mode)) {
                    812:                                /* a plain file */
                    813:                                if (0 != (status = webdav_copy_file(srv, con, p, &s, &d, overwrite))) {
                    814:                                        break;
                    815:                                }
                    816:                        }
                    817:                }
                    818: 
                    819:                buffer_free(s.path);
                    820:                buffer_free(s.rel_path);
                    821:                buffer_free(d.path);
                    822:                buffer_free(d.rel_path);
                    823: 
                    824:                closedir(srcdir);
                    825:        }
                    826: 
                    827:        return status;
                    828: }
                    829: 
                    830: static int webdav_get_live_property(server *srv, connection *con, plugin_data *p, physical *dst, char *prop_name, buffer *b) {
                    831:        stat_cache_entry *sce = NULL;
                    832:        int found = 0;
                    833: 
                    834:        UNUSED(p);
                    835: 
                    836:        if (HANDLER_ERROR != (stat_cache_get_entry(srv, con, dst->path, &sce))) {
                    837:                char ctime_buf[] = "2005-08-18T07:27:16Z";
                    838:                char mtime_buf[] = "Thu, 18 Aug 2005 07:27:16 GMT";
                    839:                size_t k;
                    840: 
                    841:                if (0 == strcmp(prop_name, "resourcetype")) {
                    842:                        if (S_ISDIR(sce->st.st_mode)) {
                    843:                                buffer_append_string_len(b, CONST_STR_LEN("<D:resourcetype><D:collection/></D:resourcetype>"));
                    844:                                found = 1;
                    845:                        }
                    846:                } else if (0 == strcmp(prop_name, "getcontenttype")) {
                    847:                        if (S_ISDIR(sce->st.st_mode)) {
                    848:                                buffer_append_string_len(b, CONST_STR_LEN("<D:getcontenttype>httpd/unix-directory</D:getcontenttype>"));
                    849:                                found = 1;
                    850:                        } else if(S_ISREG(sce->st.st_mode)) {
                    851:                                for (k = 0; k < con->conf.mimetypes->used; k++) {
                    852:                                        data_string *ds = (data_string *)con->conf.mimetypes->data[k];
                    853: 
                    854:                                        if (ds->key->used == 0) continue;
                    855: 
                    856:                                        if (buffer_is_equal_right_len(dst->path, ds->key, ds->key->used - 1)) {
                    857:                                                buffer_append_string_len(b,CONST_STR_LEN("<D:getcontenttype>"));
                    858:                                                buffer_append_string_buffer(b, ds->value);
                    859:                                                buffer_append_string_len(b, CONST_STR_LEN("</D:getcontenttype>"));
                    860:                                                found = 1;
                    861: 
                    862:                                                break;
                    863:                                        }
                    864:                                }
                    865:                        }
                    866:                } else if (0 == strcmp(prop_name, "creationdate")) {
                    867:                        buffer_append_string_len(b, CONST_STR_LEN("<D:creationdate ns0:dt=\"dateTime.tz\">"));
                    868:                        strftime(ctime_buf, sizeof(ctime_buf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&(sce->st.st_ctime)));
                    869:                        buffer_append_string(b, ctime_buf);
                    870:                        buffer_append_string_len(b, CONST_STR_LEN("</D:creationdate>"));
                    871:                        found = 1;
                    872:                } else if (0 == strcmp(prop_name, "getlastmodified")) {
                    873:                        buffer_append_string_len(b,CONST_STR_LEN("<D:getlastmodified ns0:dt=\"dateTime.rfc1123\">"));
                    874:                        strftime(mtime_buf, sizeof(mtime_buf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(sce->st.st_mtime)));
                    875:                        buffer_append_string(b, mtime_buf);
                    876:                        buffer_append_string_len(b, CONST_STR_LEN("</D:getlastmodified>"));
                    877:                        found = 1;
                    878:                } else if (0 == strcmp(prop_name, "getcontentlength")) {
                    879:                        buffer_append_string_len(b,CONST_STR_LEN("<D:getcontentlength>"));
                    880:                        buffer_append_off_t(b, sce->st.st_size);
                    881:                        buffer_append_string_len(b, CONST_STR_LEN("</D:getcontentlength>"));
                    882:                        found = 1;
                    883:                } else if (0 == strcmp(prop_name, "getcontentlanguage")) {
                    884:                        buffer_append_string_len(b,CONST_STR_LEN("<D:getcontentlanguage>"));
                    885:                        buffer_append_string_len(b, CONST_STR_LEN("en"));
                    886:                        buffer_append_string_len(b, CONST_STR_LEN("</D:getcontentlanguage>"));
                    887:                        found = 1;
                    888:                }
                    889:        }
                    890: 
                    891:        return found ? 0 : -1;
                    892: }
                    893: 
                    894: static int webdav_get_property(server *srv, connection *con, plugin_data *p, physical *dst, char *prop_name, char *prop_ns, buffer *b) {
                    895:        if (0 == strcmp(prop_ns, "DAV:")) {
                    896:                /* a local 'live' property */
                    897:                return webdav_get_live_property(srv, con, p, dst, prop_name, b);
                    898:        } else {
                    899:                int found = 0;
                    900: #ifdef USE_PROPPATCH
                    901:                sqlite3_stmt *stmt = p->conf.stmt_select_prop;
                    902: 
                    903:                if (stmt) {
                    904:                        /* perhaps it is in sqlite3 */
                    905:                        sqlite3_reset(stmt);
                    906: 
                    907:                        /* bind the values to the insert */
                    908: 
                    909:                        sqlite3_bind_text(stmt, 1,
                    910:                                          dst->rel_path->ptr,
                    911:                                          dst->rel_path->used - 1,
                    912:                                          SQLITE_TRANSIENT);
                    913:                        sqlite3_bind_text(stmt, 2,
                    914:                                          prop_name,
                    915:                                          strlen(prop_name),
                    916:                                          SQLITE_TRANSIENT);
                    917:                        sqlite3_bind_text(stmt, 3,
                    918:                                          prop_ns,
                    919:                                          strlen(prop_ns),
                    920:                                          SQLITE_TRANSIENT);
                    921: 
                    922:                        /* it is the PK */
                    923:                        while (SQLITE_ROW == sqlite3_step(stmt)) {
                    924:                                /* there is a row for us, we only expect a single col 'value' */
                    925:                                webdav_gen_prop_tag(srv, con, prop_name, prop_ns, (char *)sqlite3_column_text(stmt, 0), b);
                    926:                                found = 1;
                    927:                        }
                    928:                }
                    929: #endif
                    930:                return found ? 0 : -1;
                    931:        }
                    932: 
                    933:        /* not found */
                    934:        return -1;
                    935: }
                    936: 
                    937: typedef struct {
                    938:        char *ns;
                    939:        char *prop;
                    940: } webdav_property;
                    941: 
                    942: static webdav_property live_properties[] = {
                    943:        { "DAV:", "creationdate" },
                    944:        { "DAV:", "displayname" },
                    945:        { "DAV:", "getcontentlanguage" },
                    946:        { "DAV:", "getcontentlength" },
                    947:        { "DAV:", "getcontenttype" },
                    948:        { "DAV:", "getetag" },
                    949:        { "DAV:", "getlastmodified" },
                    950:        { "DAV:", "resourcetype" },
                    951:        { "DAV:", "lockdiscovery" },
                    952:        { "DAV:", "source" },
                    953:        { "DAV:", "supportedlock" },
                    954: 
                    955:        { NULL, NULL }
                    956: };
                    957: 
                    958: typedef struct {
                    959:        webdav_property **ptr;
                    960: 
                    961:        size_t used;
                    962:        size_t size;
                    963: } webdav_properties;
                    964: 
                    965: static int webdav_get_props(server *srv, connection *con, plugin_data *p, physical *dst, webdav_properties *props, buffer *b_200, buffer *b_404) {
                    966:        size_t i;
                    967: 
                    968:        if (props) {
                    969:                for (i = 0; i < props->used; i++) {
                    970:                        webdav_property *prop;
                    971: 
                    972:                        prop = props->ptr[i];
                    973: 
                    974:                        if (0 != webdav_get_property(srv, con, p,
                    975:                                dst, prop->prop, prop->ns, b_200)) {
                    976:                                webdav_gen_prop_tag(srv, con, prop->prop, prop->ns, NULL, b_404);
                    977:                        }
                    978:                }
                    979:        } else {
                    980:                for (i = 0; live_properties[i].prop; i++) {
                    981:                        /* a local 'live' property */
                    982:                        webdav_get_live_property(srv, con, p, dst, live_properties[i].prop, b_200);
                    983:                }
                    984:        }
                    985: 
                    986:        return 0;
                    987: }
                    988: 
                    989: #ifdef USE_PROPPATCH
                    990: static int webdav_parse_chunkqueue(server *srv, connection *con, plugin_data *p, chunkqueue *cq, xmlDoc **ret_xml) {
                    991:        xmlParserCtxtPtr ctxt;
                    992:        xmlDoc *xml;
                    993:        int res;
                    994:        int err;
                    995: 
                    996:        chunk *c;
                    997: 
                    998:        UNUSED(con);
                    999: 
                   1000:        /* read the chunks in to the XML document */
                   1001:        ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
                   1002: 
                   1003:        for (c = cq->first; cq->bytes_out != cq->bytes_in; c = cq->first) {
                   1004:                size_t weWant = cq->bytes_out - cq->bytes_in;
                   1005:                size_t weHave;
                   1006: 
                   1007:                switch(c->type) {
                   1008:                case FILE_CHUNK:
                   1009:                        weHave = c->file.length - c->offset;
                   1010: 
                   1011:                        if (weHave > weWant) weHave = weWant;
                   1012: 
                   1013:                        /* xml chunks are always memory, mmap() is our friend */
                   1014:                        if (c->file.mmap.start == MAP_FAILED) {
                   1015:                                if (-1 == c->file.fd &&  /* open the file if not already open */
                   1016:                                    -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
                   1017:                                        log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
                   1018: 
                   1019:                                        return -1;
                   1020:                                }
                   1021: 
                   1022:                                if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
                   1023:                                        log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
                   1024:                                                        strerror(errno), c->file.name,  c->file.fd);
                   1025:                                        close(c->file.fd);
                   1026:                                        c->file.fd = -1;
                   1027: 
                   1028:                                        return -1;
                   1029:                                }
                   1030: 
                   1031:                                close(c->file.fd);
                   1032:                                c->file.fd = -1;
                   1033: 
                   1034:                                c->file.mmap.length = c->file.length;
                   1035: 
                   1036:                                /* chunk_reset() or chunk_free() will cleanup for us */
                   1037:                        }
                   1038: 
                   1039:                        if (XML_ERR_OK != (err = xmlParseChunk(ctxt, c->file.mmap.start + c->offset, weHave, 0))) {
                   1040:                                log_error_write(srv, __FILE__, __LINE__, "sodd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err);
                   1041:                        }
                   1042: 
                   1043:                        c->offset += weHave;
                   1044:                        cq->bytes_out += weHave;
                   1045: 
                   1046:                        break;
                   1047:                case MEM_CHUNK:
                   1048:                        /* append to the buffer */
                   1049:                        weHave = c->mem->used - 1 - c->offset;
                   1050: 
                   1051:                        if (weHave > weWant) weHave = weWant;
                   1052: 
                   1053:                        if (p->conf.log_xml) {
                   1054:                                log_error_write(srv, __FILE__, __LINE__, "ss", "XML-request-body:", c->mem->ptr + c->offset);
                   1055:                        }
                   1056: 
                   1057:                        if (XML_ERR_OK != (err = xmlParseChunk(ctxt, c->mem->ptr + c->offset, weHave, 0))) {
                   1058:                                log_error_write(srv, __FILE__, __LINE__, "sodd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err);
                   1059:                        }
                   1060: 
                   1061:                        c->offset += weHave;
                   1062:                        cq->bytes_out += weHave;
                   1063: 
                   1064:                        break;
                   1065:                case UNUSED_CHUNK:
                   1066:                        break;
                   1067:                }
                   1068:                chunkqueue_remove_finished_chunks(cq);
                   1069:        }
                   1070: 
                   1071: 
                   1072:        switch ((err = xmlParseChunk(ctxt, 0, 0, 1))) {
                   1073:        case XML_ERR_DOCUMENT_END:
                   1074:        case XML_ERR_OK:
                   1075:                break;
                   1076:        default:
                   1077:                log_error_write(srv, __FILE__, __LINE__, "sd", "xmlParseChunk failed at final packet:", err);
                   1078:                break;
                   1079:        }
                   1080: 
                   1081:        xml = ctxt->myDoc;
                   1082:        res = ctxt->wellFormed;
                   1083:        xmlFreeParserCtxt(ctxt);
                   1084: 
                   1085:        if (res == 0) {
                   1086:                xmlFreeDoc(xml);
                   1087:        } else {
                   1088:                *ret_xml = xml;
                   1089:        }
                   1090: 
                   1091:        return res;
                   1092: }
                   1093: #endif
                   1094: 
                   1095: #ifdef USE_LOCKS
                   1096: static int webdav_lockdiscovery(server *srv, connection *con,
                   1097:                buffer *locktoken, const char *lockscope, const char *locktype, int depth) {
                   1098: 
                   1099:        buffer *b;
                   1100: 
                   1101:        response_header_overwrite(srv, con, CONST_STR_LEN("Lock-Token"), CONST_BUF_LEN(locktoken));
                   1102: 
                   1103:        response_header_overwrite(srv, con,
                   1104:                CONST_STR_LEN("Content-Type"),
                   1105:                CONST_STR_LEN("text/xml; charset=\"utf-8\""));
                   1106: 
                   1107:        b = chunkqueue_get_append_buffer(con->write_queue);
                   1108: 
                   1109:        buffer_copy_string_len(b, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"));
                   1110: 
                   1111:        buffer_append_string_len(b,CONST_STR_LEN("<D:prop xmlns:D=\"DAV:\" xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n"));
                   1112:        buffer_append_string_len(b,CONST_STR_LEN("<D:lockdiscovery>\n"));
                   1113:        buffer_append_string_len(b,CONST_STR_LEN("<D:activelock>\n"));
                   1114: 
                   1115:        buffer_append_string_len(b,CONST_STR_LEN("<D:lockscope>"));
                   1116:        buffer_append_string_len(b,CONST_STR_LEN("<D:"));
                   1117:        buffer_append_string(b, lockscope);
                   1118:        buffer_append_string_len(b, CONST_STR_LEN("/>"));
                   1119:        buffer_append_string_len(b,CONST_STR_LEN("</D:lockscope>\n"));
                   1120: 
                   1121:        buffer_append_string_len(b,CONST_STR_LEN("<D:locktype>"));
                   1122:        buffer_append_string_len(b,CONST_STR_LEN("<D:"));
                   1123:        buffer_append_string(b, locktype);
                   1124:        buffer_append_string_len(b, CONST_STR_LEN("/>"));
                   1125:        buffer_append_string_len(b,CONST_STR_LEN("</D:locktype>\n"));
                   1126: 
                   1127:        buffer_append_string_len(b,CONST_STR_LEN("<D:depth>"));
                   1128:        buffer_append_string(b, depth == 0 ? "0" : "infinity");
                   1129:        buffer_append_string_len(b,CONST_STR_LEN("</D:depth>\n"));
                   1130: 
                   1131:        buffer_append_string_len(b,CONST_STR_LEN("<D:timeout>"));
                   1132:        buffer_append_string_len(b, CONST_STR_LEN("Second-600"));
                   1133:        buffer_append_string_len(b,CONST_STR_LEN("</D:timeout>\n"));
                   1134: 
                   1135:        buffer_append_string_len(b,CONST_STR_LEN("<D:owner>"));
                   1136:        buffer_append_string_len(b,CONST_STR_LEN("</D:owner>\n"));
                   1137: 
                   1138:        buffer_append_string_len(b,CONST_STR_LEN("<D:locktoken>"));
                   1139:        buffer_append_string_len(b, CONST_STR_LEN("<D:href>"));
                   1140:        buffer_append_string_buffer(b, locktoken);
                   1141:        buffer_append_string_len(b, CONST_STR_LEN("</D:href>"));
                   1142:        buffer_append_string_len(b,CONST_STR_LEN("</D:locktoken>\n"));
                   1143: 
                   1144:        buffer_append_string_len(b,CONST_STR_LEN("</D:activelock>\n"));
                   1145:        buffer_append_string_len(b,CONST_STR_LEN("</D:lockdiscovery>\n"));
                   1146:        buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n"));
                   1147: 
                   1148:        return 0;
                   1149: }
                   1150: #endif
                   1151: 
                   1152: /**
                   1153:  * check if resource is having the right locks to access to resource
                   1154:  *
                   1155:  *
                   1156:  *
                   1157:  */
                   1158: static int webdav_has_lock(server *srv, connection *con, plugin_data *p, buffer *uri) {
                   1159:        int has_lock = 1;
                   1160: 
                   1161: #ifdef USE_LOCKS
                   1162:        data_string *ds;
                   1163:        UNUSED(srv);
                   1164: 
                   1165:        /**
                   1166:         * This implementation is more fake than real
                   1167:         * we need a parser for the If: header to really handle the full scope
                   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:         * - a tagged check:
                   1172:         *   if http://127.0.0.1:1025/dav/litmus/lockme is locked with
                   1173:         *   opaquelocktoken:2165478d-0611-49c4-be92-e790d68a38f1, go on
                   1174:         *
                   1175:         * X-Litmus: locks: 16 (fail_cond_put)
                   1176:         * If: (<DAV:no-lock> ["-1622396671"])
                   1177:         * - untagged:
                   1178:         *   go on if the resource has the etag [...] and the lock
                   1179:         */
                   1180:        if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) {
                   1181:                /* Ooh, ooh. A if tag, now the fun begins.
                   1182:                 *
                   1183:                 * this can only work with a real parser
                   1184:                 **/
                   1185:        } else {
                   1186:                /* we didn't provided a lock-token -> */
                   1187:                /* if the resource is locked -> 423 */
                   1188: 
                   1189:                sqlite3_stmt *stmt = p->conf.stmt_read_lock_by_uri;
                   1190: 
                   1191:                sqlite3_reset(stmt);
                   1192: 
                   1193:                sqlite3_bind_text(stmt, 1,
                   1194:                          CONST_BUF_LEN(uri),
                   1195:                          SQLITE_TRANSIENT);
                   1196: 
                   1197:                while (SQLITE_ROW == sqlite3_step(stmt)) {
                   1198:                        has_lock = 0;
                   1199:                }
                   1200:        }
                   1201: #else
                   1202:        UNUSED(srv);
                   1203:        UNUSED(con);
                   1204:        UNUSED(p);
                   1205:        UNUSED(uri);
                   1206: #endif
                   1207: 
                   1208:        return has_lock;
                   1209: }
                   1210: 
                   1211: URIHANDLER_FUNC(mod_webdav_subrequest_handler) {
                   1212:        plugin_data *p = p_d;
                   1213:        buffer *b;
                   1214:        DIR *dir;
                   1215:        data_string *ds;
                   1216:        int depth = -1;
                   1217:        struct stat st;
                   1218:        buffer *prop_200;
                   1219:        buffer *prop_404;
                   1220:        webdav_properties *req_props;
                   1221:        stat_cache_entry *sce = NULL;
                   1222: 
                   1223:        UNUSED(srv);
                   1224: 
                   1225:        if (!p->conf.enabled) return HANDLER_GO_ON;
                   1226:        /* physical path is setup */
                   1227:        if (con->physical.path->used == 0) return HANDLER_GO_ON;
                   1228: 
                   1229:        /* PROPFIND need them */
                   1230:        if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Depth"))) {
                   1231:                depth = strtol(ds->value->ptr, NULL, 10);
                   1232:        }
                   1233: 
                   1234:        switch (con->request.http_method) {
                   1235:        case HTTP_METHOD_PROPFIND:
                   1236:                /* they want to know the properties of the directory */
                   1237:                req_props = NULL;
                   1238: 
                   1239:                /* is there a content-body ? */
                   1240: 
                   1241:                switch (stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
                   1242:                case HANDLER_ERROR:
                   1243:                        if (errno == ENOENT) {
                   1244:                                con->http_status = 404;
                   1245:                                return HANDLER_FINISHED;
                   1246:                        }
                   1247:                        break;
                   1248:                default:
                   1249:                        break;
                   1250:                }
                   1251: 
                   1252: 
                   1253: #ifdef USE_PROPPATCH
                   1254:                /* any special requests or just allprop ? */
                   1255:                if (con->request.content_length) {
                   1256:                        xmlDocPtr xml;
                   1257: 
                   1258:                        if (1 == webdav_parse_chunkqueue(srv, con, p, con->request_content_queue, &xml)) {
                   1259:                                xmlNode *rootnode = xmlDocGetRootElement(xml);
                   1260: 
                   1261:                                assert(rootnode);
                   1262: 
                   1263:                                if (0 == xmlStrcmp(rootnode->name, BAD_CAST "propfind")) {
                   1264:                                        xmlNode *cmd;
                   1265: 
                   1266:                                        req_props = calloc(1, sizeof(*req_props));
                   1267: 
                   1268:                                        for (cmd = rootnode->children; cmd; cmd = cmd->next) {
                   1269: 
                   1270:                                                if (0 == xmlStrcmp(cmd->name, BAD_CAST "prop")) {
                   1271:                                                        /* get prop by name */
                   1272:                                                        xmlNode *prop;
                   1273: 
                   1274:                                                        for (prop = cmd->children; prop; prop = prop->next) {
                   1275:                                                                if (prop->type == XML_TEXT_NODE) continue; /* ignore WS */
                   1276: 
                   1277:                                                                if (prop->ns &&
                   1278:                                                                    (0 == xmlStrcmp(prop->ns->href, BAD_CAST "")) &&
                   1279:                                                                    (0 != xmlStrcmp(prop->ns->prefix, BAD_CAST ""))) {
                   1280:                                                                        size_t i;
                   1281:                                                                        log_error_write(srv, __FILE__, __LINE__, "ss",
                   1282:                                                                                        "no name space for:",
                   1283:                                                                                        prop->name);
                   1284: 
                   1285:                                                                        xmlFreeDoc(xml);
                   1286: 
                   1287:                                                                        for (i = 0; i < req_props->used; i++) {
                   1288:                                                                                free(req_props->ptr[i]->ns);
                   1289:                                                                                free(req_props->ptr[i]->prop);
                   1290:                                                                                free(req_props->ptr[i]);
                   1291:                                                                        }
                   1292:                                                                        free(req_props->ptr);
                   1293:                                                                        free(req_props);
                   1294: 
                   1295:                                                                        con->http_status = 400;
                   1296:                                                                        return HANDLER_FINISHED;
                   1297:                                                                }
                   1298: 
                   1299:                                                                /* add property to requested list */
                   1300:                                                                if (req_props->size == 0) {
                   1301:                                                                        req_props->size = 16;
                   1302:                                                                        req_props->ptr = malloc(sizeof(*(req_props->ptr)) * req_props->size);
                   1303:                                                                } else if (req_props->used == req_props->size) {
                   1304:                                                                        req_props->size += 16;
                   1305:                                                                        req_props->ptr = realloc(req_props->ptr, sizeof(*(req_props->ptr)) * req_props->size);
                   1306:                                                                }
                   1307: 
                   1308:                                                                req_props->ptr[req_props->used] = malloc(sizeof(webdav_property));
                   1309:                                                                req_props->ptr[req_props->used]->ns = (char *)xmlStrdup(prop->ns ? prop->ns->href : (xmlChar *)"");
                   1310:                                                                req_props->ptr[req_props->used]->prop = (char *)xmlStrdup(prop->name);
                   1311:                                                                req_props->used++;
                   1312:                                                        }
                   1313:                                                } else if (0 == xmlStrcmp(cmd->name, BAD_CAST "propname")) {
                   1314:                                                        sqlite3_stmt *stmt = p->conf.stmt_select_propnames;
                   1315: 
                   1316:                                                        if (stmt) {
                   1317:                                                                /* get all property names (EMPTY) */
                   1318:                                                                sqlite3_reset(stmt);
                   1319:                                                                /* bind the values to the insert */
                   1320: 
                   1321:                                                                sqlite3_bind_text(stmt, 1,
                   1322:                                                                                  con->uri.path->ptr,
                   1323:                                                                                  con->uri.path->used - 1,
                   1324:                                                                                  SQLITE_TRANSIENT);
                   1325: 
                   1326:                                                                if (SQLITE_DONE != sqlite3_step(stmt)) {
                   1327:                                                                }
                   1328:                                                        }
                   1329:                                                } else if (0 == xmlStrcmp(cmd->name, BAD_CAST "allprop")) {
                   1330:                                                        /* get all properties (EMPTY) */
                   1331:                                                }
                   1332:                                        }
                   1333:                                }
                   1334: 
                   1335:                                xmlFreeDoc(xml);
                   1336:                        } else {
                   1337:                                con->http_status = 400;
                   1338:                                return HANDLER_FINISHED;
                   1339:                        }
                   1340:                }
                   1341: #endif
                   1342:                con->http_status = 207;
                   1343: 
                   1344:                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\""));
                   1345: 
                   1346:                b = chunkqueue_get_append_buffer(con->write_queue);
                   1347: 
                   1348:                buffer_copy_string_len(b, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"));
                   1349: 
                   1350:                buffer_append_string_len(b,CONST_STR_LEN("<D:multistatus xmlns:D=\"DAV:\" xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n"));
                   1351: 
                   1352:                /* allprop */
                   1353: 
                   1354:                prop_200 = buffer_init();
                   1355:                prop_404 = buffer_init();
                   1356: 
                   1357:                switch(depth) {
                   1358:                case 0:
                   1359:                        /* Depth: 0 */
                   1360:                        webdav_get_props(srv, con, p, &(con->physical), req_props, prop_200, prop_404);
                   1361: 
                   1362:                        buffer_append_string_len(b,CONST_STR_LEN("<D:response>\n"));
                   1363:                        buffer_append_string_len(b,CONST_STR_LEN("<D:href>"));
                   1364:                        buffer_append_string_buffer(b, con->uri.scheme);
                   1365:                        buffer_append_string_len(b,CONST_STR_LEN("://"));
                   1366:                        buffer_append_string_buffer(b, con->uri.authority);
                   1367:                        buffer_append_string_encoded(b, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI);
                   1368:                        buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n"));
                   1369: 
                   1370:                        if (!buffer_is_empty(prop_200)) {
                   1371:                                buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n"));
                   1372:                                buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n"));
                   1373: 
                   1374:                                buffer_append_string_buffer(b, prop_200);
                   1375: 
                   1376:                                buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n"));
                   1377: 
                   1378:                                buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 200 OK</D:status>\n"));
                   1379: 
                   1380:                                buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n"));
                   1381:                        }
                   1382:                        if (!buffer_is_empty(prop_404)) {
                   1383:                                buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n"));
                   1384:                                buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n"));
                   1385: 
                   1386:                                buffer_append_string_buffer(b, prop_404);
                   1387: 
                   1388:                                buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n"));
                   1389: 
                   1390:                                buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 404 Not Found</D:status>\n"));
                   1391: 
                   1392:                                buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n"));
                   1393:                        }
                   1394: 
                   1395:                        buffer_append_string_len(b,CONST_STR_LEN("</D:response>\n"));
                   1396: 
                   1397:                        break;
                   1398:                case 1:
                   1399:                        if (NULL != (dir = opendir(con->physical.path->ptr))) {
                   1400:                                struct dirent *de;
                   1401:                                physical d;
                   1402:                                physical *dst = &(con->physical);
                   1403: 
                   1404:                                d.path = buffer_init();
                   1405:                                d.rel_path = buffer_init();
                   1406: 
                   1407:                                while(NULL != (de = readdir(dir))) {
                   1408:                                        if (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0') {
                   1409:                                                continue;
                   1410:                                                /* ignore the parent dir */
                   1411:                                        }
                   1412: 
                   1413:                                        buffer_copy_string_buffer(d.path, dst->path);
                   1414:                                        BUFFER_APPEND_SLASH(d.path);
                   1415: 
                   1416:                                        buffer_copy_string_buffer(d.rel_path, dst->rel_path);
                   1417:                                        BUFFER_APPEND_SLASH(d.rel_path);
                   1418: 
                   1419:                                        if (de->d_name[0] == '.' && de->d_name[1] == '\0') {
                   1420:                                                /* don't append the . */
                   1421:                                        } else {
                   1422:                                                buffer_append_string(d.path, de->d_name);
                   1423:                                                buffer_append_string(d.rel_path, de->d_name);
                   1424:                                        }
                   1425: 
                   1426:                                        buffer_reset(prop_200);
                   1427:                                        buffer_reset(prop_404);
                   1428: 
                   1429:                                        webdav_get_props(srv, con, p, &d, req_props, prop_200, prop_404);
                   1430: 
                   1431:                                        buffer_append_string_len(b,CONST_STR_LEN("<D:response>\n"));
                   1432:                                        buffer_append_string_len(b,CONST_STR_LEN("<D:href>"));
                   1433:                                        buffer_append_string_buffer(b, con->uri.scheme);
                   1434:                                        buffer_append_string_len(b,CONST_STR_LEN("://"));
                   1435:                                        buffer_append_string_buffer(b, con->uri.authority);
                   1436:                                        buffer_append_string_encoded(b, CONST_BUF_LEN(d.rel_path), ENCODING_REL_URI);
                   1437:                                        buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n"));
                   1438: 
                   1439:                                        if (!buffer_is_empty(prop_200)) {
                   1440:                                                buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n"));
                   1441:                                                buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n"));
                   1442: 
                   1443:                                                buffer_append_string_buffer(b, prop_200);
                   1444: 
                   1445:                                                buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n"));
                   1446: 
                   1447:                                                buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 200 OK</D:status>\n"));
                   1448: 
                   1449:                                                buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n"));
                   1450:                                        }
                   1451:                                        if (!buffer_is_empty(prop_404)) {
                   1452:                                                buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n"));
                   1453:                                                buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n"));
                   1454: 
                   1455:                                                buffer_append_string_buffer(b, prop_404);
                   1456: 
                   1457:                                                buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n"));
                   1458: 
                   1459:                                                buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 404 Not Found</D:status>\n"));
                   1460: 
                   1461:                                                buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n"));
                   1462:                                        }
                   1463: 
                   1464:                                        buffer_append_string_len(b,CONST_STR_LEN("</D:response>\n"));
                   1465:                                }
                   1466:                                closedir(dir);
                   1467:                                buffer_free(d.path);
                   1468:                                buffer_free(d.rel_path);
                   1469:                        }
                   1470:                        break;
                   1471:                }
                   1472: 
                   1473:                if (req_props) {
                   1474:                        size_t i;
                   1475:                        for (i = 0; i < req_props->used; i++) {
                   1476:                                free(req_props->ptr[i]->ns);
                   1477:                                free(req_props->ptr[i]->prop);
                   1478:                                free(req_props->ptr[i]);
                   1479:                        }
                   1480:                        free(req_props->ptr);
                   1481:                        free(req_props);
                   1482:                }
                   1483: 
                   1484:                buffer_free(prop_200);
                   1485:                buffer_free(prop_404);
                   1486: 
                   1487:                buffer_append_string_len(b,CONST_STR_LEN("</D:multistatus>\n"));
                   1488: 
                   1489:                if (p->conf.log_xml) {
                   1490:                        log_error_write(srv, __FILE__, __LINE__, "sb", "XML-response-body:", b);
                   1491:                }
                   1492:                con->file_finished = 1;
                   1493: 
                   1494:                return HANDLER_FINISHED;
                   1495:        case HTTP_METHOD_MKCOL:
                   1496:                if (p->conf.is_readonly) {
                   1497:                        con->http_status = 403;
                   1498:                        return HANDLER_FINISHED;
                   1499:                }
                   1500: 
                   1501:                if (con->request.content_length != 0) {
                   1502:                        /* we don't support MKCOL with a body */
                   1503:                        con->http_status = 415;
                   1504: 
                   1505:                        return HANDLER_FINISHED;
                   1506:                }
                   1507: 
                   1508:                /* let's create the directory */
                   1509: 
                   1510:                if (-1 == mkdir(con->physical.path->ptr, WEBDAV_DIR_MODE)) {
                   1511:                        switch(errno) {
                   1512:                        case EPERM:
                   1513:                                con->http_status = 403;
                   1514:                                break;
                   1515:                        case ENOENT:
                   1516:                        case ENOTDIR:
                   1517:                                con->http_status = 409;
                   1518:                                break;
                   1519:                        case EEXIST:
                   1520:                        default:
                   1521:                                con->http_status = 405; /* not allowed */
                   1522:                                break;
                   1523:                        }
                   1524:                } else {
                   1525:                        con->http_status = 201;
                   1526:                        con->file_finished = 1;
                   1527:                }
                   1528: 
                   1529:                return HANDLER_FINISHED;
                   1530:        case HTTP_METHOD_DELETE:
                   1531:                if (p->conf.is_readonly) {
                   1532:                        con->http_status = 403;
                   1533:                        return HANDLER_FINISHED;
                   1534:                }
                   1535: 
                   1536:                /* does the client have a lock for this connection ? */
                   1537:                if (!webdav_has_lock(srv, con, p, con->uri.path)) {
                   1538:                        con->http_status = 423;
                   1539:                        return HANDLER_FINISHED;
                   1540:                }
                   1541: 
                   1542:                /* stat and unlink afterwards */
                   1543:                if (-1 == stat(con->physical.path->ptr, &st)) {
                   1544:                        /* don't about it yet, unlink will fail too */
                   1545:                        switch(errno) {
                   1546:                        case ENOENT:
                   1547:                                 con->http_status = 404;
                   1548:                                 break;
                   1549:                        default:
                   1550:                                 con->http_status = 403;
                   1551:                                 break;
                   1552:                        }
                   1553:                } else if (S_ISDIR(st.st_mode)) {
                   1554:                        buffer *multi_status_resp = buffer_init();
                   1555: 
                   1556:                        if (webdav_delete_dir(srv, con, p, &(con->physical), multi_status_resp)) {
                   1557:                                /* we got an error somewhere in between, build a 207 */
                   1558:                                response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\""));
                   1559: 
                   1560:                                b = chunkqueue_get_append_buffer(con->write_queue);
                   1561: 
                   1562:                                buffer_copy_string_len(b, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"));
                   1563: 
                   1564:                                buffer_append_string_len(b,CONST_STR_LEN("<D:multistatus xmlns:D=\"DAV:\">\n"));
                   1565: 
                   1566:                                buffer_append_string_buffer(b, multi_status_resp);
                   1567: 
                   1568:                                buffer_append_string_len(b,CONST_STR_LEN("</D:multistatus>\n"));
                   1569: 
                   1570:                                if (p->conf.log_xml) {
                   1571:                                        log_error_write(srv, __FILE__, __LINE__, "sb", "XML-response-body:", b);
                   1572:                                }
                   1573: 
                   1574:                                con->http_status = 207;
                   1575:                                con->file_finished = 1;
                   1576:                        } else {
                   1577:                                /* everything went fine, remove the directory */
                   1578: 
                   1579:                                if (-1 == rmdir(con->physical.path->ptr)) {
                   1580:                                        switch(errno) {
                   1581:                                        case ENOENT:
                   1582:                                                con->http_status = 404;
                   1583:                                                break;
                   1584:                                        default:
                   1585:                                                con->http_status = 501;
                   1586:                                                break;
                   1587:                                        }
                   1588:                                } else {
                   1589:                                        con->http_status = 204;
                   1590:                                }
                   1591:                        }
                   1592: 
                   1593:                        buffer_free(multi_status_resp);
                   1594:                } else if (-1 == unlink(con->physical.path->ptr)) {
                   1595:                        switch(errno) {
                   1596:                        case EPERM:
                   1597:                                con->http_status = 403;
                   1598:                                break;
                   1599:                        case ENOENT:
                   1600:                                con->http_status = 404;
                   1601:                                break;
                   1602:                        default:
                   1603:                                con->http_status = 501;
                   1604:                                break;
                   1605:                        }
                   1606:                } else {
                   1607:                        con->http_status = 204;
                   1608:                }
                   1609:                return HANDLER_FINISHED;
                   1610:        case HTTP_METHOD_PUT: {
                   1611:                int fd;
                   1612:                chunkqueue *cq = con->request_content_queue;
                   1613:                chunk *c;
                   1614:                data_string *ds_range;
                   1615: 
                   1616:                if (p->conf.is_readonly) {
                   1617:                        con->http_status = 403;
                   1618:                        return HANDLER_FINISHED;
                   1619:                }
                   1620: 
                   1621:                /* is a exclusive lock set on the source */
                   1622:                if (!webdav_has_lock(srv, con, p, con->uri.path)) {
                   1623:                        con->http_status = 423;
                   1624:                        return HANDLER_FINISHED;
                   1625:                }
                   1626: 
                   1627: 
                   1628:                assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
                   1629: 
                   1630:                /* RFC2616 Section 9.6 PUT requires us to send 501 on all Content-* we don't support
                   1631:                 * - most important Content-Range
                   1632:                 *
                   1633:                 *
                   1634:                 * Example: Content-Range: bytes 100-1037/1038 */
                   1635: 
                   1636:                if (NULL != (ds_range = (data_string *)array_get_element(con->request.headers, "Content-Range"))) {
                   1637:                        const char *num = ds_range->value->ptr;
                   1638:                        off_t offset;
                   1639:                        char *err = NULL;
                   1640: 
                   1641:                        if (0 != strncmp(num, "bytes ", 6)) {
                   1642:                                con->http_status = 501; /* not implemented */
                   1643: 
                   1644:                                return HANDLER_FINISHED;
                   1645:                        }
                   1646: 
                   1647:                        /* we only support <num>- ... */
                   1648: 
                   1649:                        num += 6;
                   1650: 
                   1651:                        /* skip WS */
                   1652:                        while (*num == ' ' || *num == '\t') num++;
                   1653: 
                   1654:                        if (*num == '\0') {
                   1655:                                con->http_status = 501; /* not implemented */
                   1656: 
                   1657:                                return HANDLER_FINISHED;
                   1658:                        }
                   1659: 
                   1660:                        offset = strtoll(num, &err, 10);
                   1661: 
                   1662:                        if (*err != '-' || offset < 0) {
                   1663:                                con->http_status = 501; /* not implemented */
                   1664: 
                   1665:                                return HANDLER_FINISHED;
                   1666:                        }
                   1667: 
                   1668:                        if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY, WEBDAV_FILE_MODE))) {
                   1669:                                switch (errno) {
                   1670:                                case ENOENT:
                   1671:                                        con->http_status = 404; /* not found */
                   1672:                                        break;
                   1673:                                default:
                   1674:                                        con->http_status = 403; /* not found */
                   1675:                                        break;
                   1676:                                }
                   1677:                                return HANDLER_FINISHED;
                   1678:                        }
                   1679: 
                   1680:                        if (-1 == lseek(fd, offset, SEEK_SET)) {
                   1681:                                con->http_status = 501; /* not implemented */
                   1682: 
                   1683:                                close(fd);
                   1684: 
                   1685:                                return HANDLER_FINISHED;
                   1686:                        }
                   1687:                        con->http_status = 200; /* modified */
                   1688:                } else {
                   1689:                        /* take what we have in the request-body and write it to a file */
                   1690: 
                   1691:                        /* if the file doesn't exist, create it */
                   1692:                        if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY|O_TRUNC, WEBDAV_FILE_MODE))) {
                   1693:                                if (errno == ENOENT &&
                   1694:                                    -1 == (fd = open(con->physical.path->ptr, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, WEBDAV_FILE_MODE))) {
                   1695:                                        /* we can't open the file */
                   1696:                                        con->http_status = 403;
                   1697: 
                   1698:                                        return HANDLER_FINISHED;
                   1699:                                } else {
                   1700:                                        con->http_status = 201; /* created */
                   1701:                                }
                   1702:                        } else {
                   1703:                                con->http_status = 200; /* modified */
                   1704:                        }
                   1705:                }
                   1706: 
                   1707:                con->file_finished = 1;
                   1708: 
                   1709:                for (c = cq->first; c; c = cq->first) {
                   1710:                        int r = 0;
                   1711: 
                   1712:                        /* copy all chunks */
                   1713:                        switch(c->type) {
                   1714:                        case FILE_CHUNK:
                   1715: 
                   1716:                                if (c->file.mmap.start == MAP_FAILED) {
                   1717:                                        if (-1 == c->file.fd &&  /* open the file if not already open */
                   1718:                                            -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
                   1719:                                                log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
                   1720: 
                   1721:                                                return HANDLER_ERROR;
                   1722:                                        }
                   1723: 
                   1724:                                        if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
                   1725:                                                log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
                   1726:                                                                strerror(errno), c->file.name,  c->file.fd);
                   1727:                                                close(c->file.fd);
                   1728:                                                c->file.fd = -1;
                   1729: 
                   1730:                                                return HANDLER_ERROR;
                   1731:                                        }
                   1732: 
                   1733:                                        c->file.mmap.length = c->file.length;
                   1734: 
                   1735:                                        close(c->file.fd);
                   1736:                                        c->file.fd = -1;
                   1737: 
                   1738:                                        /* chunk_reset() or chunk_free() will cleanup for us */
                   1739:                                }
                   1740: 
                   1741:                                if ((r = write(fd, c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
                   1742:                                        switch(errno) {
                   1743:                                        case ENOSPC:
                   1744:                                                con->http_status = 507;
                   1745: 
                   1746:                                                break;
                   1747:                                        default:
                   1748:                                                con->http_status = 403;
                   1749:                                                break;
                   1750:                                        }
                   1751:                                }
                   1752:                                break;
                   1753:                        case MEM_CHUNK:
                   1754:                                if ((r = write(fd, c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
                   1755:                                        switch(errno) {
                   1756:                                        case ENOSPC:
                   1757:                                                con->http_status = 507;
                   1758: 
                   1759:                                                break;
                   1760:                                        default:
                   1761:                                                con->http_status = 403;
                   1762:                                                break;
                   1763:                                        }
                   1764:                                }
                   1765:                                break;
                   1766:                        case UNUSED_CHUNK:
                   1767:                                break;
                   1768:                        }
                   1769: 
                   1770:                        if (r > 0) {
                   1771:                                c->offset += r;
                   1772:                                cq->bytes_out += r;
                   1773:                        } else {
                   1774:                                break;
                   1775:                        }
                   1776:                        chunkqueue_remove_finished_chunks(cq);
                   1777:                }
                   1778:                close(fd);
                   1779: 
                   1780:                return HANDLER_FINISHED;
                   1781:        }
                   1782:        case HTTP_METHOD_MOVE:
                   1783:        case HTTP_METHOD_COPY: {
                   1784:                buffer *destination = NULL;
                   1785:                char *sep, *sep2, *start;
                   1786:                int overwrite = 1;
                   1787: 
                   1788:                if (p->conf.is_readonly) {
                   1789:                        con->http_status = 403;
                   1790:                        return HANDLER_FINISHED;
                   1791:                }
                   1792: 
                   1793:                /* is a exclusive lock set on the source */
                   1794:                if (con->request.http_method == HTTP_METHOD_MOVE) {
                   1795:                        if (!webdav_has_lock(srv, con, p, con->uri.path)) {
                   1796:                                con->http_status = 423;
                   1797:                                return HANDLER_FINISHED;
                   1798:                        }
                   1799:                }
                   1800: 
                   1801:                if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Destination"))) {
                   1802:                        destination = ds->value;
                   1803:                } else {
                   1804:                        con->http_status = 400;
                   1805:                        return HANDLER_FINISHED;
                   1806:                }
                   1807: 
                   1808:                if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Overwrite"))) {
                   1809:                        if (ds->value->used != 2 ||
                   1810:                            (ds->value->ptr[0] != 'F' &&
                   1811:                             ds->value->ptr[0] != 'T') )  {
                   1812:                                con->http_status = 400;
                   1813:                                return HANDLER_FINISHED;
                   1814:                        }
                   1815:                        overwrite = (ds->value->ptr[0] == 'F' ? 0 : 1);
                   1816:                }
                   1817:                /* let's parse the Destination
                   1818:                 *
                   1819:                 * http://127.0.0.1:1025/dav/litmus/copydest
                   1820:                 *
                   1821:                 * - host has to be the same as the Host: header we got
                   1822:                 * - we have to stay inside the document root
                   1823:                 * - the query string is thrown away
                   1824:                 *  */
                   1825: 
                   1826:                buffer_reset(p->uri.scheme);
                   1827:                buffer_reset(p->uri.path_raw);
                   1828:                buffer_reset(p->uri.authority);
                   1829: 
                   1830:                start = destination->ptr;
                   1831: 
                   1832:                if (NULL == (sep = strstr(start, "://"))) {
                   1833:                        con->http_status = 400;
                   1834:                        return HANDLER_FINISHED;
                   1835:                }
                   1836:                buffer_copy_string_len(p->uri.scheme, start, sep - start);
                   1837: 
                   1838:                start = sep + 3;
                   1839: 
                   1840:                if (NULL == (sep = strchr(start, '/'))) {
                   1841:                        con->http_status = 400;
                   1842:                        return HANDLER_FINISHED;
                   1843:                }
                   1844:                if (NULL != (sep2 = memchr(start, '@', sep - start))) {
                   1845:                        /* skip login information */
                   1846:                        start = sep2 + 1;
                   1847:                }
                   1848:                buffer_copy_string_len(p->uri.authority, start, sep - start);
                   1849: 
                   1850:                start = sep + 1;
                   1851: 
                   1852:                if (NULL == (sep = strchr(start, '?'))) {
                   1853:                        /* no query string, good */
                   1854:                        buffer_copy_string(p->uri.path_raw, start);
                   1855:                } else {
                   1856:                        buffer_copy_string_len(p->uri.path_raw, start, sep - start);
                   1857:                }
                   1858: 
                   1859:                if (!buffer_is_equal(p->uri.authority, con->uri.authority)) {
                   1860:                        /* not the same host */
                   1861:                        con->http_status = 502;
                   1862:                        return HANDLER_FINISHED;
                   1863:                }
                   1864: 
                   1865:                buffer_copy_string_buffer(p->tmp_buf, p->uri.path_raw);
                   1866:                buffer_urldecode_path(p->tmp_buf);
                   1867:                buffer_path_simplify(p->uri.path, p->tmp_buf);
                   1868: 
                   1869:                /* we now have a URI which is clean. transform it into a physical path */
                   1870:                buffer_copy_string_buffer(p->physical.doc_root, con->physical.doc_root);
                   1871:                buffer_copy_string_buffer(p->physical.rel_path, p->uri.path);
                   1872: 
                   1873:                if (con->conf.force_lowercase_filenames) {
                   1874:                        buffer_to_lower(p->physical.rel_path);
                   1875:                }
                   1876: 
                   1877:                buffer_copy_string_buffer(p->physical.path, p->physical.doc_root);
                   1878:                BUFFER_APPEND_SLASH(p->physical.path);
                   1879:                buffer_copy_string_buffer(p->physical.basedir, p->physical.path);
                   1880: 
                   1881:                /* don't add a second / */
                   1882:                if (p->physical.rel_path->ptr[0] == '/') {
                   1883:                        buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr + 1, p->physical.rel_path->used - 2);
                   1884:                } else {
                   1885:                        buffer_append_string_buffer(p->physical.path, p->physical.rel_path);
                   1886:                }
                   1887: 
                   1888:                /* let's see if the source is a directory
                   1889:                 * if yes, we fail with 501 */
                   1890: 
                   1891:                if (-1 == stat(con->physical.path->ptr, &st)) {
                   1892:                        /* don't about it yet, unlink will fail too */
                   1893:                        switch(errno) {
                   1894:                        case ENOENT:
                   1895:                                 con->http_status = 404;
                   1896:                                 break;
                   1897:                        default:
                   1898:                                 con->http_status = 403;
                   1899:                                 break;
                   1900:                        }
                   1901:                } else if (S_ISDIR(st.st_mode)) {
                   1902:                        int r;
                   1903:                        /* src is a directory */
                   1904: 
                   1905:                        if (-1 == stat(p->physical.path->ptr, &st)) {
                   1906:                                if (-1 == mkdir(p->physical.path->ptr, WEBDAV_DIR_MODE)) {
                   1907:                                        con->http_status = 403;
                   1908:                                        return HANDLER_FINISHED;
                   1909:                                }
                   1910:                        } else if (!S_ISDIR(st.st_mode)) {
                   1911:                                if (overwrite == 0) {
                   1912:                                        /* copying into a non-dir ? */
                   1913:                                        con->http_status = 409;
                   1914:                                        return HANDLER_FINISHED;
                   1915:                                } else {
                   1916:                                        unlink(p->physical.path->ptr);
                   1917:                                        if (-1 == mkdir(p->physical.path->ptr, WEBDAV_DIR_MODE)) {
                   1918:                                                con->http_status = 403;
                   1919:                                                return HANDLER_FINISHED;
                   1920:                                        }
                   1921:                                }
                   1922:                        }
                   1923: 
                   1924:                        /* copy the content of src to dest */
                   1925:                        if (0 != (r = webdav_copy_dir(srv, con, p, &(con->physical), &(p->physical), overwrite))) {
                   1926:                                con->http_status = r;
                   1927:                                return HANDLER_FINISHED;
                   1928:                        }
                   1929:                        if (con->request.http_method == HTTP_METHOD_MOVE) {
                   1930:                                b = buffer_init();
                   1931:                                webdav_delete_dir(srv, con, p, &(con->physical), b); /* content */
                   1932:                                buffer_free(b);
                   1933: 
                   1934:                                rmdir(con->physical.path->ptr);
                   1935:                        }
                   1936:                        con->http_status = 201;
                   1937:                        con->file_finished = 1;
                   1938:                } else {
                   1939:                        /* it is just a file, good */
                   1940:                        int r;
                   1941: 
                   1942:                        /* does the client have a lock for this connection ? */
                   1943:                        if (!webdav_has_lock(srv, con, p, p->uri.path)) {
                   1944:                                con->http_status = 423;
                   1945:                                return HANDLER_FINISHED;
                   1946:                        }
                   1947: 
                   1948:                        /* destination exists */
                   1949:                        if (0 == (r = stat(p->physical.path->ptr, &st))) {
                   1950:                                if (S_ISDIR(st.st_mode)) {
                   1951:                                        /* file to dir/
                   1952:                                         * append basename to physical path */
                   1953: 
                   1954:                                        if (NULL != (sep = strrchr(con->physical.path->ptr, '/'))) {
                   1955:                                                buffer_append_string(p->physical.path, sep);
                   1956:                                                r = stat(p->physical.path->ptr, &st);
                   1957:                                        }
                   1958:                                }
                   1959:                        }
                   1960: 
                   1961:                        if (-1 == r) {
                   1962:                                con->http_status = 201; /* we will create a new one */
                   1963:                                con->file_finished = 1;
                   1964: 
                   1965:                                switch(errno) {
                   1966:                                case ENOTDIR:
                   1967:                                        con->http_status = 409;
                   1968:                                        return HANDLER_FINISHED;
                   1969:                                }
                   1970:                        } else if (overwrite == 0) {
                   1971:                                /* destination exists, but overwrite is not set */
                   1972:                                con->http_status = 412;
                   1973:                                return HANDLER_FINISHED;
                   1974:                        } else {
                   1975:                                con->http_status = 204; /* resource already existed */
                   1976:                        }
                   1977: 
                   1978:                        if (con->request.http_method == HTTP_METHOD_MOVE) {
                   1979:                                /* try a rename */
                   1980: 
                   1981:                                if (0 == rename(con->physical.path->ptr, p->physical.path->ptr)) {
                   1982: #ifdef USE_PROPPATCH
                   1983:                                        sqlite3_stmt *stmt;
                   1984: 
                   1985:                                        stmt = p->conf.stmt_delete_uri;
                   1986:                                        if (stmt) {
                   1987: 
                   1988:                                                sqlite3_reset(stmt);
                   1989: 
                   1990:                                                /* bind the values to the insert */
                   1991:                                                sqlite3_bind_text(stmt, 1,
                   1992:                                                                  con->uri.path->ptr,
                   1993:                                                                  con->uri.path->used - 1,
                   1994:                                                                  SQLITE_TRANSIENT);
                   1995: 
                   1996:                                                if (SQLITE_DONE != sqlite3_step(stmt)) {
                   1997:                                                        log_error_write(srv, __FILE__, __LINE__, "ss", "sql-move(delete old) failed:", sqlite3_errmsg(p->conf.sql));
                   1998:                                                }
                   1999:                                        }
                   2000: 
                   2001:                                        stmt = p->conf.stmt_move_uri;
                   2002:                                        if (stmt) {
                   2003: 
                   2004:                                                sqlite3_reset(stmt);
                   2005: 
                   2006:                                                /* bind the values to the insert */
                   2007:                                                sqlite3_bind_text(stmt, 1,
                   2008:                                                                  p->uri.path->ptr,
                   2009:                                                                  p->uri.path->used - 1,
                   2010:                                                                  SQLITE_TRANSIENT);
                   2011: 
                   2012:                                                sqlite3_bind_text(stmt, 2,
                   2013:                                                                  con->uri.path->ptr,
                   2014:                                                                  con->uri.path->used - 1,
                   2015:                                                                  SQLITE_TRANSIENT);
                   2016: 
                   2017:                                                if (SQLITE_DONE != sqlite3_step(stmt)) {
                   2018:                                                        log_error_write(srv, __FILE__, __LINE__, "ss", "sql-move failed:", sqlite3_errmsg(p->conf.sql));
                   2019:                                                }
                   2020:                                        }
                   2021: #endif
                   2022:                                        return HANDLER_FINISHED;
                   2023:                                }
                   2024: 
                   2025:                                /* rename failed, fall back to COPY + DELETE */
                   2026:                        }
                   2027: 
                   2028:                        if (0 != (r = webdav_copy_file(srv, con, p, &(con->physical), &(p->physical), overwrite))) {
                   2029:                                con->http_status = r;
                   2030: 
                   2031:                                return HANDLER_FINISHED;
                   2032:                        }
                   2033: 
                   2034:                        if (con->request.http_method == HTTP_METHOD_MOVE) {
                   2035:                                b = buffer_init();
                   2036:                                webdav_delete_file(srv, con, p, &(con->physical), b);
                   2037:                                buffer_free(b);
                   2038:                        }
                   2039:                }
                   2040: 
                   2041:                return HANDLER_FINISHED;
                   2042:        }
                   2043:        case HTTP_METHOD_PROPPATCH:
                   2044:                if (p->conf.is_readonly) {
                   2045:                        con->http_status = 403;
                   2046:                        return HANDLER_FINISHED;
                   2047:                }
                   2048: 
                   2049:                if (!webdav_has_lock(srv, con, p, con->uri.path)) {
                   2050:                        con->http_status = 423;
                   2051:                        return HANDLER_FINISHED;
                   2052:                }
                   2053: 
                   2054:                /* check if destination exists */
                   2055:                if (-1 == stat(con->physical.path->ptr, &st)) {
                   2056:                        switch(errno) {
                   2057:                        case ENOENT:
                   2058:                                con->http_status = 404;
                   2059:                                break;
                   2060:                        }
                   2061:                }
                   2062: 
                   2063: #ifdef USE_PROPPATCH
                   2064:                if (con->request.content_length) {
                   2065:                        xmlDocPtr xml;
                   2066: 
                   2067:                        if (1 == webdav_parse_chunkqueue(srv, con, p, con->request_content_queue, &xml)) {
                   2068:                                xmlNode *rootnode = xmlDocGetRootElement(xml);
                   2069: 
                   2070:                                if (0 == xmlStrcmp(rootnode->name, BAD_CAST "propertyupdate")) {
                   2071:                                        xmlNode *cmd;
                   2072:                                        char *err = NULL;
                   2073:                                        int empty_ns = 0; /* send 400 on a empty namespace attribute */
                   2074: 
                   2075:                                        /* start response */
                   2076: 
                   2077:                                        if (SQLITE_OK != sqlite3_exec(p->conf.sql, "BEGIN TRANSACTION", NULL, NULL, &err)) {
                   2078:                                                log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err);
                   2079:                                                sqlite3_free(err);
                   2080: 
                   2081:                                                goto propmatch_cleanup;
                   2082:                                        }
                   2083: 
                   2084:                                        /* a UPDATE request, we know 'set' and 'remove' */
                   2085:                                        for (cmd = rootnode->children; cmd; cmd = cmd->next) {
                   2086:                                                xmlNode *props;
                   2087:                                                /* either set or remove */
                   2088: 
                   2089:                                                if ((0 == xmlStrcmp(cmd->name, BAD_CAST "set")) ||
                   2090:                                                    (0 == xmlStrcmp(cmd->name, BAD_CAST "remove"))) {
                   2091: 
                   2092:                                                        sqlite3_stmt *stmt;
                   2093: 
                   2094:                                                        stmt = (0 == xmlStrcmp(cmd->name, BAD_CAST "remove")) ?
                   2095:                                                                p->conf.stmt_delete_prop : p->conf.stmt_update_prop;
                   2096: 
                   2097:                                                        for (props = cmd->children; props; props = props->next) {
                   2098:                                                                if (0 == xmlStrcmp(props->name, BAD_CAST "prop")) {
                   2099:                                                                        xmlNode *prop;
                   2100:                                                                        int r;
                   2101: 
                   2102:                                                                        prop = props->children;
                   2103: 
                   2104:                                                                        if (prop->ns &&
                   2105:                                                                            (0 == xmlStrcmp(prop->ns->href, BAD_CAST "")) &&
                   2106:                                                                            (0 != xmlStrcmp(prop->ns->prefix, BAD_CAST ""))) {
                   2107:                                                                                log_error_write(srv, __FILE__, __LINE__, "ss",
                   2108:                                                                                                "no name space for:",
                   2109:                                                                                                prop->name);
                   2110: 
                   2111:                                                                                empty_ns = 1;
                   2112:                                                                                break;
                   2113:                                                                        }
                   2114: 
                   2115:                                                                        sqlite3_reset(stmt);
                   2116: 
                   2117:                                                                        /* bind the values to the insert */
                   2118: 
                   2119:                                                                        sqlite3_bind_text(stmt, 1,
                   2120:                                                                                          con->uri.path->ptr,
                   2121:                                                                                          con->uri.path->used - 1,
                   2122:                                                                                          SQLITE_TRANSIENT);
                   2123:                                                                        sqlite3_bind_text(stmt, 2,
                   2124:                                                                                          (char *)prop->name,
                   2125:                                                                                          strlen((char *)prop->name),
                   2126:                                                                                          SQLITE_TRANSIENT);
                   2127:                                                                        if (prop->ns) {
                   2128:                                                                                sqlite3_bind_text(stmt, 3,
                   2129:                                                                                                  (char *)prop->ns->href,
                   2130:                                                                                                  strlen((char *)prop->ns->href),
                   2131:                                                                                                  SQLITE_TRANSIENT);
                   2132:                                                                        } else {
                   2133:                                                                                sqlite3_bind_text(stmt, 3,
                   2134:                                                                                                  "",
                   2135:                                                                                                  0,
                   2136:                                                                                                  SQLITE_TRANSIENT);
                   2137:                                                                        }
                   2138:                                                                        if (stmt == p->conf.stmt_update_prop) {
                   2139:                                                                                sqlite3_bind_text(stmt, 4,
                   2140:                                                                                          (char *)xmlNodeGetContent(prop),
                   2141:                                                                                          strlen((char *)xmlNodeGetContent(prop)),
                   2142:                                                                                          SQLITE_TRANSIENT);
                   2143:                                                                        }
                   2144: 
                   2145:                                                                        if (SQLITE_DONE != (r = sqlite3_step(stmt))) {
                   2146:                                                                                log_error_write(srv, __FILE__, __LINE__, "ss",
                   2147:                                                                                                "sql-set failed:", sqlite3_errmsg(p->conf.sql));
                   2148:                                                                        }
                   2149:                                                                }
                   2150:                                                        }
                   2151:                                                        if (empty_ns) break;
                   2152:                                                }
                   2153:                                        }
                   2154: 
                   2155:                                        if (empty_ns) {
                   2156:                                                if (SQLITE_OK != sqlite3_exec(p->conf.sql, "ROLLBACK", NULL, NULL, &err)) {
                   2157:                                                        log_error_write(srv, __FILE__, __LINE__, "ss", "can't rollback transaction:", err);
                   2158:                                                        sqlite3_free(err);
                   2159: 
                   2160:                                                        goto propmatch_cleanup;
                   2161:                                                }
                   2162: 
                   2163:                                                con->http_status = 400;
                   2164:                                        } else {
                   2165:                                                if (SQLITE_OK != sqlite3_exec(p->conf.sql, "COMMIT", NULL, NULL, &err)) {
                   2166:                                                        log_error_write(srv, __FILE__, __LINE__, "ss", "can't commit transaction:", err);
                   2167:                                                        sqlite3_free(err);
                   2168: 
                   2169:                                                        goto propmatch_cleanup;
                   2170:                                                }
                   2171:                                                con->http_status = 200;
                   2172:                                        }
                   2173:                                        con->file_finished = 1;
                   2174: 
                   2175:                                        return HANDLER_FINISHED;
                   2176:                                }
                   2177: 
                   2178: propmatch_cleanup:
                   2179: 
                   2180:                                xmlFreeDoc(xml);
                   2181:                        } else {
                   2182:                                con->http_status = 400;
                   2183:                                return HANDLER_FINISHED;
                   2184:                        }
                   2185:                }
                   2186: #endif
                   2187:                con->http_status = 501;
                   2188:                return HANDLER_FINISHED;
                   2189:        case HTTP_METHOD_LOCK:
                   2190:                /**
                   2191:                 * a mac wants to write
                   2192:                 *
                   2193:                 * LOCK /dav/expire.txt HTTP/1.1\r\n
                   2194:                 * User-Agent: WebDAVFS/1.3 (01308000) Darwin/8.1.0 (Power Macintosh)\r\n
                   2195:                 * Accept: * / *\r\n
                   2196:                 * Depth: 0\r\n
                   2197:                 * Timeout: Second-600\r\n
                   2198:                 * Content-Type: text/xml; charset=\"utf-8\"\r\n
                   2199:                 * Content-Length: 229\r\n
                   2200:                 * Connection: keep-alive\r\n
                   2201:                 * Host: 192.168.178.23:1025\r\n
                   2202:                 * \r\n
                   2203:                 * <?xml version=\"1.0\" encoding=\"utf-8\"?>\n
                   2204:                 * <D:lockinfo xmlns:D=\"DAV:\">\n
                   2205:                 *  <D:lockscope><D:exclusive/></D:lockscope>\n
                   2206:                 *  <D:locktype><D:write/></D:locktype>\n
                   2207:                 *  <D:owner>\n
                   2208:                 *   <D:href>http://www.apple.com/webdav_fs/</D:href>\n
                   2209:                 *  </D:owner>\n
                   2210:                 * </D:lockinfo>\n
                   2211:                 */
                   2212: 
                   2213:                if (depth != 0 && depth != -1) {
                   2214:                        con->http_status = 400;
                   2215: 
                   2216:                        return HANDLER_FINISHED;
                   2217:                }
                   2218: 
                   2219: #ifdef USE_LOCKS
                   2220:                if (con->request.content_length) {
                   2221:                        xmlDocPtr xml;
                   2222:                        buffer *hdr_if = NULL;
                   2223: 
                   2224:                        if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) {
                   2225:                                hdr_if = ds->value;
                   2226:                        }
                   2227: 
                   2228:                        /* we don't support Depth: Infinity on locks */
                   2229:                        if (hdr_if == NULL && depth == -1) {
                   2230:                                con->http_status = 409; /* Conflict */
                   2231: 
                   2232:                                return HANDLER_FINISHED;
                   2233:                        }
                   2234: 
                   2235:                        if (1 == webdav_parse_chunkqueue(srv, con, p, con->request_content_queue, &xml)) {
                   2236:                                xmlNode *rootnode = xmlDocGetRootElement(xml);
                   2237: 
                   2238:                                assert(rootnode);
                   2239: 
                   2240:                                if (0 == xmlStrcmp(rootnode->name, BAD_CAST "lockinfo")) {
                   2241:                                        xmlNode *lockinfo;
                   2242:                                        const xmlChar *lockscope = NULL, *locktype = NULL; /* TODO: compiler says unused: *owner = NULL; */
                   2243: 
                   2244:                                        for (lockinfo = rootnode->children; lockinfo; lockinfo = lockinfo->next) {
                   2245:                                                if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "lockscope")) {
                   2246:                                                        xmlNode *value;
                   2247:                                                        for (value = lockinfo->children; value; value = value->next) {
                   2248:                                                                if ((0 == xmlStrcmp(value->name, BAD_CAST "exclusive")) ||
                   2249:                                                                    (0 == xmlStrcmp(value->name, BAD_CAST "shared"))) {
                   2250:                                                                        lockscope = value->name;
                   2251:                                                                } else {
                   2252:                                                                        con->http_status = 400;
                   2253: 
                   2254:                                                                        xmlFreeDoc(xml);
                   2255:                                                                        return HANDLER_FINISHED;
                   2256:                                                                }
                   2257:                                                        }
                   2258:                                                } else if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "locktype")) {
                   2259:                                                        xmlNode *value;
                   2260:                                                        for (value = lockinfo->children; value; value = value->next) {
                   2261:                                                                if ((0 == xmlStrcmp(value->name, BAD_CAST "write"))) {
                   2262:                                                                        locktype = value->name;
                   2263:                                                                } else {
                   2264:                                                                        con->http_status = 400;
                   2265: 
                   2266:                                                                        xmlFreeDoc(xml);
                   2267:                                                                        return HANDLER_FINISHED;
                   2268:                                                                }
                   2269:                                                        }
                   2270: 
                   2271:                                                } else if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "owner")) {
                   2272:                                                }
                   2273:                                        }
                   2274: 
                   2275:                                        if (lockscope && locktype) {
                   2276:                                                sqlite3_stmt *stmt = p->conf.stmt_read_lock_by_uri;
                   2277: 
                   2278:                                                /* is this resourse already locked ? */
                   2279: 
                   2280:                                                /* SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout
                   2281:                                                 *   FROM locks
                   2282:                                                 *  WHERE resource = ? */
                   2283: 
                   2284:                                                if (stmt) {
                   2285: 
                   2286:                                                        sqlite3_reset(stmt);
                   2287: 
                   2288:                                                        sqlite3_bind_text(stmt, 1,
                   2289:                                                                          p->uri.path->ptr,
                   2290:                                                                          p->uri.path->used - 1,
                   2291:                                                                          SQLITE_TRANSIENT);
                   2292: 
                   2293:                                                        /* it is the PK */
                   2294:                                                        while (SQLITE_ROW == sqlite3_step(stmt)) {
                   2295:                                                                /* we found a lock
                   2296:                                                                 * 1. is it compatible ?
                   2297:                                                                 * 2. is it ours */
                   2298:                                                                char *sql_lockscope = (char *)sqlite3_column_text(stmt, 2);
                   2299: 
                   2300:                                                                if (strcmp(sql_lockscope, "exclusive")) {
                   2301:                                                                        con->http_status = 423;
                   2302:                                                                } else if (0 == xmlStrcmp(lockscope, BAD_CAST "exclusive")) {
                   2303:                                                                        /* resourse is locked with a shared lock
                   2304:                                                                         * client wants exclusive */
                   2305:                                                                        con->http_status = 423;
                   2306:                                                                }
                   2307:                                                        }
                   2308:                                                        if (con->http_status == 423) {
                   2309:                                                                xmlFreeDoc(xml);
                   2310:                                                                return HANDLER_FINISHED;
                   2311:                                                        }
                   2312:                                                }
                   2313: 
                   2314:                                                stmt = p->conf.stmt_create_lock;
                   2315:                                                if (stmt) {
                   2316:                                                        /* create a lock-token */
                   2317:                                                        uuid_t id;
                   2318:                                                        char uuid[37] /* 36 + \0 */;
                   2319: 
                   2320:                                                        uuid_generate(id);
                   2321:                                                        uuid_unparse(id, uuid);
                   2322: 
                   2323:                                                        buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("opaquelocktoken:"));
                   2324:                                                        buffer_append_string(p->tmp_buf, uuid);
                   2325: 
                   2326:                                                        /* "CREATE TABLE locks ("
                   2327:                                                         * "  locktoken TEXT NOT NULL,"
                   2328:                                                         * "  resource TEXT NOT NULL,"
                   2329:                                                         * "  lockscope TEXT NOT NULL,"
                   2330:                                                         * "  locktype TEXT NOT NULL,"
                   2331:                                                         * "  owner TEXT NOT NULL,"
                   2332:                                                         * "  depth INT NOT NULL,"
                   2333:                                                         */
                   2334: 
                   2335:                                                        sqlite3_reset(stmt);
                   2336: 
                   2337:                                                        sqlite3_bind_text(stmt, 1,
                   2338:                                                                          CONST_BUF_LEN(p->tmp_buf),
                   2339:                                                                          SQLITE_TRANSIENT);
                   2340: 
                   2341:                                                        sqlite3_bind_text(stmt, 2,
                   2342:                                                                          CONST_BUF_LEN(con->uri.path),
                   2343:                                                                          SQLITE_TRANSIENT);
                   2344: 
                   2345:                                                        sqlite3_bind_text(stmt, 3,
                   2346:                                                                          (const char *)lockscope,
                   2347:                                                                          xmlStrlen(lockscope),
                   2348:                                                                          SQLITE_TRANSIENT);
                   2349: 
                   2350:                                                        sqlite3_bind_text(stmt, 4,
                   2351:                                                                          (const char *)locktype,
                   2352:                                                                          xmlStrlen(locktype),
                   2353:                                                                          SQLITE_TRANSIENT);
                   2354: 
                   2355:                                                        /* owner */
                   2356:                                                        sqlite3_bind_text(stmt, 5,
                   2357:                                                                          "",
                   2358:                                                                          0,
                   2359:                                                                          SQLITE_TRANSIENT);
                   2360: 
                   2361:                                                        /* depth */
                   2362:                                                        sqlite3_bind_int(stmt, 6,
                   2363:                                                                         depth);
                   2364: 
                   2365: 
                   2366:                                                        if (SQLITE_DONE != sqlite3_step(stmt)) {
                   2367:                                                                log_error_write(srv, __FILE__, __LINE__, "ss",
                   2368:                                                                                "create lock:", sqlite3_errmsg(p->conf.sql));
                   2369:                                                        }
                   2370: 
                   2371:                                                        /* looks like we survived */
                   2372:                                                        webdav_lockdiscovery(srv, con, p->tmp_buf, (const char *)lockscope, (const char *)locktype, depth);
                   2373: 
                   2374:                                                        con->http_status = 201;
                   2375:                                                        con->file_finished = 1;
                   2376:                                                }
                   2377:                                        }
                   2378:                                }
                   2379: 
                   2380:                                xmlFreeDoc(xml);
                   2381:                                return HANDLER_FINISHED;
                   2382:                        } else {
                   2383:                                con->http_status = 400;
                   2384:                                return HANDLER_FINISHED;
                   2385:                        }
                   2386:                } else {
                   2387: 
                   2388:                        if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If"))) {
                   2389:                                buffer *locktoken = ds->value;
                   2390:                                sqlite3_stmt *stmt = p->conf.stmt_refresh_lock;
                   2391: 
                   2392:                                /* remove the < > around the token */
                   2393:                                if (locktoken->used < 6) {
                   2394:                                        con->http_status = 400;
                   2395: 
                   2396:                                        return HANDLER_FINISHED;
                   2397:                                }
                   2398: 
                   2399:                                buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 2, locktoken->used - 5);
                   2400: 
                   2401:                                sqlite3_reset(stmt);
                   2402: 
                   2403:                                sqlite3_bind_text(stmt, 1,
                   2404:                                          CONST_BUF_LEN(p->tmp_buf),
                   2405:                                          SQLITE_TRANSIENT);
                   2406: 
                   2407:                                if (SQLITE_DONE != sqlite3_step(stmt)) {
                   2408:                                        log_error_write(srv, __FILE__, __LINE__, "ss",
                   2409:                                                "refresh lock:", sqlite3_errmsg(p->conf.sql));
                   2410:                                }
                   2411: 
                   2412:                                webdav_lockdiscovery(srv, con, p->tmp_buf, "exclusive", "write", 0);
                   2413: 
                   2414:                                con->http_status = 200;
                   2415:                                con->file_finished = 1;
                   2416:                                return HANDLER_FINISHED;
                   2417:                        } else {
                   2418:                                /* we need a lock-token to refresh */
                   2419:                                con->http_status = 400;
                   2420: 
                   2421:                                return HANDLER_FINISHED;
                   2422:                        }
                   2423:                }
                   2424:                break;
                   2425: #else
                   2426:                con->http_status = 501;
                   2427:                return HANDLER_FINISHED;
                   2428: #endif
                   2429:        case HTTP_METHOD_UNLOCK:
                   2430: #ifdef USE_LOCKS
                   2431:                if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Lock-Token"))) {
                   2432:                        buffer *locktoken = ds->value;
                   2433:                        sqlite3_stmt *stmt = p->conf.stmt_remove_lock;
                   2434: 
                   2435:                        /* remove the < > around the token */
                   2436:                        if (locktoken->used < 4) {
                   2437:                                con->http_status = 400;
                   2438: 
                   2439:                                return HANDLER_FINISHED;
                   2440:                        }
                   2441: 
                   2442:                        /**
                   2443:                         * FIXME:
                   2444:                         *
                   2445:                         * if the resourse is locked:
                   2446:                         * - by us: unlock
                   2447:                         * - by someone else: 401
                   2448:                         * if the resource is not locked:
                   2449:                         * - 412
                   2450:                         *  */
                   2451: 
                   2452:                        buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 1, locktoken->used - 3);
                   2453: 
                   2454:                        sqlite3_reset(stmt);
                   2455: 
                   2456:                        sqlite3_bind_text(stmt, 1,
                   2457:                                  CONST_BUF_LEN(p->tmp_buf),
                   2458:                                  SQLITE_TRANSIENT);
                   2459: 
                   2460:                        sqlite3_bind_text(stmt, 2,
                   2461:                                  CONST_BUF_LEN(con->uri.path),
                   2462:                                  SQLITE_TRANSIENT);
                   2463: 
                   2464:                        if (SQLITE_DONE != sqlite3_step(stmt)) {
                   2465:                                log_error_write(srv, __FILE__, __LINE__, "ss",
                   2466:                                        "remove lock:", sqlite3_errmsg(p->conf.sql));
                   2467:                        }
                   2468: 
                   2469:                        if (0 == sqlite3_changes(p->conf.sql)) {
                   2470:                                con->http_status = 401;
                   2471:                        } else {
                   2472:                                con->http_status = 204;
                   2473:                        }
                   2474:                        return HANDLER_FINISHED;
                   2475:                } else {
                   2476:                        /* we need a lock-token to unlock */
                   2477:                        con->http_status = 400;
                   2478: 
                   2479:                        return HANDLER_FINISHED;
                   2480:                }
                   2481:                break;
                   2482: #else
                   2483:                con->http_status = 501;
                   2484:                return HANDLER_FINISHED;
                   2485: #endif
                   2486:        default:
                   2487:                break;
                   2488:        }
                   2489: 
                   2490:        /* not found */
                   2491:        return HANDLER_GO_ON;
                   2492: }
                   2493: 
                   2494: 
                   2495: /* this function is called at dlopen() time and inits the callbacks */
                   2496: 
                   2497: int mod_webdav_plugin_init(plugin *p);
                   2498: int mod_webdav_plugin_init(plugin *p) {
                   2499:        p->version     = LIGHTTPD_VERSION_ID;
                   2500:        p->name        = buffer_init_string("webdav");
                   2501: 
                   2502:        p->init        = mod_webdav_init;
                   2503:        p->handle_uri_clean  = mod_webdav_uri_handler;
                   2504:        p->handle_physical   = mod_webdav_subrequest_handler;
                   2505:        p->set_defaults  = mod_webdav_set_defaults;
                   2506:        p->cleanup     = mod_webdav_free;
                   2507: 
                   2508:        p->data        = NULL;
                   2509: 
                   2510:        return 0;
                   2511: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>