|  | @@ -2307,6 +2307,22 @@ typedef int volatile stop_flag_t;
 | 
	
		
			
				|  |  |  #endif /* STOP_FLAG_NEEDS_LOCK */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#if !defined(NUM_WEBDAV_LOCKS)
 | 
	
		
			
				|  |  | +#define NUM_WEBDAV_LOCKS 10
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +#if !defined(LOCK_DURATION_S)
 | 
	
		
			
				|  |  | +#define LOCK_DURATION_S 60
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct twebdav_lock {
 | 
	
		
			
				|  |  | +	uint64_t locktime;
 | 
	
		
			
				|  |  | +	char token[33];
 | 
	
		
			
				|  |  | +	char path[UTF8_PATH_MAX * 2];
 | 
	
		
			
				|  |  | +	char user[UTF8_PATH_MAX * 2];
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  struct mg_context {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/* Part 1 - Physical context:
 | 
	
	
		
			
				|  | @@ -2369,6 +2385,9 @@ struct mg_context {
 | 
	
		
			
				|  |  |  	struct mg_memory_stat ctx_memory;
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	/* WebDAV lock structures */
 | 
	
		
			
				|  |  | +	struct twebdav_lock webdav_lock[NUM_WEBDAV_LOCKS];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	/* Operating system related */
 | 
	
		
			
				|  |  |  	char *systemName;  /* What operating system is running */
 | 
	
		
			
				|  |  |  	time_t start_time; /* Server start time, used for authentication
 | 
	
	
		
			
				|  | @@ -2537,7 +2556,6 @@ struct mg_connection {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* Directory entry */
 | 
	
		
			
				|  |  |  struct de {
 | 
	
		
			
				|  |  | -	struct mg_connection *conn;
 | 
	
		
			
				|  |  |  	char *file_name;
 | 
	
		
			
				|  |  |  	struct mg_file_stat file;
 | 
	
		
			
				|  |  |  };
 | 
	
	
		
			
				|  | @@ -9430,7 +9448,7 @@ mg_url_encode(const char *src, char *dst, size_t dst_len)
 | 
	
		
			
				|  |  |  /* Return 0 on success, non-zero if an error occurs. */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static int
 | 
	
		
			
				|  |  | -print_dir_entry(struct de *de)
 | 
	
		
			
				|  |  | +print_dir_entry(struct mg_connection *conn, struct de *de)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	size_t namesize, escsize, i;
 | 
	
		
			
				|  |  |  	char *href, *esc, *p;
 | 
	
	
		
			
				|  | @@ -9467,7 +9485,7 @@ print_dir_entry(struct de *de)
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if (de->file.is_directory) {
 | 
	
		
			
				|  |  | -		mg_snprintf(de->conn,
 | 
	
		
			
				|  |  | +		mg_snprintf(conn,
 | 
	
		
			
				|  |  |  		            NULL, /* Buffer is big enough */
 | 
	
		
			
				|  |  |  		            size,
 | 
	
		
			
				|  |  |  		            sizeof(size),
 | 
	
	
		
			
				|  | @@ -9477,28 +9495,28 @@ print_dir_entry(struct de *de)
 | 
	
		
			
				|  |  |  		/* We use (signed) cast below because MSVC 6 compiler cannot
 | 
	
		
			
				|  |  |  		 * convert unsigned __int64 to double. Sigh. */
 | 
	
		
			
				|  |  |  		if (de->file.size < 1024) {
 | 
	
		
			
				|  |  | -			mg_snprintf(de->conn,
 | 
	
		
			
				|  |  | +			mg_snprintf(conn,
 | 
	
		
			
				|  |  |  			            NULL, /* Buffer is big enough */
 | 
	
		
			
				|  |  |  			            size,
 | 
	
		
			
				|  |  |  			            sizeof(size),
 | 
	
		
			
				|  |  |  			            "%d",
 | 
	
		
			
				|  |  |  			            (int)de->file.size);
 | 
	
		
			
				|  |  |  		} else if (de->file.size < 0x100000) {
 | 
	
		
			
				|  |  | -			mg_snprintf(de->conn,
 | 
	
		
			
				|  |  | +			mg_snprintf(conn,
 | 
	
		
			
				|  |  |  			            NULL, /* Buffer is big enough */
 | 
	
		
			
				|  |  |  			            size,
 | 
	
		
			
				|  |  |  			            sizeof(size),
 | 
	
		
			
				|  |  |  			            "%.1fk",
 | 
	
		
			
				|  |  |  			            (double)de->file.size / 1024.0);
 | 
	
		
			
				|  |  |  		} else if (de->file.size < 0x40000000) {
 | 
	
		
			
				|  |  | -			mg_snprintf(de->conn,
 | 
	
		
			
				|  |  | +			mg_snprintf(conn,
 | 
	
		
			
				|  |  |  			            NULL, /* Buffer is big enough */
 | 
	
		
			
				|  |  |  			            size,
 | 
	
		
			
				|  |  |  			            sizeof(size),
 | 
	
		
			
				|  |  |  			            "%.1fM",
 | 
	
		
			
				|  |  |  			            (double)de->file.size / 1048576);
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  | -			mg_snprintf(de->conn,
 | 
	
		
			
				|  |  | +			mg_snprintf(conn,
 | 
	
		
			
				|  |  |  			            NULL, /* Buffer is big enough */
 | 
	
		
			
				|  |  |  			            size,
 | 
	
		
			
				|  |  |  			            sizeof(size),
 | 
	
	
		
			
				|  | @@ -9521,7 +9539,7 @@ print_dir_entry(struct de *de)
 | 
	
		
			
				|  |  |  		mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod));
 | 
	
		
			
				|  |  |  		mod[sizeof(mod) - 1] = '\0';
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | -	mg_printf(de->conn,
 | 
	
		
			
				|  |  | +	mg_printf(conn,
 | 
	
		
			
				|  |  |  	          "<tr><td><a href=\"%s%s\">%s%s</a></td>"
 | 
	
		
			
				|  |  |  	          "<td> %s</td><td>  %s</td></tr>\n",
 | 
	
		
			
				|  |  |  	          href,
 | 
	
	
		
			
				|  | @@ -9540,11 +9558,10 @@ print_dir_entry(struct de *de)
 | 
	
		
			
				|  |  |   * On windows, __cdecl specification is needed in case if project is built
 | 
	
		
			
				|  |  |   * with __stdcall convention. qsort always requires __cdels callback. */
 | 
	
		
			
				|  |  |  static int WINCDECL
 | 
	
		
			
				|  |  | -compare_dir_entries(const void *p1, const void *p2)
 | 
	
		
			
				|  |  | +compare_dir_entries(const void *p1, const void *p2, const char *query_string)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	if (p1 && p2) {
 | 
	
		
			
				|  |  |  		const struct de *a = (const struct de *)p1, *b = (const struct de *)p2;
 | 
	
		
			
				|  |  | -		const char *query_string = a->conn->request_info.query_string;
 | 
	
		
			
				|  |  |  		int cmp_result = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		if ((query_string == NULL) || (query_string[0] == '\0')) {
 | 
	
	
		
			
				|  | @@ -9604,7 +9621,6 @@ scan_directory(struct mg_connection *conn,
 | 
	
		
			
				|  |  |  	if ((dirp = mg_opendir(conn, dir)) == NULL) {
 | 
	
		
			
				|  |  |  		return 0;
 | 
	
		
			
				|  |  |  	} else {
 | 
	
		
			
				|  |  | -		de.conn = conn;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		while ((dp = mg_readdir(dirp)) != NULL) {
 | 
	
		
			
				|  |  |  			/* Do not show current dir and hidden files */
 | 
	
	
		
			
				|  | @@ -9662,7 +9678,6 @@ remove_directory(struct mg_connection *conn, const char *dir)
 | 
	
		
			
				|  |  |  	if ((dirp = mg_opendir(conn, dir)) == NULL) {
 | 
	
		
			
				|  |  |  		return 0;
 | 
	
		
			
				|  |  |  	} else {
 | 
	
		
			
				|  |  | -		de.conn = conn;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		while ((dp = mg_readdir(dirp)) != NULL) {
 | 
	
		
			
				|  |  |  			/* Do not show current dir (but show hidden files as they will
 | 
	
	
		
			
				|  | @@ -9750,7 +9765,6 @@ dir_scan_callback(struct de *de, void *data)
 | 
	
		
			
				|  |  |  		return 1;
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	entries[dsd->num_entries].file = de->file;
 | 
	
		
			
				|  |  | -	entries[dsd->num_entries].conn = de->conn;
 | 
	
		
			
				|  |  |  	dsd->num_entries++;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	return 0;
 | 
	
	
		
			
				|  | @@ -9850,12 +9864,13 @@ handle_directory_request(struct mg_connection *conn, const char *dir)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/* Sort and print directory entries */
 | 
	
		
			
				|  |  |  	if (data.entries != NULL) {
 | 
	
		
			
				|  |  | -		qsort(data.entries,
 | 
	
		
			
				|  |  | -		      data.num_entries,
 | 
	
		
			
				|  |  | -		      sizeof(data.entries[0]),
 | 
	
		
			
				|  |  | -		      compare_dir_entries);
 | 
	
		
			
				|  |  | +		qsort_s(data.entries,
 | 
	
		
			
				|  |  | +		        data.num_entries,
 | 
	
		
			
				|  |  | +		        sizeof(data.entries[0]),
 | 
	
		
			
				|  |  | +		        compare_dir_entries,
 | 
	
		
			
				|  |  | +		        conn->request_info.query_string);
 | 
	
		
			
				|  |  |  		for (i = 0; i < data.num_entries; i++) {
 | 
	
		
			
				|  |  | -			print_dir_entry(&data.entries[i]);
 | 
	
		
			
				|  |  | +			print_dir_entry(conn, &data.entries[i]);
 | 
	
		
			
				|  |  |  			mg_free(data.entries[i].file_name);
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  		mg_free(data.entries);
 | 
	
	
		
			
				|  | @@ -11811,38 +11826,40 @@ get_rel_url_at_current_server(const char *uri,
 | 
	
		
			
				|  |  |  static void
 | 
	
		
			
				|  |  |  dav_move_file(struct mg_connection *conn, const char *path, int do_copy)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -	const char *overwrite;
 | 
	
		
			
				|  |  | -	const char *destination;
 | 
	
		
			
				|  |  | +	const char *overwrite_hdr;
 | 
	
		
			
				|  |  | +	const char *destination_hdr;
 | 
	
		
			
				|  |  |  	const char *root;
 | 
	
		
			
				|  |  |  	int rc, dest_uri_type;
 | 
	
		
			
				|  |  |  	int http_status = 400;
 | 
	
		
			
				|  |  | -	int do_override = 0;
 | 
	
		
			
				|  |  | +	int do_overwrite = 0;
 | 
	
		
			
				|  |  |  	int destination_ok = 0;
 | 
	
		
			
				|  |  |  	char dest_path[UTF8_PATH_MAX];
 | 
	
		
			
				|  |  | +	struct mg_file_stat ignored;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if (conn == NULL) {
 | 
	
		
			
				|  |  |  		return;
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	root = conn->dom_ctx->config[DOCUMENT_ROOT];
 | 
	
		
			
				|  |  | -	overwrite = mg_get_header(conn, "Overwrite");
 | 
	
		
			
				|  |  | -	destination = mg_get_header(conn, "Destination");
 | 
	
		
			
				|  |  | -	if ((overwrite != NULL) && (toupper(overwrite[0]) == 'T')) {
 | 
	
		
			
				|  |  | -		do_override = 1;
 | 
	
		
			
				|  |  | +	overwrite_hdr = mg_get_header(conn, "Overwrite");
 | 
	
		
			
				|  |  | +	destination_hdr = mg_get_header(conn, "Destination");
 | 
	
		
			
				|  |  | +	if ((overwrite_hdr != NULL) && (toupper(overwrite_hdr[0]) == 'T')) {
 | 
	
		
			
				|  |  | +		do_overwrite = 1;
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	if ((destination == NULL) || (destination[0] == 0)) {
 | 
	
		
			
				|  |  | +	if ((destination_hdr == NULL) || (destination_hdr[0] == 0)) {
 | 
	
		
			
				|  |  |  		mg_send_http_error(conn, 400, "%s", "Missing destination");
 | 
	
		
			
				|  |  |  		return;
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if (root != NULL) {
 | 
	
		
			
				|  |  |  		char *local_dest = NULL;
 | 
	
		
			
				|  |  | -		dest_uri_type = get_uri_type(destination);
 | 
	
		
			
				|  |  | +		dest_uri_type = get_uri_type(destination_hdr);
 | 
	
		
			
				|  |  |  		if (dest_uri_type == 2) {
 | 
	
		
			
				|  |  | -			local_dest = mg_strdup_ctx(destination, conn->phys_ctx);
 | 
	
		
			
				|  |  | +			local_dest = mg_strdup_ctx(destination_hdr, conn->phys_ctx);
 | 
	
		
			
				|  |  |  		} else if ((dest_uri_type == 3) || (dest_uri_type == 4)) {
 | 
	
		
			
				|  |  | -			const char *h = get_rel_url_at_current_server(destination, conn);
 | 
	
		
			
				|  |  | +			const char *h =
 | 
	
		
			
				|  |  | +			    get_rel_url_at_current_server(destination_hdr, conn);
 | 
	
		
			
				|  |  |  			if (h) {
 | 
	
		
			
				|  |  |  				local_dest = mg_strdup_ctx(h, conn->phys_ctx);
 | 
	
		
			
				|  |  |  			}
 | 
	
	
		
			
				|  | @@ -11871,9 +11888,26 @@ dav_move_file(struct mg_connection *conn, const char *path, int do_copy)
 | 
	
		
			
				|  |  |  		return;
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	/* Check now if this file exists */
 | 
	
		
			
				|  |  | +	if (mg_stat(conn, dest_path, &ignored)) {
 | 
	
		
			
				|  |  | +		/* File exists */
 | 
	
		
			
				|  |  | +		if (do_overwrite) {
 | 
	
		
			
				|  |  | +			/* Overwrite allowed: delete the file first */
 | 
	
		
			
				|  |  | +			remove(dest_path);
 | 
	
		
			
				|  |  | +		} else {
 | 
	
		
			
				|  |  | +			/* No overwrite: return error */
 | 
	
		
			
				|  |  | +			mg_send_http_error(conn,
 | 
	
		
			
				|  |  | +			                   412,
 | 
	
		
			
				|  |  | +			                   "Destination already exists: %s",
 | 
	
		
			
				|  |  | +			                   dest_path);
 | 
	
		
			
				|  |  | +			return;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	/* Copy / Move / Rename operation. */
 | 
	
		
			
				|  |  |  #if defined(_WIN32)
 | 
	
		
			
				|  |  |  	{
 | 
	
		
			
				|  |  | +		/* For Windows, we need to convert from UTF-8 to UTF-16 first. */
 | 
	
		
			
				|  |  |  		wchar_t wSource[UTF16_PATH_MAX];
 | 
	
		
			
				|  |  |  		wchar_t wDest[UTF16_PATH_MAX];
 | 
	
		
			
				|  |  |  		BOOL ok;
 | 
	
	
		
			
				|  | @@ -11881,67 +11915,64 @@ dav_move_file(struct mg_connection *conn, const char *path, int do_copy)
 | 
	
		
			
				|  |  |  		path_to_unicode(conn, path, wSource, ARRAY_SIZE(wSource));
 | 
	
		
			
				|  |  |  		path_to_unicode(conn, dest_path, wDest, ARRAY_SIZE(wDest));
 | 
	
		
			
				|  |  |  		if (do_copy) {
 | 
	
		
			
				|  |  | -			ok = CopyFileW(wSource, wDest, do_override ? FALSE : TRUE);
 | 
	
		
			
				|  |  | +			ok = CopyFileW(wSource, wDest, do_overwrite ? FALSE : TRUE);
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  |  			ok = MoveFileExW(wSource,
 | 
	
		
			
				|  |  |  			                 wDest,
 | 
	
		
			
				|  |  | -			                 do_override ? MOVEFILE_REPLACE_EXISTING : 0);
 | 
	
		
			
				|  |  | +			                 do_overwrite ? MOVEFILE_REPLACE_EXISTING : 0);
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  		if (ok) {
 | 
	
		
			
				|  |  |  			rc = 0;
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  |  			DWORD lastErr = GetLastError();
 | 
	
		
			
				|  |  |  			if (lastErr == ERROR_ALREADY_EXISTS) {
 | 
	
		
			
				|  |  | -				http_status = 412;
 | 
	
		
			
				|  |  | +				mg_send_http_error(conn,
 | 
	
		
			
				|  |  | +				                   412,
 | 
	
		
			
				|  |  | +				                   "Destination already exists: %s",
 | 
	
		
			
				|  |  | +				                   dest_path);
 | 
	
		
			
				|  |  | +				return;
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  			rc = -1;
 | 
	
		
			
				|  |  | +			http_status = 400;
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #else
 | 
	
		
			
				|  |  | +	{
 | 
	
		
			
				|  |  | +		/* Linux uses already UTF-8, we don't need to convert file names. */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	if (do_copy) {
 | 
	
		
			
				|  |  | -		/* TODO: COPY for Linux. */
 | 
	
		
			
				|  |  | -		mg_send_http_error(conn, 403, "%s", "COPY forbidden");
 | 
	
		
			
				|  |  | -		return;
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | +		if (do_copy) {
 | 
	
		
			
				|  |  | +			/* TODO: COPY for Linux. */
 | 
	
		
			
				|  |  | +			mg_send_http_error(conn, 403, "%s", "COPY forbidden");
 | 
	
		
			
				|  |  | +			return;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	/* Linux is already UTF-8 */
 | 
	
		
			
				|  |  | -	rc = rename(path, dest_path);
 | 
	
		
			
				|  |  | -	if (rc) {
 | 
	
		
			
				|  |  | -		switch (errno) {
 | 
	
		
			
				|  |  | -		case EEXIST:
 | 
	
		
			
				|  |  | -			http_status = 412;
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		case EACCES:
 | 
	
		
			
				|  |  | -			http_status = 403;
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | -		case ENOENT:
 | 
	
		
			
				|  |  | -			http_status = 409;
 | 
	
		
			
				|  |  | -			break;
 | 
	
		
			
				|  |  | +		rc = rename(path, dest_path);
 | 
	
		
			
				|  |  | +		if (rc) {
 | 
	
		
			
				|  |  | +			switch (errno) {
 | 
	
		
			
				|  |  | +			case EEXIST:
 | 
	
		
			
				|  |  | +				http_status = 412;
 | 
	
		
			
				|  |  | +				break;
 | 
	
		
			
				|  |  | +			case EACCES:
 | 
	
		
			
				|  |  | +				http_status = 403;
 | 
	
		
			
				|  |  | +				break;
 | 
	
		
			
				|  |  | +			case ENOENT:
 | 
	
		
			
				|  |  | +				http_status = 409;
 | 
	
		
			
				|  |  | +				break;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if (rc == 0) {
 | 
	
		
			
				|  |  | -		/* Create 201 "Created" response */
 | 
	
		
			
				|  |  | -		mg_response_header_start(conn, 201);
 | 
	
		
			
				|  |  | -		send_static_cache_header(conn);
 | 
	
		
			
				|  |  | -		send_additional_header(conn);
 | 
	
		
			
				|  |  | +		/* Create 204 "No Content" response */
 | 
	
		
			
				|  |  | +		mg_response_header_start(conn, 204);
 | 
	
		
			
				|  |  |  		mg_response_header_add(conn, "Content-Length", "0", -1);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		/* Send all headers - there is no body */
 | 
	
		
			
				|  |  |  		mg_response_header_send(conn);
 | 
	
		
			
				|  |  | -	} else if (http_status == 412) {
 | 
	
		
			
				|  |  | -		mg_send_http_error(conn,
 | 
	
		
			
				|  |  | -		                   412,
 | 
	
		
			
				|  |  | -		                   "Destination already exists: %s",
 | 
	
		
			
				|  |  | -		                   dest_path);
 | 
	
		
			
				|  |  |  	} else {
 | 
	
		
			
				|  |  | -		mg_send_http_error(conn,
 | 
	
		
			
				|  |  | -		                   http_status,
 | 
	
		
			
				|  |  | -		                   "Operation failed with code %i",
 | 
	
		
			
				|  |  | -		                   rc);
 | 
	
		
			
				|  |  | +		mg_send_http_error(conn, http_status, "Operation failed");
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -12534,17 +12565,6 @@ print_dav_dir_entry(struct de *de, void *data)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#define NUM_WEBDAV_LOCKS 10
 | 
	
		
			
				|  |  | -struct tdav_lock {
 | 
	
		
			
				|  |  | -	uint64_t locktime;
 | 
	
		
			
				|  |  | -	char token[33];
 | 
	
		
			
				|  |  | -	char path[UTF8_PATH_MAX * 2];
 | 
	
		
			
				|  |  | -	char user[UTF8_PATH_MAX * 2];
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -static uint32_t LOCK_DURATION_S = 60;
 | 
	
		
			
				|  |  | -static struct tdav_lock dav_lock[NUM_WEBDAV_LOCKS];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  static void
 | 
	
		
			
				|  |  |  handle_propfind(struct mg_connection *conn,
 | 
	
		
			
				|  |  |                  const char *path,
 | 
	
	
		
			
				|  | @@ -12552,6 +12572,7 @@ handle_propfind(struct mg_connection *conn,
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
 | 
	
		
			
				|  |  |  	const char *depth = mg_get_header(conn, "Depth");
 | 
	
		
			
				|  |  | +	struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
 | 
	
		
			
				|  |  |  	int i;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if (!conn || !path || !filep || !conn->dom_ctx) {
 | 
	
	
		
			
				|  | @@ -12590,6 +12611,7 @@ handle_propfind(struct mg_connection *conn,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/* add lock discovery data */
 | 
	
		
			
				|  |  |  	if (!filep->is_directory) {
 | 
	
		
			
				|  |  | +		/* Lock information*/
 | 
	
		
			
				|  |  |  		mg_printf(conn,
 | 
	
		
			
				|  |  |  		          "<d:response>"
 | 
	
		
			
				|  |  |  		          "<d:href>%s</d:href>"
 | 
	
	
		
			
				|  | @@ -12612,7 +12634,7 @@ handle_propfind(struct mg_connection *conn,
 | 
	
		
			
				|  |  |  				          "</d:locktoken>"
 | 
	
		
			
				|  |  |  				          "</d:activelock>\n",
 | 
	
		
			
				|  |  |  				          dav_lock[i].user,
 | 
	
		
			
				|  |  | -				          LOCK_DURATION_S,
 | 
	
		
			
				|  |  | +				          (unsigned)LOCK_DURATION_S,
 | 
	
		
			
				|  |  |  				          dav_lock[i].token);
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  		}
 | 
	
	
		
			
				|  | @@ -12636,7 +12658,9 @@ dav_lock_file(struct mg_connection *conn, const char *path)
 | 
	
		
			
				|  |  |  	uint64_t new_locktime;
 | 
	
		
			
				|  |  |  	int lock_index = -1;
 | 
	
		
			
				|  |  |  	int i;
 | 
	
		
			
				|  |  | -	uint64_t LOCK_DURATION_NS = LOCK_DURATION_S * (int64_t)1000000000;
 | 
	
		
			
				|  |  | +	uint64_t LOCK_DURATION_NS =
 | 
	
		
			
				|  |  | +	    (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000;
 | 
	
		
			
				|  |  | +	struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if (!conn || !path || !conn->dom_ctx || !conn->request_info.remote_user) {
 | 
	
		
			
				|  |  |  		return;
 | 
	
	
		
			
				|  | @@ -12749,7 +12773,7 @@ dav_lock_file(struct mg_connection *conn, const char *path)
 | 
	
		
			
				|  |  |  	          "     </d:lockdiscovery>\n"
 | 
	
		
			
				|  |  |  	          "   </d:prop>\n",
 | 
	
		
			
				|  |  |  	          dav_lock[lock_index].user,
 | 
	
		
			
				|  |  | -	          LOCK_DURATION_S,
 | 
	
		
			
				|  |  | +	          (LOCK_DURATION_S),
 | 
	
		
			
				|  |  |  	          dav_lock[lock_index].token,
 | 
	
		
			
				|  |  |  	          dav_lock[lock_index].path);
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -12759,6 +12783,7 @@ static void
 | 
	
		
			
				|  |  |  dav_unlock_file(struct mg_connection *conn, const char *path)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
 | 
	
		
			
				|  |  | +	struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
 | 
	
		
			
				|  |  |  	int lock_index;
 | 
	
		
			
				|  |  |  	if (!conn || !path || !conn->dom_ctx || !conn->request_info.remote_user) {
 | 
	
		
			
				|  |  |  		return;
 | 
	
	
		
			
				|  | @@ -14593,6 +14618,12 @@ handle_request(struct mg_connection *conn)
 | 
	
		
			
				|  |  |  	/* step 1. completed, the url is known now */
 | 
	
		
			
				|  |  |  	DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	/*
 | 
	
		
			
				|  |  | +	char req_str[1024];
 | 
	
		
			
				|  |  | +	sprintf(req_str, "REQUEST: %s %s\n", ri->request_method, ri->local_uri);
 | 
	
		
			
				|  |  | +	OutputDebugStringA(req_str);
 | 
	
		
			
				|  |  | +	/**/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	/* 2. if this ip has limited speed, set it for this connection */
 | 
	
		
			
				|  |  |  	conn->throttle = set_throttle(conn->dom_ctx->config[THROTTLE],
 | 
	
		
			
				|  |  |  	                              &conn->client.rsa,
 |