Commit 3b69f5d5 authored by Deomid Ryabkov's avatar Deomid Ryabkov Committed by Cesanta Bot

MG_DISABLE_DAV -> MG_ENABLE_HTTP_WEBDAV

PUBLISHED_FROM=62267ea0a8e10d8ba7bad590d1a56b179bcffce9
parent b298d46a
...@@ -5,9 +5,10 @@ title: Enabling flags ...@@ -5,9 +5,10 @@ title: Enabling flags
- `MG_ENABLE_CGI` Enable CGI support - `MG_ENABLE_CGI` Enable CGI support
- `MG_ENABLE_SSL` Enable SSL/TLS support (OpenSSL API) - `MG_ENABLE_SSL` Enable SSL/TLS support (OpenSSL API)
- `MG_ENABLE_IPV6` Enable IPV6 support - `MG_ENABLE_IPV6` Enable IPV6 support
- `MG_ENABLE_THREADS` enable `mg_start_thread()` API
- `MG_ENABLE_MQTT` enable MQTT client - `MG_ENABLE_MQTT` enable MQTT client
- `MG_ENABLE_MQTT_BROKER` enable MQTT broker - `MG_ENABLE_MQTT_BROKER` enable MQTT broker
- `MG_ENABLE_DNS_SERVER` enable DNS server - `MG_ENABLE_DNS_SERVER` enable DNS server
- `MG_ENABLE_COAP` enable CoAP protocol - `MG_ENABLE_COAP` enable CoAP protocol
- `MG_ENABLE_HTTP_WEBDAV` enable WebDAV extensions to HTTP
- `MG_ENABLE_GETADDRINFO` enable `getaddrinfo()` in `mg_resolve2()` - `MG_ENABLE_GETADDRINFO` enable `getaddrinfo()` in `mg_resolve2()`
- `MG_ENABLE_THREADS` enable `mg_start_thread()` API
...@@ -130,6 +130,23 @@ MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog, ...@@ -130,6 +130,23 @@ MG_INTERNAL void mg_handle_cgi(struct mg_connection *nc, const char *prog,
struct mg_http_proto_data_cgi; struct mg_http_proto_data_cgi;
MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d); MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d);
#endif #endif
#if !MG_DISABLE_HTTP && MG_ENABLE_HTTP_WEBDAV
MG_INTERNAL int mg_is_dav_request(const struct mg_str *s);
MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path,
cs_stat_t *stp, struct http_message *hm,
struct mg_serve_http_opts *opts);
MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path);
MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path,
struct http_message *hm);
MG_INTERNAL void mg_handle_move(struct mg_connection *c,
const struct mg_serve_http_opts *opts,
const char *path, struct http_message *hm);
MG_INTERNAL void mg_handle_delete(struct mg_connection *nc,
const struct mg_serve_http_opts *opts,
const char *path);
MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path,
struct http_message *hm);
#endif
#endif /* CS_MONGOOSE_SRC_INTERNAL_H_ */ #endif /* CS_MONGOOSE_SRC_INTERNAL_H_ */
#ifdef MG_MODULE_LINES #ifdef MG_MODULE_LINES
...@@ -3999,17 +4016,6 @@ static const struct { ...@@ -3999,17 +4016,6 @@ static const struct {
MIME_ENTRY("bmp", "image/bmp"), MIME_ENTRY("bmp", "image/bmp"),
{NULL, 0, NULL}}; {NULL, 0, NULL}};
#if !MG_DISABLE_DAV
static int mg_mkdir(const char *path, uint32_t mode) {
#ifndef _WIN32
return mkdir(path, mode);
#else
(void) mode;
return _mkdir(path);
#endif
}
#endif
static struct mg_str mg_get_mime_type(const char *path, const char *dflt, static struct mg_str mg_get_mime_type(const char *path, const char *dflt,
const struct mg_serve_http_opts *opts) { const struct mg_serve_http_opts *opts) {
const char *ext, *overrides; const char *ext, *overrides;
...@@ -6196,367 +6202,114 @@ static void mg_send_directory_listing(struct mg_connection *nc, const char *dir, ...@@ -6196,367 +6202,114 @@ static void mg_send_directory_listing(struct mg_connection *nc, const char *dir,
} }
#endif /* MG_DISABLE_DIRECTORY_LISTING */ #endif /* MG_DISABLE_DIRECTORY_LISTING */
#if !MG_DISABLE_DAV /*
static void mg_print_props(struct mg_connection *nc, const char *name, * Given a directory path, find one of the files specified in the
cs_stat_t *stp) { * comma-separated list of index files `list`.
char mtime[64], buf[MAX_PATH_SIZE * 3]; * First found index file wins. If an index file is found, then gets
time_t t = stp->st_mtime; /* store in local variable for NDK compile */ * appended to the `path`, stat-ed, and result of `stat()` passed to `stp`.
mg_gmt_time_string(mtime, sizeof(mtime), &t); * If index file is not found, then `path` and `stp` remain unchanged.
mg_url_encode(name, strlen(name), buf, sizeof(buf)); */
mg_printf(nc, MG_INTERNAL void mg_find_index_file(const char *path, const char *list,
"<d:response>" char **index_file, cs_stat_t *stp) {
"<d:href>%s</d:href>" struct mg_str vec;
"<d:propstat>" size_t path_len = strlen(path);
"<d:prop>" int found = 0;
"<d:resourcetype>%s</d:resourcetype>" *index_file = NULL;
"<d:getcontentlength>%" INT64_FMT
"</d:getcontentlength>"
"<d:getlastmodified>%s</d:getlastmodified>"
"</d:prop>"
"<d:status>HTTP/1.1 200 OK</d:status>"
"</d:propstat>"
"</d:response>\n",
buf, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "",
(int64_t) stp->st_size, mtime);
}
static void mg_handle_propfind(struct mg_connection *nc, const char *path, /* Traverse index files list. For each entry, append it to the given */
cs_stat_t *stp, struct http_message *hm, /* path and see if the file exists. If it exists, break the loop */
struct mg_serve_http_opts *opts) { while ((list = mg_next_comma_list_entry(list, &vec, NULL)) != NULL) {
static const char header[] = cs_stat_t st;
"HTTP/1.1 207 Multi-Status\r\n" size_t len = path_len + 1 + vec.len + 1;
"Connection: close\r\n" *index_file = (char *) MG_REALLOC(*index_file, len);
"Content-Type: text/xml; charset=utf-8\r\n\r\n" if (*index_file == NULL) break;
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" snprintf(*index_file, len, "%s%c%.*s", path, DIRSEP, (int) vec.len, vec.p);
"<d:multistatus xmlns:d='DAV:'>\n";
static const char footer[] = "</d:multistatus>\n";
const struct mg_str *depth = mg_get_http_header(hm, "Depth");
/* Print properties for the requested resource itself */ /* Does it exist? Is it a file? */
if (S_ISDIR(stp->st_mode) && if (mg_stat(*index_file, &st) == 0 && S_ISREG(st.st_mode)) {
strcmp(opts->enable_directory_listing, "yes") != 0) { /* Yes it does, break the loop */
mg_printf(nc, "%s", "HTTP/1.1 403 Directory Listing Denied\r\n\r\n"); *stp = st;
} else { found = 1;
char uri[MAX_PATH_SIZE]; break;
mg_send(nc, header, sizeof(header) - 1);
snprintf(uri, sizeof(uri), "%.*s", (int) hm->uri.len, hm->uri.p);
mg_print_props(nc, uri, stp);
if (S_ISDIR(stp->st_mode) && (depth == NULL || mg_vcmp(depth, "0") != 0)) {
mg_scan_directory(nc, path, opts, mg_print_props);
} }
mg_send(nc, footer, sizeof(footer) - 1);
nc->flags |= MG_F_SEND_AND_CLOSE;
} }
} if (!found) {
MG_FREE(*index_file);
#if MG_ENABLE_FAKE_DAVLOCK *index_file = NULL;
/*
* Windows explorer (probably there are another WebDav clients like it)
* requires LOCK support in webdav. W/out this, it still works, but fails
* to save file: shows error message and offers "Save As".
* "Save as" works, but this message is very annoying.
* This is fake lock, which doesn't lock something, just returns LOCK token,
* UNLOCK always answers "OK".
* With this fake LOCK Windows Explorer looks happy and saves file.
* NOTE: that is not DAV LOCK imlementation, it is just a way to shut up
* Windows native DAV client. This is why FAKE LOCK is not enabed by default
*/
static void mg_handle_lock(struct mg_connection *nc, const char *path) {
static const char *reply =
"HTTP/1.1 207 Multi-Status\r\n"
"Connection: close\r\n"
"Content-Type: text/xml; charset=utf-8\r\n\r\n"
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<d:multistatus xmlns:d='DAV:'>\n"
"<D:lockdiscovery>\n"
"<D:activelock>\n"
"<D:locktoken>\n"
"<D:href>\n"
"opaquelocktoken:%s%u"
"</D:href>"
"</D:locktoken>"
"</D:activelock>\n"
"</D:lockdiscovery>"
"</d:multistatus>\n";
mg_printf(nc, reply, path, (unsigned int) time(NULL));
nc->flags |= MG_F_SEND_AND_CLOSE;
}
#endif
static void mg_handle_mkcol(struct mg_connection *nc, const char *path,
struct http_message *hm) {
int status_code = 500;
if (hm->body.len != (size_t) ~0 && hm->body.len > 0) {
status_code = 415;
} else if (!mg_mkdir(path, 0755)) {
status_code = 201;
} else if (errno == EEXIST) {
status_code = 405;
} else if (errno == EACCES) {
status_code = 403;
} else if (errno == ENOENT) {
status_code = 409;
} else {
status_code = 500;
} }
mg_http_send_error(nc, status_code, NULL); DBG(("[%s] [%s]", path, (*index_file ? *index_file : "")));
} }
static int mg_remove_directory(const struct mg_serve_http_opts *opts, static int mg_http_send_port_based_redirect(
const char *dir) { struct mg_connection *c, struct http_message *hm,
char path[MAX_PATH_SIZE]; const struct mg_serve_http_opts *opts) {
struct dirent *dp; const char *rewrites = opts->url_rewrites;
cs_stat_t st; struct mg_str a, b;
DIR *dirp; char local_port[20] = {'%'};
if ((dirp = opendir(dir)) == NULL) return 0; mg_conn_addr_to_str(c, local_port + 1, sizeof(local_port) - 1,
MG_SOCK_STRINGIFY_PORT);
while ((dp = readdir(dirp)) != NULL) { while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) {
if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) { if (mg_vcmp(&a, local_port) == 0) {
continue; mg_send_response_line(c, 301, NULL);
} mg_printf(c, "Content-Length: 0\r\nLocation: %.*s%.*s\r\n\r\n",
snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name); (int) b.len, b.p, (int) (hm->proto.p - hm->uri.p - 1),
mg_stat(path, &st); hm->uri.p);
if (S_ISDIR(st.st_mode)) { return 1;
mg_remove_directory(opts, path);
} else {
remove(path);
} }
} }
closedir(dirp);
rmdir(dir);
return 1; return 0;
} }
static void mg_handle_move(struct mg_connection *c, MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm,
const struct mg_serve_http_opts *opts, const struct mg_serve_http_opts *opts,
const char *path, struct http_message *hm) { char **local_path,
const struct mg_str *dest = mg_get_http_header(hm, "Destination"); struct mg_str *remainder) {
if (dest == NULL) { int ok = 1;
mg_http_send_error(c, 411, NULL); const char *cp = hm->uri.p, *cp_end = hm->uri.p + hm->uri.len;
struct mg_str root = {NULL, 0};
const char *file_uri_start = cp;
*local_path = NULL;
remainder->p = NULL;
remainder->len = 0;
{ /* 1. Determine which root to use. */
const char *rewrites = opts->url_rewrites;
struct mg_str *hh = mg_get_http_header(hm, "Host");
struct mg_str a, b;
/* Check rewrites first. */
while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) {
if (a.len > 1 && a.p[0] == '@') {
/* Host rewrite. */
if (hh != NULL && hh->len == a.len - 1 &&
mg_ncasecmp(a.p + 1, hh->p, a.len - 1) == 0) {
root = b;
break;
}
} else { } else {
const char *p = (char *) memchr(dest->p, '/', dest->len); /* Regular rewrite, URI=directory */
if (p != NULL && p[1] == '/' && int match_len = mg_match_prefix_n(a, hm->uri);
(p = (char *) memchr(p + 2, '/', dest->p + dest->len - p)) != NULL) { if (match_len > 0) {
char buf[MAX_PATH_SIZE]; file_uri_start = hm->uri.p + match_len;
snprintf(buf, sizeof(buf), "%s%.*s", opts->dav_document_root, if (*file_uri_start == '/' || file_uri_start == cp_end) {
(int) (dest->p + dest->len - p), p); /* Match ended at component boundary, ok. */
if (rename(path, buf) == 0) { } else if (*(file_uri_start - 1) == '/') {
mg_http_send_error(c, 200, NULL); /* Pattern ends with '/', backtrack. */
file_uri_start--;
} else { } else {
mg_http_send_error(c, 418, NULL); /* No match: must fall on the component boundary. */
continue;
} }
} else { root = b;
mg_http_send_error(c, 500, NULL); break;
}
}
}
static void mg_handle_delete(struct mg_connection *nc,
const struct mg_serve_http_opts *opts,
const char *path) {
cs_stat_t st;
if (mg_stat(path, &st) != 0) {
mg_http_send_error(nc, 404, NULL);
} else if (S_ISDIR(st.st_mode)) {
mg_remove_directory(opts, path);
mg_http_send_error(nc, 204, NULL);
} else if (remove(path) == 0) {
mg_http_send_error(nc, 204, NULL);
} else {
mg_http_send_error(nc, 423, NULL);
}
}
/* Return -1 on error, 1 on success. */
static int mg_create_itermediate_directories(const char *path) {
const char *s;
/* Create intermediate directories if they do not exist */
for (s = path + 1; *s != '\0'; s++) {
if (*s == '/') {
char buf[MAX_PATH_SIZE];
cs_stat_t st;
snprintf(buf, sizeof(buf), "%.*s", (int) (s - path), path);
buf[sizeof(buf) - 1] = '\0';
if (mg_stat(buf, &st) != 0 && mg_mkdir(buf, 0755) != 0) {
return -1;
}
}
}
return 1;
}
static void mg_handle_put(struct mg_connection *nc, const char *path,
struct http_message *hm) {
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
cs_stat_t st;
const struct mg_str *cl_hdr = mg_get_http_header(hm, "Content-Length");
int rc, status_code = mg_stat(path, &st) == 0 ? 200 : 201;
mg_http_free_proto_data_file(&pd->file);
if ((rc = mg_create_itermediate_directories(path)) == 0) {
mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code);
} else if (rc == -1) {
mg_http_send_error(nc, 500, NULL);
} else if (cl_hdr == NULL) {
mg_http_send_error(nc, 411, NULL);
} else if ((pd->file.fp = fopen(path, "w+b")) == NULL) {
mg_http_send_error(nc, 500, NULL);
} else {
const struct mg_str *range_hdr = mg_get_http_header(hm, "Content-Range");
int64_t r1 = 0, r2 = 0;
pd->file.type = DATA_PUT;
mg_set_close_on_exec(fileno(pd->file.fp));
pd->file.cl = to64(cl_hdr->p);
if (range_hdr != NULL &&
mg_http_parse_range_header(range_hdr, &r1, &r2) > 0) {
status_code = 206;
fseeko(pd->file.fp, r1, SEEK_SET);
pd->file.cl = r2 > r1 ? r2 - r1 + 1 : pd->file.cl - r1;
}
mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code);
/* Remove HTTP request from the mbuf, leave only payload */
mbuf_remove(&nc->recv_mbuf, hm->message.len - hm->body.len);
mg_http_transfer_file_data(nc);
}
}
#endif /* MG_DISABLE_DAV */
static int mg_is_dav_request(const struct mg_str *s) {
static const char *methods[] = {
"PUT",
"DELETE",
"MKCOL",
"PROPFIND",
"MOVE"
#if MG_ENABLE_FAKE_DAVLOCK
,
"LOCK",
"UNLOCK"
#endif
};
size_t i;
for (i = 0; i < ARRAY_SIZE(methods); i++) {
if (mg_vcmp(s, methods[i]) == 0) {
return 1;
}
}
return 0;
}
/*
* Given a directory path, find one of the files specified in the
* comma-separated list of index files `list`.
* First found index file wins. If an index file is found, then gets
* appended to the `path`, stat-ed, and result of `stat()` passed to `stp`.
* If index file is not found, then `path` and `stp` remain unchanged.
*/
MG_INTERNAL void mg_find_index_file(const char *path, const char *list,
char **index_file, cs_stat_t *stp) {
struct mg_str vec;
size_t path_len = strlen(path);
int found = 0;
*index_file = NULL;
/* Traverse index files list. For each entry, append it to the given */
/* path and see if the file exists. If it exists, break the loop */
while ((list = mg_next_comma_list_entry(list, &vec, NULL)) != NULL) {
cs_stat_t st;
size_t len = path_len + 1 + vec.len + 1;
*index_file = (char *) MG_REALLOC(*index_file, len);
if (*index_file == NULL) break;
snprintf(*index_file, len, "%s%c%.*s", path, DIRSEP, (int) vec.len, vec.p);
/* Does it exist? Is it a file? */
if (mg_stat(*index_file, &st) == 0 && S_ISREG(st.st_mode)) {
/* Yes it does, break the loop */
*stp = st;
found = 1;
break;
}
}
if (!found) {
MG_FREE(*index_file);
*index_file = NULL;
}
DBG(("[%s] [%s]", path, (*index_file ? *index_file : "")));
}
static int mg_http_send_port_based_redirect(
struct mg_connection *c, struct http_message *hm,
const struct mg_serve_http_opts *opts) {
const char *rewrites = opts->url_rewrites;
struct mg_str a, b;
char local_port[20] = {'%'};
mg_conn_addr_to_str(c, local_port + 1, sizeof(local_port) - 1,
MG_SOCK_STRINGIFY_PORT);
while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) {
if (mg_vcmp(&a, local_port) == 0) {
mg_send_response_line(c, 301, NULL);
mg_printf(c, "Content-Length: 0\r\nLocation: %.*s%.*s\r\n\r\n",
(int) b.len, b.p, (int) (hm->proto.p - hm->uri.p - 1),
hm->uri.p);
return 1;
}
}
return 0;
}
MG_INTERNAL int mg_uri_to_local_path(struct http_message *hm,
const struct mg_serve_http_opts *opts,
char **local_path,
struct mg_str *remainder) {
int ok = 1;
const char *cp = hm->uri.p, *cp_end = hm->uri.p + hm->uri.len;
struct mg_str root = {NULL, 0};
const char *file_uri_start = cp;
*local_path = NULL;
remainder->p = NULL;
remainder->len = 0;
{ /* 1. Determine which root to use. */
const char *rewrites = opts->url_rewrites;
struct mg_str *hh = mg_get_http_header(hm, "Host");
struct mg_str a, b;
/* Check rewrites first. */
while ((rewrites = mg_next_comma_list_entry(rewrites, &a, &b)) != NULL) {
if (a.len > 1 && a.p[0] == '@') {
/* Host rewrite. */
if (hh != NULL && hh->len == a.len - 1 &&
mg_ncasecmp(a.p + 1, hh->p, a.len - 1) == 0) {
root = b;
break;
}
} else {
/* Regular rewrite, URI=directory */
int match_len = mg_match_prefix_n(a, hm->uri);
if (match_len > 0) {
file_uri_start = hm->uri.p + match_len;
if (*file_uri_start == '/' || file_uri_start == cp_end) {
/* Match ended at component boundary, ok. */
} else if (*(file_uri_start - 1) == '/') {
/* Pattern ends with '/', backtrack. */
file_uri_start--;
} else {
/* No match: must fall on the component boundary. */
continue;
}
root = b;
break;
} }
} }
} }
/* If no rewrite rules matched, use DAV or regular document root. */ /* If no rewrite rules matched, use DAV or regular document root. */
if (root.p == NULL) { if (root.p == NULL) {
#if !MG_DISABLE_DAV #if MG_ENABLE_HTTP_WEBDAV
if (opts->dav_document_root != NULL && mg_is_dav_request(&hm->method)) { if (opts->dav_document_root != NULL && mg_is_dav_request(&hm->method)) {
root.p = opts->dav_document_root; root.p = opts->dav_document_root;
root.len = strlen(opts->dav_document_root); root.len = strlen(opts->dav_document_root);
...@@ -6729,7 +6482,7 @@ static void mg_http_send_digest_auth_request(struct mg_connection *c, ...@@ -6729,7 +6482,7 @@ static void mg_http_send_digest_auth_request(struct mg_connection *c,
static void mg_http_send_options(struct mg_connection *nc) { static void mg_http_send_options(struct mg_connection *nc) {
mg_printf(nc, "%s", mg_printf(nc, "%s",
"HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS" "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, OPTIONS"
#if !MG_DISABLE_DAV #if MG_ENABLE_HTTP_WEBDAV
", MKCOL, PUT, DELETE, PROPFIND, MOVE\r\nDAV: 1,2" ", MKCOL, PUT, DELETE, PROPFIND, MOVE\r\nDAV: 1,2"
#endif #endif
"\r\n\r\n"); "\r\n\r\n");
...@@ -6744,8 +6497,12 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, ...@@ -6744,8 +6497,12 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
const struct mg_str *path_info, const struct mg_str *path_info,
struct http_message *hm, struct http_message *hm,
struct mg_serve_http_opts *opts) { struct mg_serve_http_opts *opts) {
int exists, is_directory, is_dav = mg_is_dav_request(&hm->method); int exists, is_directory, is_cgi;
int is_cgi; #if MG_ENABLE_HTTP_WEBDAV
int is_dav = mg_is_dav_request(&hm->method);
#else
int is_dav = 0;
#endif
char *index_file = NULL; char *index_file = NULL;
cs_stat_t st; cs_stat_t st;
...@@ -6796,7 +6553,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, ...@@ -6796,7 +6553,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
mg_is_file_hidden(path, opts, 0 /* specials are ok */)) && mg_is_file_hidden(path, opts, 0 /* specials are ok */)) &&
!mg_is_creation_request(hm)) { !mg_is_creation_request(hm)) {
mg_http_send_error(nc, 404, NULL); mg_http_send_error(nc, 404, NULL);
#if !MG_DISABLE_DAV #if MG_ENABLE_HTTP_WEBDAV
} else if (!mg_vcmp(&hm->method, "PROPFIND")) { } else if (!mg_vcmp(&hm->method, "PROPFIND")) {
mg_handle_propfind(nc, path, &st, hm, opts); mg_handle_propfind(nc, path, &st, hm, opts);
#if !MG_DISABLE_DAV_AUTH #if !MG_DISABLE_DAV_AUTH
...@@ -6819,7 +6576,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path, ...@@ -6819,7 +6576,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
} else if (!mg_vcmp(&hm->method, "LOCK")) { } else if (!mg_vcmp(&hm->method, "LOCK")) {
mg_handle_lock(nc, path); mg_handle_lock(nc, path);
#endif #endif
#endif #endif /* MG_ENABLE_HTTP_WEBDAV */
} else if (!mg_vcmp(&hm->method, "OPTIONS")) { } else if (!mg_vcmp(&hm->method, "OPTIONS")) {
mg_http_send_options(nc); mg_http_send_options(nc);
} else if (is_directory && index_file == NULL) { } else if (is_directory && index_file == NULL) {
...@@ -7603,6 +7360,277 @@ MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) { ...@@ -7603,6 +7360,277 @@ MG_INTERNAL void mg_http_free_proto_data_cgi(struct mg_http_proto_data_cgi *d) {
#endif /* MG_ENABLE_HTTP && MG_ENABLE_CGI */ #endif /* MG_ENABLE_HTTP && MG_ENABLE_CGI */
#ifdef MG_MODULE_LINES #ifdef MG_MODULE_LINES
#line 1 "mongoose/src/http_webdav.c"
#endif
/*
* Copyright (c) 2014-2016 Cesanta Software Limited
* All rights reserved
*/
#if !MG_DISABLE_HTTP && MG_ENABLE_HTTP_WEBDAV
MG_INTERNAL int mg_is_dav_request(const struct mg_str *s) {
static const char *methods[] = {
"PUT",
"DELETE",
"MKCOL",
"PROPFIND",
"MOVE"
#if MG_ENABLE_FAKE_DAVLOCK
,
"LOCK",
"UNLOCK"
#endif
};
size_t i;
for (i = 0; i < ARRAY_SIZE(methods); i++) {
if (mg_vcmp(s, methods[i]) == 0) {
return 1;
}
}
return 0;
}
static int mg_mkdir(const char *path, uint32_t mode) {
#ifndef _WIN32
return mkdir(path, mode);
#else
(void) mode;
return _mkdir(path);
#endif
}
static void mg_print_props(struct mg_connection *nc, const char *name,
cs_stat_t *stp) {
char mtime[64], buf[MAX_PATH_SIZE * 3];
time_t t = stp->st_mtime; /* store in local variable for NDK compile */
mg_gmt_time_string(mtime, sizeof(mtime), &t);
mg_url_encode(name, strlen(name), buf, sizeof(buf));
mg_printf(nc,
"<d:response>"
"<d:href>%s</d:href>"
"<d:propstat>"
"<d:prop>"
"<d:resourcetype>%s</d:resourcetype>"
"<d:getcontentlength>%" INT64_FMT
"</d:getcontentlength>"
"<d:getlastmodified>%s</d:getlastmodified>"
"</d:prop>"
"<d:status>HTTP/1.1 200 OK</d:status>"
"</d:propstat>"
"</d:response>\n",
buf, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "",
(int64_t) stp->st_size, mtime);
}
MG_INTERNAL void mg_handle_propfind(struct mg_connection *nc, const char *path,
cs_stat_t *stp, struct http_message *hm,
struct mg_serve_http_opts *opts) {
static const char header[] =
"HTTP/1.1 207 Multi-Status\r\n"
"Connection: close\r\n"
"Content-Type: text/xml; charset=utf-8\r\n\r\n"
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<d:multistatus xmlns:d='DAV:'>\n";
static const char footer[] = "</d:multistatus>\n";
const struct mg_str *depth = mg_get_http_header(hm, "Depth");
/* Print properties for the requested resource itself */
if (S_ISDIR(stp->st_mode) &&
strcmp(opts->enable_directory_listing, "yes") != 0) {
mg_printf(nc, "%s", "HTTP/1.1 403 Directory Listing Denied\r\n\r\n");
} else {
char uri[MAX_PATH_SIZE];
mg_send(nc, header, sizeof(header) - 1);
snprintf(uri, sizeof(uri), "%.*s", (int) hm->uri.len, hm->uri.p);
mg_print_props(nc, uri, stp);
if (S_ISDIR(stp->st_mode) && (depth == NULL || mg_vcmp(depth, "0") != 0)) {
mg_scan_directory(nc, path, opts, mg_print_props);
}
mg_send(nc, footer, sizeof(footer) - 1);
nc->flags |= MG_F_SEND_AND_CLOSE;
}
}
#if MG_ENABLE_FAKE_DAVLOCK
/*
* Windows explorer (probably there are another WebDav clients like it)
* requires LOCK support in webdav. W/out this, it still works, but fails
* to save file: shows error message and offers "Save As".
* "Save as" works, but this message is very annoying.
* This is fake lock, which doesn't lock something, just returns LOCK token,
* UNLOCK always answers "OK".
* With this fake LOCK Windows Explorer looks happy and saves file.
* NOTE: that is not DAV LOCK imlementation, it is just a way to shut up
* Windows native DAV client. This is why FAKE LOCK is not enabed by default
*/
MG_INTERNAL void mg_handle_lock(struct mg_connection *nc, const char *path) {
static const char *reply =
"HTTP/1.1 207 Multi-Status\r\n"
"Connection: close\r\n"
"Content-Type: text/xml; charset=utf-8\r\n\r\n"
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<d:multistatus xmlns:d='DAV:'>\n"
"<D:lockdiscovery>\n"
"<D:activelock>\n"
"<D:locktoken>\n"
"<D:href>\n"
"opaquelocktoken:%s%u"
"</D:href>"
"</D:locktoken>"
"</D:activelock>\n"
"</D:lockdiscovery>"
"</d:multistatus>\n";
mg_printf(nc, reply, path, (unsigned int) time(NULL));
nc->flags |= MG_F_SEND_AND_CLOSE;
}
#endif
MG_INTERNAL void mg_handle_mkcol(struct mg_connection *nc, const char *path,
struct http_message *hm) {
int status_code = 500;
if (hm->body.len != (size_t) ~0 && hm->body.len > 0) {
status_code = 415;
} else if (!mg_mkdir(path, 0755)) {
status_code = 201;
} else if (errno == EEXIST) {
status_code = 405;
} else if (errno == EACCES) {
status_code = 403;
} else if (errno == ENOENT) {
status_code = 409;
} else {
status_code = 500;
}
mg_http_send_error(nc, status_code, NULL);
}
static int mg_remove_directory(const struct mg_serve_http_opts *opts,
const char *dir) {
char path[MAX_PATH_SIZE];
struct dirent *dp;
cs_stat_t st;
DIR *dirp;
if ((dirp = opendir(dir)) == NULL) return 0;
while ((dp = readdir(dirp)) != NULL) {
if (mg_is_file_hidden((const char *) dp->d_name, opts, 1)) {
continue;
}
snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
mg_stat(path, &st);
if (S_ISDIR(st.st_mode)) {
mg_remove_directory(opts, path);
} else {
remove(path);
}
}
closedir(dirp);
rmdir(dir);
return 1;
}
MG_INTERNAL void mg_handle_move(struct mg_connection *c,
const struct mg_serve_http_opts *opts,
const char *path, struct http_message *hm) {
const struct mg_str *dest = mg_get_http_header(hm, "Destination");
if (dest == NULL) {
mg_http_send_error(c, 411, NULL);
} else {
const char *p = (char *) memchr(dest->p, '/', dest->len);
if (p != NULL && p[1] == '/' &&
(p = (char *) memchr(p + 2, '/', dest->p + dest->len - p)) != NULL) {
char buf[MAX_PATH_SIZE];
snprintf(buf, sizeof(buf), "%s%.*s", opts->dav_document_root,
(int) (dest->p + dest->len - p), p);
if (rename(path, buf) == 0) {
mg_http_send_error(c, 200, NULL);
} else {
mg_http_send_error(c, 418, NULL);
}
} else {
mg_http_send_error(c, 500, NULL);
}
}
}
MG_INTERNAL void mg_handle_delete(struct mg_connection *nc,
const struct mg_serve_http_opts *opts,
const char *path) {
cs_stat_t st;
if (mg_stat(path, &st) != 0) {
mg_http_send_error(nc, 404, NULL);
} else if (S_ISDIR(st.st_mode)) {
mg_remove_directory(opts, path);
mg_http_send_error(nc, 204, NULL);
} else if (remove(path) == 0) {
mg_http_send_error(nc, 204, NULL);
} else {
mg_http_send_error(nc, 423, NULL);
}
}
/* Return -1 on error, 1 on success. */
static int mg_create_itermediate_directories(const char *path) {
const char *s;
/* Create intermediate directories if they do not exist */
for (s = path + 1; *s != '\0'; s++) {
if (*s == '/') {
char buf[MAX_PATH_SIZE];
cs_stat_t st;
snprintf(buf, sizeof(buf), "%.*s", (int) (s - path), path);
buf[sizeof(buf) - 1] = '\0';
if (mg_stat(buf, &st) != 0 && mg_mkdir(buf, 0755) != 0) {
return -1;
}
}
}
return 1;
}
MG_INTERNAL void mg_handle_put(struct mg_connection *nc, const char *path,
struct http_message *hm) {
struct mg_http_proto_data *pd = mg_http_get_proto_data(nc);
cs_stat_t st;
const struct mg_str *cl_hdr = mg_get_http_header(hm, "Content-Length");
int rc, status_code = mg_stat(path, &st) == 0 ? 200 : 201;
mg_http_free_proto_data_file(&pd->file);
if ((rc = mg_create_itermediate_directories(path)) == 0) {
mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code);
} else if (rc == -1) {
mg_http_send_error(nc, 500, NULL);
} else if (cl_hdr == NULL) {
mg_http_send_error(nc, 411, NULL);
} else if ((pd->file.fp = fopen(path, "w+b")) == NULL) {
mg_http_send_error(nc, 500, NULL);
} else {
const struct mg_str *range_hdr = mg_get_http_header(hm, "Content-Range");
int64_t r1 = 0, r2 = 0;
pd->file.type = DATA_PUT;
mg_set_close_on_exec(fileno(pd->file.fp));
pd->file.cl = to64(cl_hdr->p);
if (range_hdr != NULL &&
mg_http_parse_range_header(range_hdr, &r1, &r2) > 0) {
status_code = 206;
fseeko(pd->file.fp, r1, SEEK_SET);
pd->file.cl = r2 > r1 ? r2 - r1 + 1 : pd->file.cl - r1;
}
mg_printf(nc, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n", status_code);
/* Remove HTTP request from the mbuf, leave only payload */
mbuf_remove(&nc->recv_mbuf, hm->message.len - hm->body.len);
mg_http_transfer_file_data(nc);
}
}
#endif /* !MG_DISABLE_HTTP && MG_ENABLE_HTTP_WEBDAV */
#ifdef MG_MODULE_LINES
#line 1 "mongoose/src/util.c" #line 1 "mongoose/src/util.c"
#endif #endif
/* /*
...@@ -9772,7 +9800,7 @@ int mg_set_protocol_coap(struct mg_connection *nc) { ...@@ -9772,7 +9800,7 @@ int mg_set_protocol_coap(struct mg_connection *nc) {
return 0; return 0;
} }
#endif /* MG_DISABLE_COAP */ #endif /* MG_ENABLE_COAP */
#ifdef MG_MODULE_LINES #ifdef MG_MODULE_LINES
#line 1 "common/platforms/cc3200/cc3200_libc.c" #line 1 "common/platforms/cc3200/cc3200_libc.c"
#endif #endif
......
...@@ -1215,10 +1215,6 @@ const char *c_strnstr(const char *s, const char *find, size_t slen); ...@@ -1215,10 +1215,6 @@ const char *c_strnstr(const char *s, const char *find, size_t slen);
#define MG_DISABLE_HTTP_WEBSOCKET 0 #define MG_DISABLE_HTTP_WEBSOCKET 0
#endif #endif
#ifndef MG_DISABLE_DAV
#define MG_DISABLE_DAV 0
#endif
#ifndef MG_DISABLE_PFS #ifndef MG_DISABLE_PFS
#define MG_DISABLE_PFS 0 #define MG_DISABLE_PFS 0
#endif #endif
...@@ -1275,6 +1271,10 @@ const char *c_strnstr(const char *s, const char *find, size_t slen); ...@@ -1275,6 +1271,10 @@ const char *c_strnstr(const char *s, const char *find, size_t slen);
#define MG_ENABLE_HTTP_STREAMING_MULTIPART 0 #define MG_ENABLE_HTTP_STREAMING_MULTIPART 0
#endif #endif
#ifndef MG_ENABLE_HTTP_WEBDAV
#define MG_ENABLE_HTTP_WEBDAV 0
#endif
#ifndef MG_ENABLE_IPV6 #ifndef MG_ENABLE_IPV6
#define MG_ENABLE_IPV6 0 #define MG_ENABLE_IPV6 0
#endif #endif
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment