Newer
Older
"Content-Length: %" INT64_FMT "\r\n"
"Connection: %s\r\n"
"Accept-Ranges: bytes\r\n"
"%s%s\r\n",
conn->mg_conn.status_code, msg, date, lm, etag,
(int) mime_vec.len, mime_vec.ptr, conn->cl,
suggest_connection_header(&conn->mg_conn),
range, MONGOOSE_USE_EXTRA_HTTP_HEADERS);
if (!strcmp(conn->mg_conn.request_method, "HEAD")) {
conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
close(conn->endpoint.fd);
conn->endpoint_type = EP_NONE;
}
}
#endif // MONGOOSE_NO_FILESYSTEM
static void call_request_handler_if_data_is_buffered(struct connection *conn) {
struct mg_connection *c = &conn->mg_conn;
#ifndef MONGOOSE_NO_WEBSOCKET
if (conn->mg_conn.is_websocket) {
do { } while (deliver_websocket_frame(conn));
} else
if ((size_t) loc->len >= c->content_len &&
open_local_endpoint(conn, 1);
#if !defined(MONGOOSE_NO_DIRECTORY_LISTING) || !defined(MONGOOSE_NO_DAV)
#ifdef _WIN32
struct dirent {
char d_name[MAX_PATH_SIZE];
};
typedef struct DIR {
HANDLE handle;
WIN32_FIND_DATAW info;
struct dirent result;
} DIR;
// Implementation of POSIX opendir/closedir/readdir for Windows.
static DIR *opendir(const char *name) {
DIR *dir = NULL;
wchar_t wpath[MAX_PATH_SIZE];
DWORD attrs;
if (name == NULL) {
SetLastError(ERROR_BAD_ARGUMENTS);
} else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
} else {
to_wchar(name, wpath, ARRAY_SIZE(wpath));
attrs = GetFileAttributesW(wpath);
if (attrs != 0xFFFFFFFF &&
((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
(void) wcscat(wpath, L"\\*");
dir->handle = FindFirstFileW(wpath, &dir->info);
dir->result.d_name[0] = '\0';
} else {
free(dir);
dir = NULL;
}
static int closedir(DIR *dir) {
int result = 0;
if (dir != NULL) {
if (dir->handle != INVALID_HANDLE_VALUE)
result = FindClose(dir->handle) ? 0 : -1;
free(dir);
} else {
result = -1;
SetLastError(ERROR_BAD_ARGUMENTS);
static struct dirent *readdir(DIR *dir) {
struct dirent *result = 0;
if (dir) {
if (dir->handle != INVALID_HANDLE_VALUE) {
result = &dir->result;
(void) WideCharToMultiByte(CP_UTF8, 0,
dir->info.cFileName, -1, result->d_name,
sizeof(result->d_name), NULL, NULL);
if (!FindNextFileW(dir->handle, &dir->info)) {
(void) FindClose(dir->handle);
dir->handle = INVALID_HANDLE_VALUE;
}
} else {
SetLastError(ERROR_FILE_NOT_FOUND);
} else {
SetLastError(ERROR_BAD_ARGUMENTS);
return result;
}
#endif // _WIN32 POSIX opendir/closedir/readdir implementation
static int scan_directory(struct connection *conn, const char *dir,
struct dir_entry **arr) {
char path[MAX_PATH_SIZE];
struct dir_entry *p;
struct dirent *dp;
int arr_size = 0, arr_ind = 0, inc = 100;
if ((dirp = (opendir(dir))) == NULL) return 0;
while ((dp = readdir(dirp)) != NULL) {
// Do not show current dir and hidden files
if (!strcmp(dp->d_name, ".") ||
!strcmp(dp->d_name, "..") ||
must_hide_file(conn, dp->d_name)) {
continue;
mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
realloc(*arr, (inc + arr_size) * sizeof(**arr))) != NULL) {
// Memset new chunk to zero, otherwize st_mtime will have garbage which
// can make strftime() segfault, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset(p + arr_size, 0, sizeof(**arr) * inc);
if (arr_ind < arr_size) {
(*arr)[arr_ind].conn = conn;
(*arr)[arr_ind].file_name = strdup(dp->d_name);
stat(path, &(*arr)[arr_ind].st);
arr_ind++;
}
Sergey Lyubka
committed
int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len) {
static const char *dont_escape = "._-$,;~()";
static const char *hex = "0123456789abcdef";
Sergey Lyubka
committed
size_t i = 0, j = 0;
for (i = j = 0; dst_len > 0 && i < s_len && j < dst_len - 1; i++, j++) {
if (isalnum(* (const unsigned char *) (src + i)) ||
strchr(dont_escape, * (const unsigned char *) (src + i)) != NULL) {
dst[j] = src[i];
} else if (j + 3 < dst_len) {
dst[j] = '%';
dst[j + 1] = hex[(* (const unsigned char *) (src + i)) >> 4];
dst[j + 2] = hex[(* (const unsigned char *) (src + i)) & 0xf];
j += 2;
Sergey Lyubka
committed
dst[j] = '\0';
return j;
#endif // !NO_DIRECTORY_LISTING || !MONGOOSE_NO_DAV
#ifndef MONGOOSE_NO_DIRECTORY_LISTING
static void print_dir_entry(const struct dir_entry *de) {
const char *slash = is_dir ? "/" : "";
if (is_dir) {
mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]");
} else {
// We use (signed) cast below because MSVC 6 compiler cannot
// convert unsigned __int64 to double.
if (fsize < 1024) {
mg_snprintf(size, sizeof(size), "%d", (int) fsize);
} else if (fsize < 0x100000) {
mg_snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0);
} else if (fsize < 0x40000000) {
mg_snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576);
} else {
mg_snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824);
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.st_mtime));
Sergey Lyubka
committed
mg_url_encode(de->file_name, strlen(de->file_name), href, sizeof(href));
"<td> %s</td><td> %s</td></tr>\n",
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
// Sort directory entries by size, or name, or modification time.
// On windows, __cdecl specification is needed in case if project is built
// with __stdcall convention. qsort always requires __cdels callback.
static int __cdecl compare_dir_entries(const void *p1, const void *p2) {
const struct dir_entry *a = (const struct dir_entry *) p1,
*b = (const struct dir_entry *) p2;
const char *qs = a->conn->mg_conn.query_string ?
a->conn->mg_conn.query_string : "na";
int cmp_result = 0;
if (S_ISDIR(a->st.st_mode) && !S_ISDIR(b->st.st_mode)) {
return -1; // Always put directories on top
} else if (!S_ISDIR(a->st.st_mode) && S_ISDIR(b->st.st_mode)) {
return 1; // Always put directories on top
} else if (*qs == 'n') {
cmp_result = strcmp(a->file_name, b->file_name);
} else if (*qs == 's') {
cmp_result = a->st.st_size == b->st.st_size ? 0 :
a->st.st_size > b->st.st_size ? 1 : -1;
} else if (*qs == 'd') {
cmp_result = a->st.st_mtime == b->st.st_mtime ? 0 :
a->st.st_mtime > b->st.st_mtime ? 1 : -1;
return qs[1] == 'd' ? -cmp_result : cmp_result;
static void send_directory_listing(struct connection *conn, const char *dir) {
struct dir_entry *arr = NULL;
int i, num_entries, sort_direction = conn->mg_conn.query_string != NULL &&
conn->mg_conn.query_string[1] == 'd' ? 'a' : 'd';
mg_send_header(&conn->mg_conn, "Transfer-Encoding", "chunked");
mg_send_header(&conn->mg_conn, "Content-Type", "text/html; charset=utf-8");
"<html><head><title>Index of %s</title>"
"<style>th {text-align: left;}</style></head>"
"<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
"<tr><th><a href=\"?n%c\">Name</a></th>"
"<th><a href=\"?d%c\">Modified</a></th>"
"<th><a href=\"?s%c\">Size</a></th></tr>"
"<tr><td colspan=\"3\"><hr></td></tr>",
conn->mg_conn.uri, conn->mg_conn.uri,
sort_direction, sort_direction, sort_direction);
num_entries = scan_directory(conn, dir, &arr);
qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries);
for (i = 0; i < num_entries; i++) {
print_dir_entry(&arr[i]);
free(arr[i].file_name);
}
free(arr);
write_terminating_chunk(conn);
close_local_endpoint(conn);
}
#endif // MONGOOSE_NO_DIRECTORY_LISTING
static void print_props(struct connection *conn, const char *uri,
file_stat_t *stp) {
gmt_time_string(mtime, sizeof(mtime), &stp->st_mtime);
"<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",
uri, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "",
(int64_t) stp->st_size, mtime);
static void handle_propfind(struct connection *conn, const char *path,
file_stat_t *stp, int exists) {
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>";
const char *depth = mg_get_header(&conn->mg_conn, "Depth"),
*list_dir = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
// Print properties for the requested resource itself
if (!exists) {
conn->mg_conn.status_code = 404;
mg_printf(&conn->mg_conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n");
} else if (S_ISDIR(stp->st_mode) && mg_strcasecmp(list_dir, "yes") != 0) {
conn->mg_conn.status_code = 403;
mg_printf(&conn->mg_conn, "%s",
"HTTP/1.1 403 Directory Listing Denied\r\n\r\n");
ns_send(conn->ns_conn, header, sizeof(header) - 1);
print_props(conn, conn->mg_conn.uri, stp);
if (S_ISDIR(stp->st_mode) &&
(depth == NULL || strcmp(depth, "0") != 0)) {
struct dir_entry *arr = NULL;
int i, num_entries = scan_directory(conn, path, &arr);
for (i = 0; i < num_entries; i++) {
char buf[MAX_PATH_SIZE * 3];
struct dir_entry *de = &arr[i];
Sergey Lyubka
committed
mg_url_encode(de->file_name, strlen(de->file_name), buf, sizeof(buf));
print_props(conn, buf, &de->st);
}
ns_send(conn->ns_conn, footer, sizeof(footer) - 1);
static void handle_mkcol(struct connection *conn, const char *path) {
int status_code = 500;
if (conn->mg_conn.content_len > 0) {
status_code = 415;
} else if (!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;
}
send_http_error(conn, status_code, NULL);
static int remove_directory(const char *dir) {
char path[MAX_PATH_SIZE];
struct dirent *dp;
file_stat_t st;
DIR *dirp;
if ((dirp = opendir(dir)) == NULL) return 0;
while ((dp = readdir(dirp)) != NULL) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
stat(path, &st);
if (S_ISDIR(st.st_mode)) {
remove_directory(path);
} else {
remove(path);
}
return 1;
}
static void handle_delete(struct connection *conn, const char *path) {
file_stat_t st;
send_http_error(conn, 404, NULL);
} else if (S_ISDIR(st.st_mode)) {
remove_directory(path);
send_http_error(conn, 204, NULL);
} else if (remove(path) == 0) {
send_http_error(conn, 204, NULL);
send_http_error(conn, 423, NULL);
// For a given PUT path, create all intermediate subdirectories
// for given path. Return 0 if the path itself is a directory,
// or -1 on error, 1 if OK.
static int put_dir(const char *path) {
char buf[MAX_PATH_SIZE];
const char *s, *p;
file_stat_t st;
// Create intermediate directories if they do not exist
for (s = p = path + 1; (p = strchr(s, '/')) != NULL; s = ++p) {
if (p - path >= (int) sizeof(buf)) return -1; // Buffer overflow
memcpy(buf, path, p - path);
buf[p - path] = '\0';
if (stat(buf, &st) != 0 && mkdir(buf, 0755) != 0) return -1;
if (p[1] == '\0') return 0; // Path is a directory itself
static void handle_put(struct connection *conn, const char *path) {
file_stat_t st;
const char *range, *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
conn->mg_conn.status_code = !stat(path, &st) ? 200 : 201;
mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\n\r\n",
conn->mg_conn.status_code);
close_local_endpoint(conn);
send_http_error(conn, 500, "put_dir: %s", strerror(errno));
send_http_error(conn, 411, NULL);
#ifdef _WIN32
//On Windows, open() is a macro with 2 params
} else if ((conn->endpoint.fd =
open(path, O_RDWR | O_CREAT | O_TRUNC)) < 0) {
#else
} else if ((conn->endpoint.fd =
open(path, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
send_http_error(conn, 500, "open(%s): %s", path, strerror(errno));
DBG(("PUT [%s] %zu", path, conn->ns_conn->recv_iobuf.len));
range = mg_get_header(&conn->mg_conn, "Content-Range");
conn->cl = to64(cl_hdr);
r1 = r2 = 0;
if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
conn->mg_conn.status_code = 206;
lseek(conn->endpoint.fd, r1, SEEK_SET);
conn->cl = r2 > r1 ? r2 - r1 + 1: conn->cl - r1;
mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n",
conn->mg_conn.status_code);
static void forward_put_data(struct connection *conn) {
size_t k = conn->cl < (int64_t) io->len ? conn->cl : io->len; // To write
int n = write(conn->endpoint.fd, io->buf, k); // Write them!
}
if (conn->cl <= 0) {
close_local_endpoint(conn);
#endif // MONGOOSE_NO_DAV
static void send_options(struct connection *conn) {
conn->mg_conn.status_code = 200;
mg_printf(&conn->mg_conn, "%s",
"HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, PUT, "
"DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n");
close_local_endpoint(conn);
#ifndef MONGOOSE_NO_AUTH
void mg_send_digest_auth_request(struct mg_connection *c) {
c->status_code = 401;
mg_printf(c,
"HTTP/1.1 401 Unauthorized\r\n"
"WWW-Authenticate: Digest qop=\"auth\", "
"realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
conn->server->config_options[AUTH_DOMAIN],
(unsigned long) time(NULL));
close_local_endpoint(conn);
}
// Use the global passwords file, if specified by auth_gpass option,
// or search for .htpasswd in the requested directory.
static FILE *open_auth_file(struct connection *conn, const char *path) {
char name[MAX_PATH_SIZE];
const char *p, *gpass = conn->server->config_options[GLOBAL_AUTH_FILE];
file_stat_t st;
FILE *fp = NULL;
if (gpass != NULL) {
// Use global passwords file
fp = fopen(gpass, "r");
} else if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
mg_snprintf(name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME);
fp = fopen(name, "r");
// Try to find .htpasswd in requested directory.
if ((p = strrchr(path, '/')) == NULL) p = path;
mg_snprintf(name, sizeof(name), "%.*s%c%s",
(int) (p - path), path, '/', PASSWORDS_FILE_NAME);
fp = fopen(name, "r");
#if !defined(HAVE_MD5) && !defined(MONGOOSE_NO_AUTH)
typedef struct MD5Context {
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
} MD5_CTX;
static void byteReverse(unsigned char *buf, unsigned longs) {
uint32_t t;
// Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
if (is_big_endian()) {
do {
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
* (uint32_t *) buf = t;
buf += 4;
} while (--longs);
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
// initialization constants.
static void MD5Init(MD5_CTX *ctx) {
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
register uint32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
uint32_t t;
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
ctx->bits[1]++;
ctx->bits[1] += len >> 29;
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += t;
len -= t;
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += 64;
len -= 64;
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
unsigned count;
unsigned char *p;
uint32_t *a;
count = (ctx->bits[0] >> 3) & 0x3F;
p = ctx->in + count;
*p++ = 0x80;
count = 64 - 1 - count;
if (count < 8) {
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
memset(ctx->in, 0, 56);
a = (uint32_t *)ctx->in;
a[14] = ctx->bits[0];
a[15] = ctx->bits[1];
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset((char *) ctx, 0, sizeof(*ctx));
// Stringify binary data. Output buffer must be twice as big as input,
// because each byte takes 2 bytes in string representation
static void bin2str(char *to, const unsigned char *p, size_t len) {
static const char *hex = "0123456789abcdef";
for (; len--; p++) {
*to++ = hex[p[0] >> 4];
*to++ = hex[p[0] & 0x0f];
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
char *mg_md5(char buf[33], ...) {
unsigned char hash[16];
const char *p;
va_list ap;
MD5_CTX ctx;
va_start(ap, buf);
while ((p = va_arg(ap, const char *)) != NULL) {
MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
MD5Final(hash, &ctx);
bin2str(buf, hash, sizeof(hash));
return buf;
// Check the user's password, return 1 if OK
static int check_password(const char *method, const char *ha1, const char *uri,
const char *nonce, const char *nc, const char *cnonce,
const char *qop, const char *response) {
char ha2[32 + 1], expected_response[32 + 1];
#if 0
// Check for authentication timeout
if ((unsigned long) time(NULL) - (unsigned long) to64(nonce) > 3600 * 2) {
mg_md5(ha2, method, ":", uri, NULL);
mg_md5(expected_response, ha1, ":", nonce, ":", nc,
":", cnonce, ":", qop, ":", ha2, NULL);
return mg_strcasecmp(response, expected_response) == 0 ? MG_TRUE : MG_FALSE;
// Authorize against the opened passwords file. Return 1 if authorized.
int mg_authorize_digest(struct mg_connection *c, FILE *fp) {
char line[256], f_user[256], ha1[256], f_domain[256], user[100], nonce[100],
uri[MAX_REQUEST_SIZE], cnonce[100], resp[100], qop[100], nc[100];
if (c == NULL || fp == NULL) return 0;
if ((hdr = mg_get_header(c, "Authorization")) == NULL ||
mg_strncasecmp(hdr, "Digest ", 7) != 0) return 0;
if (!mg_parse_header(hdr, "username", user, sizeof(user))) return 0;
if (!mg_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce))) return 0;
if (!mg_parse_header(hdr, "response", resp, sizeof(resp))) return 0;
if (!mg_parse_header(hdr, "uri", uri, sizeof(uri))) return 0;
if (!mg_parse_header(hdr, "qop", qop, sizeof(qop))) return 0;
if (!mg_parse_header(hdr, "nc", nc, sizeof(nc))) return 0;
if (!mg_parse_header(hdr, "nonce", nonce, sizeof(nonce))) return 0;
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) == 3 &&
!strcmp(user, f_user) &&
// NOTE(lsm): due to a bug in MSIE, we do not compare URIs
!strcmp(conn->server->config_options[AUTH_DOMAIN], f_domain))
return check_password(c->request_method, ha1, uri,
// Return 1 if request is authorised, 0 otherwise.
static int is_authorized(struct connection *conn, const char *path) {
FILE *fp;
int authorized = MG_TRUE;
if ((fp = open_auth_file(conn, path)) != NULL) {
authorized = mg_authorize_digest(&conn->mg_conn, fp);
static int is_authorized_for_dav(struct connection *conn) {
const char *auth_file = conn->server->config_options[DAV_AUTH_FILE];
const char *method = conn->mg_conn.request_method;
int authorized = MG_FALSE;
// If dav_auth_file is not set, allow non-authorized PROPFIND
if (method != NULL && !strcmp(method, "PROPFIND") && auth_file == NULL) {
authorized = MG_TRUE;
} else if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
authorized = mg_authorize_digest(&conn->mg_conn, fp);
static int is_dav_request(const struct connection *conn) {
const char *s = conn->mg_conn.request_method;
return !strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
!strcmp(s, "MKCOL") || !strcmp(s, "PROPFIND");
#endif // MONGOOSE_NO_AUTH
static int parse_header(const char *str, int str_len, const char *var_name,
char *buf, size_t buf_size) {
int ch = ' ', len = 0, n = strlen(var_name);
const char *p, *end = str + str_len, *s = NULL;
if (buf != NULL && buf_size > 0) buf[0] = '\0';
for (s = str; s != NULL && s + n < end; s++) {
if ((s == str || s[-1] == ' ' || s[-1] == ',') && s[n] == '=' &&
s += n + 1;
if (*s == '"' || *s == '\'') ch = *s++;
p = s;
while (p < end && p[0] != ch && p[0] != ',' && len < (int) buf_size) {
if (p[0] == '\\' && p[1] == ch) p++;
buf[len++] = *p++;
}
if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
len = 0;
} else {
if (len > 0 && s[len - 1] == ',') len--;
int mg_parse_header(const char *s, const char *var_name, char *buf,
return parse_header(s, s == NULL ? 0 : strlen(s), var_name, buf, buf_size);
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
#ifndef MONGOOSE_NO_SSI
static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
static void send_file_data(struct mg_connection *conn, FILE *fp) {
char buf[IOBUF_SIZE];
int n;
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
mg_write(conn, buf, n);
}
}
static void do_ssi_include(struct mg_connection *conn, const char *ssi,
char *tag, int include_level) {
char file_name[IOBUF_SIZE], path[MAX_PATH_SIZE], *p;
char **opts = (MG_CONN_2_CONN(conn))->server->config_options;
FILE *fp;
// sscanf() is safe here, since send_ssi_file() also uses buffer
// of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
// File name is relative to the webserver root
mg_snprintf(path, sizeof(path), "%s%c%s",
opts[DOCUMENT_ROOT], '/', file_name);
} else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) {
// File name is relative to the webserver working directory
// or it is absolute system path
mg_snprintf(path, sizeof(path), "%s", file_name);
} else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 ||
sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
// File name is relative to the currect document
mg_snprintf(path, sizeof(path), "%s", ssi);
if ((p = strrchr(path, '/')) != NULL) {
p[1] = '\0';
}
mg_snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s",
file_name);
} else {
mg_printf(conn, "Bad SSI #include: [%s]", tag);
return;
}
if ((fp = fopen(path, "rb")) == NULL) {
mg_printf(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
tag, path, strerror(errno));
} else {
ns_set_close_on_exec(fileno(fp));
if (mg_match_prefix(opts[SSI_PATTERN], strlen(opts[SSI_PATTERN]),
path) > 0) {
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
send_ssi_file(conn, path, fp, include_level + 1);
} else {
send_file_data(conn, fp);
}
fclose(fp);
}
}
#ifndef MONGOOSE_NO_POPEN
static void do_ssi_exec(struct mg_connection *conn, char *tag) {
char cmd[IOBUF_SIZE];
FILE *fp;
if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
mg_printf(conn, "Bad SSI #exec: [%s]", tag);
} else if ((fp = popen(cmd, "r")) == NULL) {
mg_printf(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(errno));
} else {
send_file_data(conn, fp);
pclose(fp);
}
}
#endif // !MONGOOSE_NO_POPEN
static void send_ssi_file(struct mg_connection *conn, const char *path,
FILE *fp, int include_level) {
char buf[IOBUF_SIZE];
int ch, offset, len, in_ssi_tag;
if (include_level > 10) {
mg_printf(conn, "SSI #include level is too deep (%s)", path);
return;
}
in_ssi_tag = len = offset = 0;
while ((ch = fgetc(fp)) != EOF) {
if (in_ssi_tag && ch == '>') {
in_ssi_tag = 0;
buf[len++] = (char) ch;
buf[len] = '\0';
assert(len <= (int) sizeof(buf));
if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
// Not an SSI tag, pass it
(void) mg_write(conn, buf, (size_t) len);
} else {
if (!memcmp(buf + 5, "include", 7)) {
do_ssi_include(conn, path, buf + 12, include_level);
#if !defined(MONGOOSE_NO_POPEN)
} else if (!memcmp(buf + 5, "exec", 4)) {
do_ssi_exec(conn, buf + 9);
#endif // !NO_POPEN
} else {
mg_printf(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
}
}
len = 0;
} else if (in_ssi_tag) {
if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
// Not an SSI tag
in_ssi_tag = 0;
} else if (len == (int) sizeof(buf) - 2) {
mg_printf(conn, "%s: SSI tag is too large", path);
len = 0;
}
buf[len++] = ch & 0xff;
} else if (ch == '<') {
in_ssi_tag = 1;
if (len > 0) {
mg_write(conn, buf, (size_t) len);
}
len = 0;
buf[len++] = ch & 0xff;
} else {
buf[len++] = ch & 0xff;
if (len == (int) sizeof(buf)) {
mg_write(conn, buf, (size_t) len);
len = 0;
}
}
}
// Send the rest of buffered data
if (len > 0) {
mg_write(conn, buf, (size_t) len);
}
}