Newer
Older
mg_write(conn, buf, strlen(buf));
}
static size_t deliver_websocket_frame(struct connection *conn) {
// Having buf unsigned char * is important, as it is used below in arithmetic
unsigned char *buf = (unsigned char *) conn->ns_conn->recv_iobuf.buf;
size_t i, len, buf_len = conn->ns_conn->recv_iobuf.len, frame_len = 0,
mask_len = 0, header_len = 0, data_len = 0, buffered = 0;
if (buf_len >= 2) {
len = buf[1] & 127;
mask_len = buf[1] & 128 ? 4 : 0;
if (len < 126 && buf_len >= mask_len) {
data_len = len;
header_len = 2 + mask_len;
} else if (len == 126 && buf_len >= 4 + mask_len) {
header_len = 4 + mask_len;
} else if (buf_len >= 10 + mask_len) {
header_len = 10 + mask_len;
data_len = (size_t) (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) +
htonl(* (uint32_t *) &buf[6]);
}
}
frame_len = header_len + data_len;
buffered = frame_len > 0 && frame_len <= buf_len;
if (buffered) {
conn->mg_conn.content_len = data_len;
conn->mg_conn.content = (char *) buf + header_len;
conn->mg_conn.wsbits = buf[0];
// Apply mask if necessary
if (mask_len > 0) {
for (i = 0; i < data_len; i++) {
buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4];
// Call the handler and remove frame from the iobuf
if (call_user(conn, MG_REQUEST) == MG_FALSE ||
(buf[0] & 0x0f) == WEBSOCKET_OPCODE_CONNECTION_CLOSE) {
conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
iobuf_remove(&conn->ns_conn->recv_iobuf, frame_len);
size_t mg_websocket_write(struct mg_connection *conn, int opcode,
unsigned char mem[4192], *copy = mem;
/* Check overflow */
if (data_len > ~(size_t)0 - (size_t)10) {
return 0;
}
if (data_len + 10 > sizeof(mem) &&
(copy = (unsigned char *) NS_MALLOC(data_len + 10)) == NULL) {
return 0;
}
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) = (uint16_t) htons((uint16_t) data_len);
memcpy(copy + 4, data, data_len);
copy_len = 4 + data_len;
} else {
// 64-bit length field
const uint32_t hi = htonl((uint32_t) ((uint64_t) data_len >> 32));
const uint32_t lo = htonl(data_len & 0xffffffff);
memcpy(copy+2,&hi,sizeof(hi));
memcpy(copy+6,&lo,sizeof(lo));
memcpy(copy + 10, data, data_len);
copy_len = 10 + data_len;
}
mg_write(conn, copy, copy_len);
if (copy != mem) {
// If we send closing frame, schedule a connection to be closed after
// data is drained to the client.
if (opcode == WEBSOCKET_OPCODE_CONNECTION_CLOSE) {
MG_CONN_2_CONN(conn)->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
}
return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len;
size_t mg_websocket_printf(struct mg_connection *conn, int opcode,
const char *fmt, ...) {
char mem[4192], *buf = mem;
va_list ap;
int len;
va_start(ap, fmt);
if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
mg_websocket_write(conn, opcode, buf, len);
}
va_end(ap);
if (buf != mem && buf != NULL) {
return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len;
static void send_websocket_handshake_if_requested(struct mg_connection *conn) {
const char *ver = mg_get_header(conn, "Sec-WebSocket-Version"),
*key = mg_get_header(conn, "Sec-WebSocket-Key");
if (ver != NULL && key != NULL) {
conn->is_websocket = 1;
if (call_user(MG_CONN_2_CONN(conn), MG_WS_HANDSHAKE) == MG_FALSE) {
send_websocket_handshake(conn, key);
}
static void ping_idle_websocket_connection(struct connection *conn, time_t t) {
if (t - conn->ns_conn->last_io_time > MONGOOSE_USE_WEBSOCKET_PING_INTERVAL) {
mg_websocket_write(&conn->mg_conn, WEBSOCKET_OPCODE_PING, "", 0);
}
}
#else
#define ping_idle_websocket_connection(conn, t)
#endif // !MONGOOSE_NO_WEBSOCKET
static void write_terminating_chunk(struct connection *conn) {
mg_write(&conn->mg_conn, "0\r\n\r\n", 5);
}
static int call_request_handler(struct connection *conn) {
int result;
conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf;
Sergey Lyubka
committed
if ((result = call_user(conn, MG_REQUEST)) == MG_TRUE) {
if (conn->ns_conn->flags & MG_USING_CHUNKED_API) {
terminate_headers(&conn->mg_conn);
write_terminating_chunk(conn);
}
close_local_endpoint(conn);
const char *mg_get_mime_type(const char *path, const char *default_mime_type) {
const char *ext;
size_t i, path_len;
path_len = strlen(path);
for (i = 0; static_builtin_mime_types[i].extension != NULL; i++) {
ext = path + (path_len - static_builtin_mime_types[i].ext_len);
if (path_len > static_builtin_mime_types[i].ext_len &&
mg_strcasecmp(ext, static_builtin_mime_types[i].extension) == 0) {
return static_builtin_mime_types[i].mime_type;
}
}
return default_mime_type;
#ifndef MONGOOSE_NO_FILESYSTEM
// Convert month to the month number. Return -1 on error, or month number
static int get_month_index(const char *s) {
static const char *month_names[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
for (i = 0; i < (int) ARRAY_SIZE(month_names); i++)
if (!strcmp(s, month_names[i]))
static int num_leap_years(int year) {
return year / 4 - year / 100 + year / 400;
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
// Parse UTC date-time string, and return the corresponding time_t value.
static time_t parse_date_string(const char *datetime) {
static const unsigned short days_before_month[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
char month_str[32];
int second, minute, hour, day, month, year, leap_days, days;
time_t result = (time_t) 0;
if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
&day, month_str, &year, &hour, &minute, &second) == 6) ||
(sscanf(datetime, "%d %3s %d %d:%d:%d",
&day, month_str, &year, &hour, &minute, &second) == 6) ||
(sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
&day, month_str, &year, &hour, &minute, &second) == 6) ||
(sscanf(datetime, "%d-%3s-%d %d:%d:%d",
&day, month_str, &year, &hour, &minute, &second) == 6)) &&
year > 1970 &&
(month = get_month_index(month_str)) != -1) {
leap_days = num_leap_years(year) - num_leap_years(1970);
year -= 1970;
days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
}
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
// Look at the "path" extension and figure what mime type it has.
// Store mime type in the vector.
static void get_mime_type(const struct mg_server *server, const char *path,
struct vec *vec) {
struct vec ext_vec, mime_vec;
const char *list, *ext;
size_t path_len;
path_len = strlen(path);
// Scan user-defined mime types first, in case user wants to
// override default mime types.
list = server->config_options[EXTRA_MIME_TYPES];
while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
// ext now points to the path suffix
ext = path + path_len - ext_vec.len;
if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
*vec = mime_vec;
return;
}
}
vec->ptr = mg_get_mime_type(path, "text/plain");
vec->len = strlen(vec->ptr);
}
static const char *suggest_connection_header(const struct mg_connection *conn) {
return should_keep_alive(conn) ? "keep-alive" : "close";
}
static void construct_etag(char *buf, size_t buf_len, const file_stat_t *st) {
mg_snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
(unsigned long) st->st_mtime, (int64_t) st->st_size);
}
// Return True if we should reply 304 Not Modified.
static int is_not_modified(const struct connection *conn,
const file_stat_t *stp) {
char etag[64];
const char *ims = mg_get_header(&conn->mg_conn, "If-Modified-Since");
const char *inm = mg_get_header(&conn->mg_conn, "If-None-Match");
construct_etag(etag, sizeof(etag), stp);
return (inm != NULL && !mg_strcasecmp(etag, inm)) ||
(ims != NULL && stp->st_mtime <= parse_date_string(ims));
}
// For given directory path, substitute it to valid index file.
// Return 0 if index file has been found, -1 if not found.
// If the file is found, it's stats is returned in stp.
static int find_index_file(struct connection *conn, char *path,
size_t path_len, file_stat_t *stp) {
const char *list = conn->server->config_options[INDEX_FILES];
file_stat_t st;
struct vec filename_vec;
size_t n = strlen(path);
int found = 0;
// The 'path' given to us points to the directory. Remove all trailing
// directory separator characters from the end of the path, and
// then append single directory separator character.
while (n > 0 && path[n - 1] == '/') {
n--;
// 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 = next_option(list, &filename_vec, NULL)) != NULL) {
if (path_len <= n + 2) {
continue;
}
// Ignore too long entries that may overflow path buffer
if (filename_vec.len > (path_len - (n + 2)))
// Prepare full path to the index file
strncpy(path + n + 1, filename_vec.ptr, filename_vec.len);
path[n + 1 + filename_vec.len] = '\0';
//DBG(("[%s]", path));
// Does it exist?
if (!stat(path, &st)) {
// Yes it does, break the loop
*stp = st;
found = 1;
break;
}
// If no index file exists, restore directory path
if (!found) {
path[n] = '\0';
}
return found;
static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
}
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 open_file_endpoint(struct connection *conn, const char *path,
file_stat_t *st, const char *extra_headers) {
char date[64], lm[64], etag[64], range[64], headers[1000];
int64_t r1, r2;
struct vec mime_vec;
int n;
conn->endpoint_type = EP_FILE;
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
conn->mg_conn.status_code = 200;
get_mime_type(conn->server, path, &mime_vec);
conn->cl = st->st_size;
range[0] = '\0';
// If Range: header specified, act accordingly
r1 = r2 = 0;
hdr = mg_get_header(&conn->mg_conn, "Range");
if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
r1 >= 0 && r2 >= 0) {
conn->mg_conn.status_code = 206;
conn->cl = n == 2 ? (r2 > conn->cl ? conn->cl : r2) - r1 + 1: conn->cl - r1;
mg_snprintf(range, sizeof(range), "Content-Range: bytes "
"%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n",
r1, r1 + conn->cl - 1, (int64_t) st->st_size);
msg = "Partial Content";
lseek(conn->endpoint.fd, r1, SEEK_SET);
}
// Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
gmt_time_string(date, sizeof(date), &curtime);
t = st->st_mtime; // store in local variable for NDK compile
construct_etag(etag, sizeof(etag), st);
n = mg_snprintf(headers, sizeof(headers),
"HTTP/1.1 %d %s\r\n"
"Date: %s\r\n"
"Last-Modified: %s\r\n"
"Etag: %s\r\n"
"Content-Type: %.*s\r\n"
"Content-Length: %" INT64_FMT "\r\n"
"Connection: %s\r\n"
"Accept-Ranges: bytes\r\n"
"%s%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, extra_headers == NULL ? "" : extra_headers,
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;
}
}
void mg_send_file_data(struct mg_connection *c, int fd) {
struct connection *conn = MG_CONN_2_CONN(c);
conn->endpoint_type = EP_FILE;
conn->endpoint.fd = fd;
ns_set_close_on_exec(conn->endpoint.fd);
}
#endif // MONGOOSE_NO_FILESYSTEM
static void call_request_handler_if_data_is_buffered(struct connection *conn) {
#ifndef MONGOOSE_NO_WEBSOCKET
if (conn->mg_conn.is_websocket) {
do { } while (deliver_websocket_frame(conn));
} else
if (conn->num_bytes_recv >= (conn->cl + conn->request_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 *) NS_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 {
static int closedir(DIR *dir) {
int result = 0;
if (dir != NULL) {
if (dir->handle != INVALID_HANDLE_VALUE)
result = FindClose(dir->handle) ? 0 : -1;
} 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);
// Resize the array if necessary
NS_REALLOC(*arr, (inc + arr_size) * sizeof(**arr))) != NULL) {
// Memset new chunk to zero, otherwise 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++;
}
size_t 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 + 2 < dst_len - 1; i++, j++) {
Sergey Lyubka
committed
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);
t = de->st.st_mtime; // store in local variable for NDK compile
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&t));
Sergey Lyubka
committed
mg_url_encode(de->file_name, strlen(de->file_name), href, sizeof(href));
"<td> %s</td><td> %s</td></tr>\n",
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
// 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);
Andrea Guzzo
committed
if (arr) {
qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries);
for (i = 0; i < num_entries; i++) {
print_dir_entry(&arr[i]);
NS_FREE(arr[i].file_name);
}
NS_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) {
time_t t = stp->st_mtime; // store in local variable for NDK compile
gmt_time_string(mtime, sizeof(mtime), &t);
"<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");
#ifdef MONGOOSE_NO_DIRECTORY_LISTING
const char *list_dir = "no";
#else
const char *list_dir = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
#endif
// 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_FREE(de->file_name);
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);
} else if ((conn->endpoint.fd =
open(path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644)) < 0) {
send_http_error(conn, 500, "open(%s): %s", path, strerror(errno));
DBG(("PUT [%s] %lu", path, (unsigned long) 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 : (int64_t) io->len; // To write
size_t 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,
"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 *fp = NULL;
if (gpass != NULL) {
// Use global passwords file
fp = fopen(gpass, "r");
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)
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
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);