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

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

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