File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_webdav.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:35:00 2016 UTC (7 years, 8 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_41p8, HEAD
lighttpd 1.4.41

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

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