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

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

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