diff --git a/build/Makefile b/build/Makefile
index 79c41094..7cfc30ba 100644
--- a/build/Makefile
+++ b/build/Makefile
@@ -30,7 +30,7 @@ SOURCES = src/internal.h src/util.c src/string.c src/parse_date.c \
src/options.c src/crypto.c src/auth.c src/win32.c src/unix.c \
src/mg_printf.c src/ssl.c src/http_client.c src/mime.c \
src/directory.c src/log.c src/parse_http.c src/io.c src/cgi.c \
- src/upload.c src/websocket.c src/mongoose.c src/lua.c
+ src/upload.c src/websocket.c src/webdav.c src/mongoose.c src/lua.c
TINY_SOURCES = ../mongoose.c main.c
LUA_SOURCES = $(TINY_SOURCES) lua_5.2.1.c
diff --git a/build/src/mongoose.c b/build/src/mongoose.c
index d8d4f6c2..033b5e7a 100644
--- a/build/src/mongoose.c
+++ b/build/src/mongoose.c
@@ -190,10 +190,6 @@ static int convert_uri_to_file_name(struct mg_connection *conn, char *buf,
return 0;
}
-static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
- strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
-}
-
static void construct_etag(char *buf, size_t buf_len,
const struct file *filep) {
snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
@@ -587,66 +583,6 @@ static void handle_options_request(struct mg_connection *conn) {
mg_write(conn, reply, sizeof(reply) - 1);
}
-// Writes PROPFIND properties for a collection element
-static void print_props(struct mg_connection *conn, const char* uri,
- struct file *filep) {
- char mtime[64];
- gmt_time_string(mtime, sizeof(mtime), &filep->modification_time);
- conn->num_bytes_sent += mg_printf(conn,
- ""
- "%s"
- ""
- ""
- "%s"
- "%" INT64_FMT ""
- "%s"
- ""
- "HTTP/1.1 200 OK"
- ""
- "\n",
- uri,
- filep->is_directory ? "" : "",
- filep->size,
- mtime);
-}
-
-static void print_dav_dir_entry(struct de *de, void *data) {
- char href[PATH_MAX];
- char href_encoded[PATH_MAX];
- struct mg_connection *conn = (struct mg_connection *) data;
- mg_snprintf(href, sizeof(href), "%s%s",
- conn->request_info.uri, de->file_name);
- mg_url_encode(href, href_encoded, PATH_MAX-1);
- print_props(conn, href_encoded, &de->file);
-}
-
-static void handle_propfind(struct mg_connection *conn, const char *path,
- struct file *filep) {
- const char *depth = mg_get_header(conn, "Depth");
-
- conn->must_close = 1;
- conn->status_code = 207;
- mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
- "Connection: close\r\n"
- "Content-Type: text/xml; charset=utf-8\r\n\r\n");
-
- conn->num_bytes_sent += mg_printf(conn,
- ""
- "\n");
-
- // Print properties for the requested resource itself
- print_props(conn, conn->request_info.uri, filep);
-
- // If it is a directory, print directory entries too if Depth is not 0
- if (filep->is_directory &&
- !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") &&
- (depth == NULL || strcmp(depth, "0") != 0)) {
- scan_directory(conn, path, conn, &print_dav_dir_entry);
- }
-
- conn->num_bytes_sent += mg_printf(conn, "%s\n", "");
-}
-
static int isbyte(int n) {
return n >= 0 && n <= 255;
}
diff --git a/build/src/util.c b/build/src/util.c
index d1144c69..89446217 100644
--- a/build/src/util.c
+++ b/build/src/util.c
@@ -24,3 +24,7 @@ static void sockaddr_to_string(char *buf, size_t len,
inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
#endif
}
+
+static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
+ strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
+}
diff --git a/build/src/webdav.c b/build/src/webdav.c
new file mode 100644
index 00000000..83b724fe
--- /dev/null
+++ b/build/src/webdav.c
@@ -0,0 +1,61 @@
+#include "internal.h"
+
+// Writes PROPFIND properties for a collection element
+static void print_props(struct mg_connection *conn, const char* uri,
+ struct file *filep) {
+ char mtime[64];
+ gmt_time_string(mtime, sizeof(mtime), &filep->modification_time);
+ conn->num_bytes_sent += mg_printf(conn,
+ ""
+ "%s"
+ ""
+ ""
+ "%s"
+ "%" INT64_FMT ""
+ "%s"
+ ""
+ "HTTP/1.1 200 OK"
+ ""
+ "\n",
+ uri,
+ filep->is_directory ? "" : "",
+ filep->size,
+ mtime);
+}
+
+static void print_dav_dir_entry(struct de *de, void *data) {
+ char href[PATH_MAX];
+ char href_encoded[PATH_MAX];
+ struct mg_connection *conn = (struct mg_connection *) data;
+ mg_snprintf(href, sizeof(href), "%s%s",
+ conn->request_info.uri, de->file_name);
+ mg_url_encode(href, href_encoded, PATH_MAX-1);
+ print_props(conn, href_encoded, &de->file);
+}
+
+static void handle_propfind(struct mg_connection *conn, const char *path,
+ struct file *filep) {
+ const char *depth = mg_get_header(conn, "Depth");
+
+ conn->must_close = 1;
+ conn->status_code = 207;
+ mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
+ "Connection: close\r\n"
+ "Content-Type: text/xml; charset=utf-8\r\n\r\n");
+
+ conn->num_bytes_sent += mg_printf(conn,
+ ""
+ "\n");
+
+ // Print properties for the requested resource itself
+ print_props(conn, conn->request_info.uri, filep);
+
+ // If it is a directory, print directory entries too if Depth is not 0
+ if (filep->is_directory &&
+ !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") &&
+ (depth == NULL || strcmp(depth, "0") != 0)) {
+ scan_directory(conn, path, conn, &print_dav_dir_entry);
+ }
+
+ conn->num_bytes_sent += mg_printf(conn, "%s\n", "");
+}
diff --git a/build/src/websocket.c b/build/src/websocket.c
new file mode 100644
index 00000000..393752e8
--- /dev/null
+++ b/build/src/websocket.c
@@ -0,0 +1,309 @@
+#include "internal.h"
+
+#if defined(USE_WEBSOCKET)
+
+// START OF SHA-1 code
+// Copyright(c) By Steve Reid
+#define SHA1HANDSOFF
+#if defined(__sun)
+#include "solarisfixes.h"
+#endif
+
+union char64long16 { unsigned char c[64]; uint32_t l[16]; };
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+static uint32_t blk0(union char64long16 *block, int i) {
+ // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN
+ if (!is_big_endian()) {
+ block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
+ (rol(block->l[i], 8) & 0x00FF00FF);
+ }
+ return block->l[i];
+}
+
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+typedef struct {
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) {
+ uint32_t a, b, c, d, e;
+ union char64long16 block[1];
+
+ memcpy(block, buffer, 64);
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ a = b = c = d = e = 0;
+ memset(block, '\0', sizeof(block));
+}
+
+static void SHA1Init(SHA1_CTX* context) {
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+static void SHA1Update(SHA1_CTX* context, const unsigned char* data,
+ uint32_t len) {
+ uint32_t i, j;
+
+ j = context->count[0];
+ if ((context->count[0] += len << 3) < j)
+ context->count[1]++;
+ context->count[1] += (len>>29);
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) {
+ unsigned i;
+ unsigned char finalcount[8], c;
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ c = 0200;
+ SHA1Update(context, &c, 1);
+ while ((context->count[0] & 504) != 448) {
+ c = 0000;
+ SHA1Update(context, &c, 1);
+ }
+ SHA1Update(context, finalcount, 8);
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ memset(context, '\0', sizeof(*context));
+ memset(&finalcount, '\0', sizeof(finalcount));
+}
+// END OF SHA1 CODE
+
+static void base64_encode(const unsigned char *src, int src_len, char *dst) {
+ static const char *b64 =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int i, j, a, b, c;
+
+ for (i = j = 0; i < src_len; i += 3) {
+ a = src[i];
+ b = i + 1 >= src_len ? 0 : src[i + 1];
+ c = i + 2 >= src_len ? 0 : src[i + 2];
+
+ dst[j++] = b64[a >> 2];
+ dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
+ if (i + 1 < src_len) {
+ dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
+ }
+ if (i + 2 < src_len) {
+ dst[j++] = b64[c & 63];
+ }
+ }
+ while (j % 4 != 0) {
+ dst[j++] = '=';
+ }
+ dst[j++] = '\0';
+}
+
+void mg_websocket_handshake(struct mg_connection *conn) {
+ static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
+ SHA1_CTX sha_ctx;
+
+ mg_snprintf(buf, sizeof(buf), "%s%s",
+ mg_get_header(conn, "Sec-WebSocket-Key"), magic);
+ SHA1Init(&sha_ctx);
+ SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf));
+ SHA1Final((unsigned char *) sha, &sha_ctx);
+ base64_encode((unsigned char *) sha, sizeof(sha), b64_sha);
+ mg_printf(conn, "%s%s%s",
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
+}
+
+int mg_websocket_read(struct mg_connection *conn, int *bits, char **data) {
+ // Pointer to the beginning of the portion of the incoming websocket message
+ // queue. The original websocket upgrade request is never removed,
+ // so the queue begins after it.
+ unsigned char *buf = (unsigned char *) conn->buf + conn->request_len;
+ int n, stop = 0;
+ size_t i, len, mask_len, data_len, header_len, body_len;
+ char mask[4];
+
+ assert(conn->content_len == 0);
+
+ // Loop continuously, reading messages from the socket, invoking the callback,
+ // and waiting repeatedly until an error occurs.
+ while (!stop) {
+ header_len = 0;
+ // body_len is the length of the entire queue in bytes
+ // len is the length of the current message
+ // data_len is the length of the current message's data payload
+ // header_len is the length of the current message's header
+ if ((body_len = conn->data_len - conn->request_len) >= 2) {
+ len = buf[1] & 127;
+ mask_len = buf[1] & 128 ? 4 : 0;
+ if (len < 126 && body_len >= mask_len) {
+ data_len = len;
+ header_len = 2 + mask_len;
+ } else if (len == 126 && body_len >= 4 + mask_len) {
+ header_len = 4 + mask_len;
+ data_len = ((((int) buf[2]) << 8) + buf[3]);
+ } else if (body_len >= 10 + mask_len) {
+ header_len = 10 + mask_len;
+ data_len = (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) +
+ htonl(* (uint32_t *) &buf[6]);
+ }
+ }
+
+ // Data layout is as follows:
+ // conn->buf buf
+ // v v frame1 | frame2
+ // |---------------------|----------------|--------------|-------
+ // | |<--header_len-->|<--data_len-->|
+ // |<-conn->request_len->|<-----body_len----------->|
+ // |<-------------------conn->data_len------------->|
+
+ if (header_len > 0) {
+ // Allocate space to hold websocket payload
+ if ((*data = malloc(data_len)) == NULL) {
+ // Allocation failed, exit the loop and then close the connection
+ // TODO: notify user about the failure
+ data_len = 0;
+ break;
+ }
+
+ // Save mask and bits, otherwise it may be clobbered by memmove below
+ *bits = buf[0];
+ memcpy(mask, buf + header_len - mask_len, mask_len);
+
+ // Read frame payload into the allocated buffer.
+ assert(body_len >= header_len);
+ if (data_len + header_len > body_len) {
+ len = body_len - header_len;
+ memcpy(*data, buf + header_len, len);
+ // TODO: handle pull error
+ pull_all(NULL, conn, *data + len, data_len - len);
+ conn->data_len = conn->request_len;
+ } else {
+ len = data_len + header_len;
+ memcpy(*data, buf + header_len, data_len);
+ memmove(buf, buf + len, body_len - len);
+ conn->data_len -= len;
+ }
+
+ // Apply mask if necessary
+ if (mask_len > 0) {
+ for (i = 0; i < data_len; i++) {
+ (*data)[i] ^= mask[i % 4];
+ }
+ }
+
+ return data_len;
+ } else {
+ // Buffering websocket request
+ if ((n = pull(NULL, conn, conn->buf + conn->data_len,
+ conn->buf_size - conn->data_len)) <= 0) {
+ break;
+ }
+ conn->data_len += n;
+ }
+ }
+
+ return 0;
+}
+
+int mg_websocket_write(struct mg_connection* conn, int opcode,
+ const char *data, size_t data_len) {
+ unsigned char *copy;
+ size_t copy_len = 0;
+ int retval = -1;
+
+ if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) {
+ return -1;
+ }
+
+ copy[0] = 0x80 + (opcode & 0x0f);
+
+ // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2
+ if (data_len < 126) {
+ // Inline 7-bit length field
+ copy[1] = data_len;
+ memcpy(copy + 2, data, data_len);
+ copy_len = 2 + data_len;
+ } else if (data_len <= 0xFFFF) {
+ // 16-bit length field
+ copy[1] = 126;
+ * (uint16_t *) (copy + 2) = htons(data_len);
+ memcpy(copy + 4, data, data_len);
+ copy_len = 4 + data_len;
+ } else {
+ // 64-bit length field
+ copy[1] = 127;
+ * (uint32_t *) (copy + 2) = htonl((uint64_t) data_len >> 32);
+ * (uint32_t *) (copy + 6) = htonl(data_len & 0xffffffff);
+ memcpy(copy + 10, data, data_len);
+ copy_len = 10 + data_len;
+ }
+
+ // Not thread safe
+ if (copy_len > 0) {
+ retval = mg_write(conn, copy, copy_len);
+ }
+ free(copy);
+
+ return retval;
+}
+#endif // !USE_WEBSOCKET
+
diff --git a/mongoose.c b/mongoose.c
index 366fe420..ed49aa15 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -495,6 +495,10 @@ static void sockaddr_to_string(char *buf, size_t len,
#endif
}
+static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
+ strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
+}
+
static void mg_strlcpy(register char *dst, register const char *src, size_t n) {
for (; *src != '\0' && n > 1; n--) {
*dst++ = *src++;
@@ -3849,6 +3853,66 @@ int mg_websocket_write(struct mg_connection* conn, int opcode,
#endif // !USE_WEBSOCKET
+// Writes PROPFIND properties for a collection element
+static void print_props(struct mg_connection *conn, const char* uri,
+ struct file *filep) {
+ char mtime[64];
+ gmt_time_string(mtime, sizeof(mtime), &filep->modification_time);
+ conn->num_bytes_sent += mg_printf(conn,
+ ""
+ "%s"
+ ""
+ ""
+ "%s"
+ "%" INT64_FMT ""
+ "%s"
+ ""
+ "HTTP/1.1 200 OK"
+ ""
+ "\n",
+ uri,
+ filep->is_directory ? "" : "",
+ filep->size,
+ mtime);
+}
+
+static void print_dav_dir_entry(struct de *de, void *data) {
+ char href[PATH_MAX];
+ char href_encoded[PATH_MAX];
+ struct mg_connection *conn = (struct mg_connection *) data;
+ mg_snprintf(href, sizeof(href), "%s%s",
+ conn->request_info.uri, de->file_name);
+ mg_url_encode(href, href_encoded, PATH_MAX-1);
+ print_props(conn, href_encoded, &de->file);
+}
+
+static void handle_propfind(struct mg_connection *conn, const char *path,
+ struct file *filep) {
+ const char *depth = mg_get_header(conn, "Depth");
+
+ conn->must_close = 1;
+ conn->status_code = 207;
+ mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
+ "Connection: close\r\n"
+ "Content-Type: text/xml; charset=utf-8\r\n\r\n");
+
+ conn->num_bytes_sent += mg_printf(conn,
+ ""
+ "\n");
+
+ // Print properties for the requested resource itself
+ print_props(conn, conn->request_info.uri, filep);
+
+ // If it is a directory, print directory entries too if Depth is not 0
+ if (filep->is_directory &&
+ !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") &&
+ (depth == NULL || strcmp(depth, "0") != 0)) {
+ scan_directory(conn, path, conn, &print_dav_dir_entry);
+ }
+
+ conn->num_bytes_sent += mg_printf(conn, "%s\n", "");
+}
+
static int call_user(int type, struct mg_connection *conn, void *p) {
if (conn != NULL && conn->ctx != NULL) {
conn->event.user_data = conn->ctx->user_data;
@@ -4039,10 +4103,6 @@ static int convert_uri_to_file_name(struct mg_connection *conn, char *buf,
return 0;
}
-static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
- strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
-}
-
static void construct_etag(char *buf, size_t buf_len,
const struct file *filep) {
snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
@@ -4436,66 +4496,6 @@ static void handle_options_request(struct mg_connection *conn) {
mg_write(conn, reply, sizeof(reply) - 1);
}
-// Writes PROPFIND properties for a collection element
-static void print_props(struct mg_connection *conn, const char* uri,
- struct file *filep) {
- char mtime[64];
- gmt_time_string(mtime, sizeof(mtime), &filep->modification_time);
- conn->num_bytes_sent += mg_printf(conn,
- ""
- "%s"
- ""
- ""
- "%s"
- "%" INT64_FMT ""
- "%s"
- ""
- "HTTP/1.1 200 OK"
- ""
- "\n",
- uri,
- filep->is_directory ? "" : "",
- filep->size,
- mtime);
-}
-
-static void print_dav_dir_entry(struct de *de, void *data) {
- char href[PATH_MAX];
- char href_encoded[PATH_MAX];
- struct mg_connection *conn = (struct mg_connection *) data;
- mg_snprintf(href, sizeof(href), "%s%s",
- conn->request_info.uri, de->file_name);
- mg_url_encode(href, href_encoded, PATH_MAX-1);
- print_props(conn, href_encoded, &de->file);
-}
-
-static void handle_propfind(struct mg_connection *conn, const char *path,
- struct file *filep) {
- const char *depth = mg_get_header(conn, "Depth");
-
- conn->must_close = 1;
- conn->status_code = 207;
- mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
- "Connection: close\r\n"
- "Content-Type: text/xml; charset=utf-8\r\n\r\n");
-
- conn->num_bytes_sent += mg_printf(conn,
- ""
- "\n");
-
- // Print properties for the requested resource itself
- print_props(conn, conn->request_info.uri, filep);
-
- // If it is a directory, print directory entries too if Depth is not 0
- if (filep->is_directory &&
- !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") &&
- (depth == NULL || strcmp(depth, "0") != 0)) {
- scan_directory(conn, path, conn, &print_dav_dir_entry);
- }
-
- conn->num_bytes_sent += mg_printf(conn, "%s\n", "");
-}
-
static int isbyte(int n) {
return n >= 0 && n <= 255;
}