23 #if !defined(_CRT_SECURE_NO_WARNINGS)
24 #define _CRT_SECURE_NO_WARNINGS
28 #define _XOPEN_SOURCE 600
30 #define _LARGEFILE_SOURCE
31 #define __STDC_FORMAT_MACROS
32 #define __STDC_LIMIT_MACROS
35 #if defined (_MSC_VER)
37 #pragma warning (disable : 4306 )
39 #pragma warning (disable : 4127)
41 #pragma warning (disable : 4204)
46 #if defined(WIN32_LEAN_AND_MEAN) && (_MSC_VER <= 1400)
47 #undef WIN32_LEAN_AND_MEAN
50 #if defined USE_IPV6 && defined(_WIN32)
54 #if defined(__SYMBIAN32__)
57 #define PATH_MAX FILENAME_MAX
60 #ifndef IGNORE_UNUSED_RESULT
61 #define IGNORE_UNUSED_RESULT(a) (void)((a) && 1)
65 #include <sys/types.h>
82 #define MAX_WORKER_THREADS 1024
84 #if defined(_WIN32) && !defined(__SYMBIAN32__)
85 #if defined(_MSC_VER) && _MSC_VER <= 1400
87 #define _WIN32_WINNT 0x0400
92 #define PATH_MAX MAX_PATH
97 #define in_port_t u_short
110 #define errno GetLastError()
111 #define strerror(x) _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
114 #define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \
115 ((uint64_t)((uint32_t)(hi))) << 32))
116 #define RATE_DIFF 10000000
117 #define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de)
118 #define SYS2UNIX_TIME(lo, hi) \
119 (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)
124 #if defined(_MSC_VER) && _MSC_VER < 1300
126 #define STR(x) STRX(x)
127 #define __func__ __FILE__ ":" STR(__LINE__)
128 #define strtoull(x, y, z) (unsigned __int64) _atoi64(x)
129 #define strtoll(x, y, z) _atoi64(x)
131 #define __func__ __FUNCTION__
132 #define strtoull(x, y, z) _strtoui64(x, y, z)
133 #define strtoll(x, y, z) _strtoi64(x, y, z)
136 #define ERRNO GetLastError()
138 #define SSL_LIB "ssleay32.dll"
139 #define CRYPTO_LIB "libeay32.dll"
141 #if !defined(EWOULDBLOCK)
142 #define EWOULDBLOCK WSAEWOULDBLOCK
145 #define INT64_FMT "I64d"
147 #define WINCDECL __cdecl
149 #define snprintf _snprintf
150 #define vsnprintf _vsnprintf
151 #define mg_sleep(x) Sleep(x)
153 #define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
155 #define popen(x, y) _popen(x, y)
158 #define pclose(x) _pclose(x)
160 #define close(x) _close(x)
161 #define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
163 #define fseeko(x, y, z) _lseeki64(_fileno(x), (y), (z))
164 #define fdopen(x, y) _fdopen((x), (y))
165 #define write(x, y, z) _write((x), (y), (unsigned) z)
166 #define read(x, y, z) _read((x), (y), (unsigned) z)
167 #define flockfile(x) EnterCriticalSection(&global_log_file_lock)
168 #define funlockfile(x) LeaveCriticalSection(&global_log_file_lock)
169 #define sleep(x) Sleep((x) * 1000)
170 #define rmdir(x) _rmdir(x)
172 #if !defined(va_copy)
173 #define va_copy(x, y) x = y
177 #define fileno(x) _fileno(x)
180 typedef HANDLE pthread_mutex_t;
181 typedef DWORD pthread_key_t;
182 typedef HANDLE pthread_t;
184 CRITICAL_SECTION threadIdSec;
185 int waitingthreadcount;
186 pthread_t *waitingthreadhdls;
189 typedef DWORD clockid_t;
190 #define CLOCK_MONOTONIC (1)
191 #define CLOCK_REALTIME (2)
200 static int pthread_mutex_lock(pthread_mutex_t *);
201 static int pthread_mutex_unlock(pthread_mutex_t *);
202 static void to_unicode(
const char *path,
wchar_t *wbuf,
size_t wbuf_len);
204 static char *
mg_fgets(
char *buf,
size_t size,
struct file *filep,
char **p);
206 #if defined(HAVE_STDINT)
209 typedef unsigned int uint32_t;
210 typedef unsigned short uint16_t;
211 typedef unsigned __int64 uint64_t;
212 typedef __int64 int64_t;
213 #define INT64_MAX 9223372036854775807
223 WIN32_FIND_DATAW info;
224 struct dirent result;
227 #if !defined(USE_IPV6) && defined(_WIN32)
240 #pragma comment(lib, "Ws2_32.lib")
244 #include <sys/wait.h>
245 #include <sys/socket.h>
246 #include <sys/poll.h>
247 #include <netinet/in.h>
248 #include <arpa/inet.h>
249 #include <sys/time.h>
251 #include <inttypes.h>
257 #if !defined(NO_SSL_DL) && !defined(NO_SSL)
261 #if defined(__MACH__)
262 #define SSL_LIB "libssl.dylib"
263 #define CRYPTO_LIB "libcrypto.dylib"
265 #if !defined(SSL_LIB)
266 #define SSL_LIB "libssl.so"
268 #if !defined(CRYPTO_LIB)
269 #define CRYPTO_LIB "libcrypto.so"
275 #define closesocket(a) close(a)
276 #define mg_mkdir(x, y) mkdir(x, y)
277 #define mg_remove(x) remove(x)
278 #define mg_sleep(x) usleep((x) * 1000)
280 #define INVALID_SOCKET (-1)
281 #define INT64_FMT PRId64
289 #define PASSWORDS_FILE_NAME ".htpasswd"
290 #define CGI_ENVIRONMENT_SIZE 4096
291 #define MAX_CGI_ENVIR_VARS 64
292 #define MG_BUF_LEN 8192
293 #ifndef MAX_REQUEST_SIZE
294 #define MAX_REQUEST_SIZE 16384
296 #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
299 static CRITICAL_SECTION global_log_file_lock;
300 static DWORD pthread_self(
void)
302 return GetCurrentThreadId();
305 int pthread_key_create(pthread_key_t *key,
void (*_must_be_zero)(
void*) )
308 if ((key!=0) && (_must_be_zero ==
NULL)) {
310 return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
315 int pthread_key_delete(pthread_key_t key)
317 return TlsFree(key) ? 0 : 1;
320 int pthread_setspecific(pthread_key_t key,
void *
value)
322 return TlsSetValue(key, value) ? 0 : 1;
325 void *pthread_getspecific(pthread_key_t key)
327 return TlsGetValue(key);
331 #define MD5_STATIC static
336 #define DEBUG_TRACE(x)
339 #define DEBUG_TRACE(x) do { \
341 printf("*** %lu.%p.%s.%d: ", \
342 (unsigned long) time(NULL), (void *) pthread_self(), \
343 __func__, __LINE__); \
347 funlockfile(stdout); \
350 #define DEBUG_TRACE(x)
356 typedef int socklen_t;
358 #define _DARWIN_UNLIMITED_SELECT
360 #define IP_ADDR_STR_LEN 50
362 #if !defined(MSG_NOSIGNAL)
363 #define MSG_NOSIGNAL 0
366 #if !defined(SOMAXCONN)
367 #define SOMAXCONN 100
370 #if !defined(PATH_MAX)
371 #define PATH_MAX 4096
375 #if !defined(MGSQLEN)
379 static const char *http_500_error =
"Internal Server Error";
381 #if defined(NO_SSL_DL)
382 #include <openssl/ssl.h>
383 #include <openssl/err.h>
389 typedef struct ssl_st
SSL;
398 #define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
399 #define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
400 #define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr)
401 #define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr)
402 #define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr)
403 #define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5].ptr)
404 #define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)
405 #define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
406 #define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
407 #define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
408 #define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr)
409 #define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \
410 const char *, int)) ssl_sw[11].ptr)
411 #define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
412 const char *, int)) ssl_sw[12].ptr)
413 #define SSL_CTX_set_default_passwd_cb \
414 (* (void (*)(SSL_CTX *, mg_callback_t)) ssl_sw[13].ptr)
415 #define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
416 #define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
417 #define SSL_CTX_use_certificate_chain_file \
418 (* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr)
419 #define SSLv23_client_method (* (SSL_METHOD * (*)(void)) ssl_sw[17].ptr)
420 #define SSL_pending (* (int (*)(SSL *)) ssl_sw[18].ptr)
421 #define SSL_CTX_set_verify (* (void (*)(SSL_CTX *, int, int)) ssl_sw[19].ptr)
422 #define SSL_shutdown (* (int (*)(SSL *)) ssl_sw[20].ptr)
424 #define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr)
425 #define CRYPTO_set_locking_callback \
426 (* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr)
427 #define CRYPTO_set_id_callback \
428 (* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)
429 #define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr)
430 #define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr)
436 static struct ssl_func ssl_sw[] = {
438 {
"SSL_accept",
NULL},
439 {
"SSL_connect",
NULL},
442 {
"SSL_get_error",
NULL},
443 {
"SSL_set_fd",
NULL},
445 {
"SSL_CTX_new",
NULL},
446 {
"SSLv23_server_method",
NULL},
447 {
"SSL_library_init",
NULL},
448 {
"SSL_CTX_use_PrivateKey_file",
NULL},
449 {
"SSL_CTX_use_certificate_file",
NULL},
450 {
"SSL_CTX_set_default_passwd_cb",
NULL},
451 {
"SSL_CTX_free",
NULL},
452 {
"SSL_load_error_strings",
NULL},
453 {
"SSL_CTX_use_certificate_chain_file",
NULL},
454 {
"SSLv23_client_method",
NULL},
455 {
"SSL_pending",
NULL},
456 {
"SSL_CTX_set_verify",
NULL},
457 {
"SSL_shutdown",
NULL},
464 static struct ssl_func crypto_sw[] = {
465 {
"CRYPTO_num_locks",
NULL},
466 {
"CRYPTO_set_locking_callback",
NULL},
467 {
"CRYPTO_set_id_callback",
NULL},
468 {
"ERR_get_error",
NULL},
469 {
"ERR_error_string",
NULL},
475 static const char *month_names[] = {
476 "Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
477 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
484 struct sockaddr_in
sin;
485 #if defined(USE_IPV6)
486 struct sockaddr_in6 sin6;
498 time_t modification_time;
506 #define STRUCT_FILE_INITIALIZER {0, 0, 0, NULL, NULL, 0}
515 unsigned ssl_redir:1;
529 LUA_SCRIPT_EXTENSIONS, LUA_SERVER_PAGE_EXTENSIONS,
531 #if defined(USE_WEBSOCKET)
534 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
535 LUA_WEBSOCKET_EXTENSIONS,
541 static const char *config_options[] = {
542 "cgi_pattern",
"**.cgi$|**.pl$|**.php$",
543 "cgi_environment",
NULL,
544 "put_delete_auth_file",
NULL,
545 "cgi_interpreter",
NULL,
547 "authentication_domain",
"mydomain.com",
548 "ssi_pattern",
"**.shtml$|**.shtm$",
550 "access_log_file",
NULL,
551 "enable_directory_listing",
"yes",
552 "error_log_file",
NULL,
553 "global_auth_file",
NULL,
556 "index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,index.shtml,index.php",
558 "index.html,index.htm,index.cgi,index.shtml,index.php",
560 "enable_keep_alive",
"no",
561 "access_control_list",
NULL,
562 "extra_mime_types",
NULL,
563 "listening_ports",
"8080",
564 "document_root",
NULL,
565 "ssl_certificate",
NULL,
568 "url_rewrite_patterns",
NULL,
569 "hide_files_patterns",
NULL,
570 "request_timeout_ms",
"30000",
573 "lua_script_pattern",
"**.lua$",
574 "lua_server_page_pattern",
"**.lp$|**.lsp$",
576 #if defined(USE_WEBSOCKET)
577 "websocket_root",
NULL,
579 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
580 "lua_websocket_pattern",
"**.lua$",
586 struct mg_request_handler_info {
591 struct mg_request_handler_info *
next;
595 volatile int stop_flag;
596 void *ssllib_dll_handle;
597 void *cryptolib_dll_handle;
603 struct socket *listening_sockets;
604 in_port_t *listening_ports;
605 int num_listening_sockets;
607 volatile int num_threads;
608 pthread_mutex_t mutex;
612 volatile int sq_head;
613 volatile int sq_tail;
616 pthread_t masterthreadid;
617 int workerthreadcount;
618 pthread_t *workerthreadids;
621 struct mg_request_handler_info *request_handlers;
624 struct mg_connection {
626 struct mg_context *ctx;
629 struct socket client;
631 int64_t num_bytes_sent;
633 int64_t consumed_content;
643 time_t last_throttle_time;
644 int64_t last_throttle_bytes;
645 pthread_mutex_t mutex;
647 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
648 void * lua_websocket_state;
653 static int sTlsInit = 0;
655 struct mg_workerTLS {
657 #if defined(_WIN32) && !defined(__SYMBIAN32__)
658 HANDLE pthread_cond_helper_mutex;
664 struct mg_connection *conn;
669 #if defined(USE_WEBSOCKET)
670 static int is_websocket_request(
const struct mg_connection *conn);
682 if ((filep->membuf = conn->ctx->callbacks.open_file ==
NULL ?
NULL :
683 conn->ctx->callbacks.open_file(conn, path, &size)) !=
NULL) {
688 return filep->membuf !=
NULL;
693 return filep->membuf !=
NULL || filep->fp !=
NULL;
698 return conn ==
NULL ?
NULL : conn->ctx;
703 return ctx ==
NULL ?
NULL : ctx->user_data;
707 static int mg_fopen(
struct mg_connection *conn,
const char *path,
708 const char *mode,
struct file *filep)
714 MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode,
ARRAY_SIZE(wmode));
715 filep->fp = _wfopen(wbuf, wmode);
717 filep->fp = fopen(path, mode);
726 if (filep !=
NULL && filep->fp !=
NULL) {
735 for (i = 0; config_options[i * 2] !=
NULL; i++) {
736 if (strcmp(config_options[i * 2], name) == 0) {
748 }
else if (ctx->config[i] ==
NULL) {
751 return ctx->config[i];
755 size_t mg_get_ports(
const struct mg_context *ctx,
size_t size,
int* ports,
int* ssl)
758 for (i = 0; i < size && i < (
size_t)ctx->num_listening_sockets; i++)
760 ssl[i] = ctx->listening_sockets[i].is_ssl;
761 ports[i] = ctx->listening_ports[i];
767 const union usa *usa)
770 #if defined(USE_IPV6)
771 inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
772 (
void *) &usa->sin.sin_addr :
773 (
void *) &usa->sin6.sin6_addr, buf, len);
774 #elif defined(_WIN32)
776 strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
778 inet_ntop(usa->sa.sa_family, (
void *) &usa->sin.sin_addr, buf, len);
789 strftime(buf, buf_len,
"%a, %d %b %Y %H:%M:%S GMT", tm);
791 strncpy(buf,
"Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
792 buf[buf_len - 1] =
'\0';
797 void mg_cry(
struct mg_connection *conn,
const char *fmt, ...)
811 if (conn->ctx->callbacks.log_message ==
NULL ||
812 conn->ctx->callbacks.log_message(conn, buf) == 0) {
818 timestamp = time(
NULL);
821 fprintf(fp,
"[%010lu] [error] [client %s] ", (
unsigned long) timestamp,
824 if (conn->request_info.request_method !=
NULL) {
825 fprintf(fp,
"%s %s: ", conn->request_info.request_method,
826 conn->request_info.uri);
829 fprintf(fp,
"%s", buf);
839 static struct mg_connection *
fc(
struct mg_context *ctx)
841 static struct mg_connection fake_connection;
842 fake_connection.ctx = ctx;
843 return &fake_connection;
853 return &conn->request_info;
856 static void mg_strlcpy(
register char *dst,
register const char *src,
size_t n)
858 for (; *src !=
'\0' && n > 1; n--) {
866 return tolower(* (
const unsigned char *) s);
876 }
while (diff == 0 && s1[-1] !=
'\0' && --len > 0);
887 }
while (diff == 0 && s1[-1] !=
'\0');
908 static const char *
mg_strcasestr(
const char *big_str,
const char *small_str)
910 int i, big_len = (int)strlen(big_str), small_len = (int)strlen(small_str);
912 for (i = 0; i <= big_len - small_len; i++) {
925 static int mg_vsnprintf(
struct mg_connection *conn,
char *buf,
size_t buflen,
926 const char *fmt, va_list ap)
933 n = vsnprintf(buf, buflen, fmt, ap);
936 mg_cry(conn,
"vsnprintf error");
938 }
else if (n >= (
int) buflen) {
939 mg_cry(conn,
"truncating vsnprintf buffer: [%.*s]",
940 n > 200 ? 200 : n, buf);
941 n = (int) buflen - 1;
948 static int mg_snprintf(
struct mg_connection *conn,
char *buf,
size_t buflen,
952 static
int mg_snprintf(struct mg_connection *conn,
char *buf,
size_t buflen,
953 const
char *fmt, ...)
970 const char *whitespace,
char quotechar)
972 char *p, *begin_word, *end_word, *end_whitespace;
975 end_word = begin_word + strcspn(begin_word, delimiters);
978 if (end_word > begin_word) {
980 while (*p == quotechar) {
982 if (*end_word ==
'\0') {
986 size_t end_off = strcspn(end_word + 1, delimiters);
987 memmove (p, end_word, end_off + 1);
989 end_word += end_off + 1;
992 for (p++; p < end_word; p++) {
997 if (*end_word ==
'\0') {
1000 end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);
1002 for (p = end_word; p < end_whitespace; p++) {
1006 *buf = end_whitespace;
1014 static char *
skip(
char **buf,
const char *delimiters)
1016 return skip_quoted(buf, delimiters, delimiters, 0);
1035 return get_header(&conn->request_info, name);
1047 if (list ==
NULL || *list ==
'\0') {
1052 if ((list = strchr(val->ptr,
',')) !=
NULL) {
1054 val->len = list - val->ptr;
1058 list = val->ptr + strlen(val->ptr);
1059 val->len = list - val->ptr;
1062 if (eq_val !=
NULL) {
1066 eq_val->ptr = (
const char *) memchr(val->ptr,
'=', val->len);
1067 if (eq_val->ptr !=
NULL) {
1069 eq_val->len = val->ptr + val->len - eq_val->ptr;
1070 val->len = (eq_val->ptr - val->ptr) - 1;
1084 if ((or_str = (
const char *) memchr(pattern,
'|', pattern_len)) !=
NULL) {
1085 res =
match_prefix(pattern, (
int)(or_str - pattern), str);
1086 return res > 0 ? res :
1087 match_prefix(or_str + 1, (
int)((pattern + pattern_len) - (or_str + 1)), str);
1092 for (; i < pattern_len; i++, j++) {
1093 if (pattern[i] ==
'?' && str[j] !=
'\0') {
1095 }
else if (pattern[i] ==
'$') {
1096 return str[j] ==
'\0' ? j : -1;
1097 }
else if (pattern[i] ==
'*') {
1099 if (pattern[i] ==
'*') {
1101 len = (int) strlen(str + j);
1103 len = (int) strcspn(str + j,
"/");
1105 if (i == pattern_len) {
1109 res =
match_prefix(pattern + i, pattern_len - i, str + j + len);
1110 }
while (res == -1 && len-- > 0);
1111 return res == -1 ? -1 : j + res + len;
1124 const char *
http_version = conn->request_info.http_version;
1126 if (conn->must_close ||
1127 conn->status_code == 401 ||
1130 (header ==
NULL && http_version && strcmp(http_version,
"1.1"))) {
1147 const
char *reason, const
char *fmt, ...)
1153 time_t curtime = time(
NULL);
1155 conn->status_code =
status;
1156 if (conn->ctx->callbacks.http_error ==
NULL ||
1157 conn->ctx->callbacks.http_error(conn, status)) {
1163 if (status > 199 && status != 204 && status != 304) {
1164 len =
mg_snprintf(conn, buf,
sizeof(buf),
"Error %d: %s", status, reason);
1168 len +=
mg_vsnprintf(conn, buf + len,
sizeof(buf) - len, fmt, ap);
1174 "Content-Length: %d\r\n"
1176 "Connection: %s\r\n\r\n",
1177 status, reason, len, date,
1179 conn->num_bytes_sent +=
mg_printf(conn,
"%s", buf);
1183 #if defined(_WIN32) && !defined(__SYMBIAN32__)
1184 static int pthread_mutex_init(pthread_mutex_t *mutex,
void *unused)
1187 *mutex = CreateMutex(
NULL, FALSE,
NULL);
1188 return *mutex ==
NULL ? -1 : 0;
1191 static int pthread_mutex_destroy(pthread_mutex_t *mutex)
1193 return CloseHandle(*mutex) == 0 ? -1 : 0;
1196 static int pthread_mutex_lock(pthread_mutex_t *mutex)
1198 return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
1201 static int pthread_mutex_unlock(pthread_mutex_t *mutex)
1203 return ReleaseMutex(*mutex) == 0 ? -1 : 0;
1206 static int clock_gettime(clockid_t clk_id,
struct timespec *tp)
1212 static double perfcnt_per_sec = 0.0;
1215 if (clk_id == CLOCK_REALTIME) {
1216 GetSystemTimeAsFileTime(&ft);
1217 li.LowPart = ft.dwLowDateTime;
1218 li.HighPart = ft.dwHighDateTime;
1219 li.QuadPart -= 116444736000000000;
1220 tp->tv_sec = (time_t)(li.QuadPart / 10000000);
1221 tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
1223 }
else if (clk_id == CLOCK_MONOTONIC) {
1224 if (perfcnt_per_sec==0) {
1225 QueryPerformanceFrequency((LARGE_INTEGER *) &li);
1226 perfcnt_per_sec = 1.0 / li.QuadPart;
1228 if (perfcnt_per_sec!=0) {
1229 QueryPerformanceCounter((LARGE_INTEGER *) &li);
1230 d = li.QuadPart * perfcnt_per_sec;
1231 tp->tv_sec = (time_t)d;
1233 tp->tv_nsec = (long)(d*1.0E9);
1242 static int pthread_cond_init(
pthread_cond_t *cv,
const void *unused)
1245 InitializeCriticalSection(&cv->threadIdSec);
1246 cv->waitingthreadcount = 0;
1248 return (cv->waitingthreadhdls!=
NULL) ? 0 : -1;
1251 static int pthread_cond_timedwait(
pthread_cond_t *cv, pthread_mutex_t *mutex,
const struct timespec * abstime)
1253 struct mg_workerTLS * tls = (
struct mg_workerTLS *)TlsGetValue(sTlsKey);
1255 struct timespec tsnow;
1256 int64_t nsnow, nswaitabs, nswaitrel;
1259 EnterCriticalSection(&cv->threadIdSec);
1261 cv->waitingthreadhdls[cv->waitingthreadcount] = tls->pthread_cond_helper_mutex;
1262 cv->waitingthreadcount++;
1263 LeaveCriticalSection(&cv->threadIdSec);
1266 clock_gettime(CLOCK_REALTIME, &tsnow);
1267 nsnow = (((uint64_t)tsnow.tv_sec)<<32) + tsnow.tv_nsec;
1268 nswaitabs = (((uint64_t)abstime->tv_sec)<<32) + abstime->tv_nsec;
1269 nswaitrel = nswaitabs - nsnow;
1270 if (nswaitrel<0) nswaitrel=0;
1271 mswaitrel = (DWORD)(nswaitrel / 1000000);
1273 mswaitrel = INFINITE;
1276 pthread_mutex_unlock(mutex);
1277 ok = (WAIT_OBJECT_0 == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel));
1278 pthread_mutex_lock(mutex);
1283 static int pthread_cond_wait(
pthread_cond_t *cv, pthread_mutex_t *mutex)
1285 return pthread_cond_timedwait(cv, mutex,
NULL);
1294 EnterCriticalSection(&cv->threadIdSec);
1295 if (cv->waitingthreadcount) {
1296 wkup = cv->waitingthreadhdls[0];
1297 ok = SetEvent(wkup);
1299 for (i=1; i<cv->waitingthreadcount; i++) {
1300 cv->waitingthreadhdls[i-1] = cv->waitingthreadhdls[i];
1302 cv->waitingthreadcount--;
1306 LeaveCriticalSection(&cv->threadIdSec);
1313 EnterCriticalSection(&cv->threadIdSec);
1314 while (cv->waitingthreadcount) {
1315 pthread_cond_signal(cv);
1317 LeaveCriticalSection(&cv->threadIdSec);
1324 EnterCriticalSection(&cv->threadIdSec);
1325 assert(cv->waitingthreadcount==0);
1326 cv->waitingthreadhdls = 0;
1327 free(cv->waitingthreadhdls);
1328 LeaveCriticalSection(&cv->threadIdSec);
1329 DeleteCriticalSection(&cv->threadIdSec);
1335 static void change_slashes_to_backslashes(
char *path)
1339 for (i = 0; path[i] !=
'\0'; i++) {
1343 if (path[i] ==
'\\' && i > 0)
1344 while (path[i + 1] ==
'\\' || path[i + 1] ==
'/')
1345 (
void) memmove(path + i + 1,
1346 path + i + 2, strlen(path + i + 1));
1352 static void to_unicode(
const char *path,
wchar_t *wbuf,
size_t wbuf_len)
1357 change_slashes_to_backslashes(buf);
1361 memset(wbuf, 0, wbuf_len *
sizeof(
wchar_t));
1362 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (
int) wbuf_len);
1363 WideCharToMultiByte(CP_UTF8, 0, wbuf, (
int) wbuf_len, buf2,
sizeof(buf2),
1365 if (strcmp(buf, buf2) != 0) {
1370 #if defined(_WIN32_WCE)
1371 static time_t time(time_t *ptime)
1378 SystemTimeToFileTime(&st, &ft);
1379 t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
1381 if (ptime !=
NULL) {
1388 static struct tm *localtime(
const time_t *ptime,
struct tm *ptm)
1390 int64_t t = ((int64_t) *ptime) * RATE_DIFF + EPOCH_DIFF;
1393 TIME_ZONE_INFORMATION tzinfo;
1399 * (int64_t *) &ft = t;
1400 FileTimeToLocalFileTime(&ft, &lft);
1401 FileTimeToSystemTime(&lft, &st);
1402 ptm->tm_year = st.wYear - 1900;
1403 ptm->tm_mon = st.wMonth - 1;
1404 ptm->tm_wday = st.wDayOfWeek;
1405 ptm->tm_mday = st.wDay;
1406 ptm->tm_hour = st.wHour;
1407 ptm->tm_min = st.wMinute;
1408 ptm->tm_sec = st.wSecond;
1411 GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0;
1416 static struct tm *gmtime(
const time_t *ptime,
struct tm *ptm)
1419 return localtime(ptime, ptm);
1422 static size_t strftime(
char *dst,
size_t dst_size,
const char *fmt,
1423 const struct tm *tm)
1425 (
void) snprintf(dst, dst_size,
"implement strftime() for WinCE");
1434 static int path_cannot_disclose_cgi(
const char *path)
1436 static const char *allowed_last_characters =
"_-";
1437 int last = path[strlen(path) - 1];
1438 return isalnum(last) || strchr(allowed_last_characters, last) !=
NULL;
1441 static int mg_stat(
struct mg_connection *conn,
const char *path,
1445 WIN32_FILE_ATTRIBUTE_DATA info;
1449 if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
1450 filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
1451 filep->modification_time = SYS2UNIX_TIME(
1452 info.ftLastWriteTime.dwLowDateTime,
1453 info.ftLastWriteTime.dwHighDateTime);
1454 filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
1459 if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
1460 memset(filep, 0,
sizeof(*filep));
1465 return filep->membuf !=
NULL || filep->modification_time != 0;
1472 return DeleteFileW(wbuf) ? 0 : -1;
1475 static int mg_mkdir(
const char *path,
int mode)
1482 change_slashes_to_backslashes(buf);
1484 (
void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf,
ARRAY_SIZE(wbuf));
1486 return CreateDirectoryW(wbuf,
NULL) ? 0 : -1;
1490 static DIR * opendir(
const char *name)
1497 SetLastError(ERROR_BAD_ARGUMENTS);
1498 }
else if ((dir = (DIR *)
malloc(
sizeof(*dir))) ==
NULL) {
1499 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1502 attrs = GetFileAttributesW(wpath);
1503 if (attrs != 0xFFFFFFFF &&
1504 ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
1505 (
void) wcscat(wpath,
L"\\*");
1506 dir->handle = FindFirstFileW(wpath, &dir->info);
1507 dir->result.d_name[0] =
'\0';
1517 static int closedir(DIR *dir)
1523 result = FindClose(dir->handle) ? 0 : -1;
1528 SetLastError(ERROR_BAD_ARGUMENTS);
1534 static struct dirent *readdir(DIR *dir)
1536 struct dirent *result = 0;
1540 result = &dir->result;
1541 (
void) WideCharToMultiByte(CP_UTF8, 0,
1542 dir->info.cFileName, -1, result->d_name,
1543 sizeof(result->d_name),
NULL,
NULL);
1545 if (!FindNextFileW(dir->handle, &dir->info)) {
1546 (
void) FindClose(dir->handle);
1551 SetLastError(ERROR_FILE_NOT_FOUND);
1554 SetLastError(ERROR_BAD_ARGUMENTS);
1561 static int poll(
struct pollfd *pfd,
int n,
int milliseconds)
1568 tv.tv_sec = milliseconds / 1000;
1569 tv.tv_usec = (milliseconds % 1000) * 1000;
1572 for (i = 0; i <
n; i++) {
1573 FD_SET((SOCKET) pfd[i].fd, &set);
1576 if (pfd[i].fd > maxfd) {
1581 if ((result = select((
int)maxfd + 1, &set,
NULL,
NULL, &tv)) > 0) {
1582 for (i = 0; i <
n; i++) {
1583 if (FD_ISSET(pfd[i].fd, &set)) {
1584 pfd[i].revents = POLLIN;
1596 (
void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
1601 #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
1603 return (
long)_beginthread((
void (__cdecl *)(
void *)) f, USE_STACK_SIZE, p) == -1
L ? -1 : 0;
1605 return (
long)_beginthread((
void (__cdecl *)(
void *)) f, 0, p) == -1
L ? -1 : 0;
1612 pthread_t *threadidptr)
1615 HANDLE threadhandle;
1618 uip = _beginthreadex(
NULL, 0, (
unsigned (__stdcall *)(
void *)) f, p, 0,
1620 threadhandle = (HANDLE) uip;
1621 if (threadidptr !=
NULL) {
1622 *threadidptr = threadhandle;
1624 result = (threadhandle ==
NULL) ? -1 : 0;
1637 dwevent = WaitForSingleObject(threadid, INFINITE);
1638 if (dwevent == WAIT_FAILED) {
1641 err = GetLastError();
1642 DEBUG_TRACE((
"WaitForSingleObject() failed, error %d", err));
1644 if (dwevent == WAIT_OBJECT_0) {
1645 CloseHandle(threadid);
1653 static HANDLE dlopen(
const char *dll_name,
int flags)
1657 to_unicode(dll_name, wbuf,
ARRAY_SIZE(wbuf));
1658 return LoadLibraryW(wbuf);
1661 static int dlclose(
void *handle)
1665 if (FreeLibrary(handle) != 0) {
1674 #if !defined(NO_CGI)
1676 static int kill(pid_t pid,
int sig_num)
1678 (
void) TerminateProcess(pid, sig_num);
1679 (
void) CloseHandle(pid);
1683 static void trim_trailing_whitespaces(
char *s)
1685 char *e = s + strlen(s) - 1;
1686 while (e > s && isspace(* (
unsigned char *) e)) {
1691 static pid_t
spawn_process(
struct mg_connection *conn,
const char *prog,
1692 char *envblk,
char *envp[],
int fdin,
1693 int fdout,
const char *dir)
1700 PROCESS_INFORMATION pi = { 0 };
1704 memset(&si, 0,
sizeof(si));
1708 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1709 si.wShowWindow = SW_HIDE;
1711 me = GetCurrentProcess();
1712 DuplicateHandle(me, (HANDLE) _get_osfhandle(fdin), me,
1713 &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
1714 DuplicateHandle(me, (HANDLE) _get_osfhandle(fdout), me,
1715 &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
1719 if (interp ==
NULL) {
1720 buf[0] = buf[1] =
'\0';
1723 snprintf(cmdline,
sizeof(cmdline),
"%s%c%s", dir,
'/', prog);
1724 if (
mg_fopen(conn, cmdline,
"r", &file)) {
1725 p = (
char *) file.membuf;
1726 mg_fgets(buf,
sizeof(buf), &file, &p);
1728 buf[
sizeof(buf) - 1] =
'\0';
1731 if (buf[0] ==
'#' && buf[1] ==
'!') {
1732 trim_trailing_whitespaces(buf + 2);
1739 if (interp[0] !=
'\0') {
1740 GetFullPathNameA(interp,
sizeof(full_interp), full_interp,
NULL);
1741 interp = full_interp;
1743 GetFullPathNameA(dir,
sizeof(full_dir), full_dir,
NULL);
1745 mg_snprintf(conn, cmdline,
sizeof(cmdline),
"%s%s\"%s\\%s\"",
1746 interp, interp[0] ==
'\0' ?
"" :
" ", full_dir, prog);
1750 CREATE_NEW_PROCESS_GROUP, envblk,
NULL, &si, &pi) == 0) {
1751 mg_cry(conn,
"%s: CreateProcess(%s): %ld",
1752 __func__, cmdline,
ERRNO);
1753 pi.hProcess = (pid_t) -1;
1756 (
void) CloseHandle(si.hStdOutput);
1757 (
void) CloseHandle(si.hStdInput);
1758 if (pi.hThread !=
NULL)
1759 (
void) CloseHandle(pi.hThread);
1761 return (pid_t) pi.hProcess;
1767 unsigned long on = 1;
1768 return ioctlsocket(sock, FIONBIO, &on);
1772 static int mg_stat(
struct mg_connection *conn,
const char *path,
1778 filep->size = st.st_size;
1779 filep->modification_time = st.st_mtime;
1780 filep->is_directory = S_ISDIR(st.st_mode);
1782 filep->modification_time = (time_t) 0;
1785 return filep->membuf !=
NULL || filep->modification_time != (time_t) 0;
1790 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
1792 mg_cry(conn,
"%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
1793 __func__, strerror(
ERRNO));
1799 pthread_t thread_id;
1800 pthread_attr_t
attr;
1803 (
void) pthread_attr_init(&attr);
1804 (
void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1806 #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
1809 (
void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
1812 result = pthread_create(&thread_id, &attr, func, param);
1813 pthread_attr_destroy(&attr);
1821 pthread_t *threadidptr)
1823 pthread_t thread_id;
1824 pthread_attr_t
attr;
1827 (
void) pthread_attr_init(&attr);
1829 #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
1832 (
void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
1835 result = pthread_create(&thread_id, &attr, func, param);
1836 pthread_attr_destroy(&attr);
1837 if (threadidptr !=
NULL) {
1838 *threadidptr = thread_id;
1849 result = pthread_join(threadid,
NULL);
1855 char *envblk,
char *envp[],
int fdin,
1856 int fdout,
const char *dir)
1863 if ((pid = fork()) == -1) {
1866 }
else if (pid == 0) {
1868 if (chdir(dir) != 0) {
1869 mg_cry(conn,
"%s: chdir(%s): %s", __func__, dir, strerror(
ERRNO));
1870 }
else if (dup2(fdin, 0) == -1) {
1871 mg_cry(conn,
"%s: dup2(%d, 0): %s", __func__, fdin, strerror(
ERRNO));
1872 }
else if (dup2(fdout, 1) == -1) {
1873 mg_cry(conn,
"%s: dup2(%d, 1): %s", __func__, fdout, strerror(
ERRNO));
1885 signal(SIGCHLD, SIG_DFL);
1888 if (interp ==
NULL) {
1889 (
void) execle(prog, prog,
NULL, envp);
1890 mg_cry(conn,
"%s: execle(%s): %s", __func__, prog, strerror(
ERRNO));
1892 (
void) execle(interp, interp, prog,
NULL, envp);
1893 mg_cry(conn,
"%s: execle(%s %s): %s", __func__, interp, prog,
1908 flags = fcntl(sock, F_GETFL, 0);
1909 (
void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
1917 static int64_t
push(FILE *fp, SOCKET sock,
SSL *ssl,
const char *buf,
1925 while (sent < len) {
1928 k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
1936 n = (int) fwrite(buf + sent, 1, (
size_t) k, fp);
1954 static int pull(FILE *fp,
struct mg_connection *conn,
char *buf,
int len)
1963 nread =
read(fileno(fp), buf, (
size_t) len);
1965 }
else if (conn->ssl !=
NULL) {
1966 nread =
SSL_read(conn->ssl, buf, len);
1969 nread = recv(conn->client.sock, buf, (
size_t) len, 0);
1972 return conn->ctx->stop_flag ? -1 : nread;
1975 static int pull_all(FILE *fp,
struct mg_connection *conn,
char *buf,
int len)
1979 while (len > 0 && conn->ctx->stop_flag == 0) {
1980 n =
pull(fp, conn, buf + nread, len);
1984 }
else if (n == 0) {
1987 conn->consumed_content +=
n;
1996 int mg_read(
struct mg_connection *conn,
void *buf,
size_t len)
1998 int n, buffered_len, nread;
2002 if (conn->consumed_content == 0 && conn->content_len == 0) {
2003 conn->content_len = INT64_MAX;
2004 conn->must_close = 1;
2008 if (conn->consumed_content < conn->content_len) {
2010 int64_t to_read = conn->content_len - conn->consumed_content;
2011 if (to_read < (int64_t) len) {
2016 body = conn->buf + conn->request_len + conn->consumed_content;
2017 buffered_len = (int)(&conn->buf[conn->data_len] - body);
2018 if (buffered_len > 0) {
2019 if (len < (
size_t) buffered_len) {
2020 buffered_len = (int) len;
2022 memcpy(buf, body, (
size_t) buffered_len);
2023 len -= buffered_len;
2024 conn->consumed_content += buffered_len;
2025 nread += buffered_len;
2026 buf = (
char *) buf + buffered_len;
2032 nread = n >= 0 ? nread + n :
n;
2037 int mg_write(
struct mg_connection *conn,
const void *buf,
size_t len)
2040 int64_t
n,
total, allowed;
2042 if (conn->throttle > 0) {
2043 if ((now = time(
NULL)) != conn->last_throttle_time) {
2044 conn->last_throttle_time = now;
2045 conn->last_throttle_bytes = 0;
2047 allowed = conn->throttle - conn->last_throttle_bytes;
2048 if (allowed > (int64_t) len) {
2051 if ((total =
push(
NULL, conn->client.sock, conn->ssl, (
const char *) buf,
2052 (int64_t) allowed)) == allowed) {
2053 buf = (
char *) buf + total;
2054 conn->last_throttle_bytes +=
total;
2055 while (total < (int64_t) len && conn->ctx->stop_flag == 0) {
2056 allowed = conn->throttle > (int64_t) len - total ?
2057 (int64_t) len - total : conn->throttle;
2058 if ((n =
push(
NULL, conn->client.sock, conn->ssl, (
const char *) buf,
2059 (int64_t) allowed)) != allowed) {
2063 conn->last_throttle_bytes = allowed;
2064 conn->last_throttle_time = time(
NULL);
2065 buf = (
char *) buf + n;
2070 total =
push(
NULL, conn->client.sock, conn->ssl, (
const char *) buf,
2085 if (*buf)
free(*buf);
2086 *buf = (
char *)
malloc(size *= 4);
2088 va_copy(ap_copy, ap);
2089 len = vsnprintf(*buf, size, fmt, ap_copy);
2110 va_copy(ap_copy, ap);
2111 len = vsnprintf(
NULL, 0, fmt, ap_copy);
2118 va_copy(ap_copy, ap);
2121 }
else if (len > (
int) size &&
2122 (size = len + 1) > 0 &&
2126 va_copy(ap_copy, ap);
2134 int mg_vprintf(
struct mg_connection *conn,
const char *fmt, va_list ap);
2136 int mg_vprintf(
struct mg_connection *conn,
const char *fmt, va_list ap)
2141 if ((len =
alloc_vprintf(&buf,
sizeof(mem), fmt, ap)) > 0) {
2142 len =
mg_write(conn, buf, (
size_t) len);
2144 if (buf != mem && buf !=
NULL) {
2151 int mg_printf(
struct mg_connection *conn,
const char *fmt, ...)
2164 int dst_len,
int is_form_url_encoded)
2167 #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
2169 for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
2170 if (src[i] ==
'%' && i < src_len - 2 &&
2171 isxdigit(* (
const unsigned char *) (src + i + 1)) &&
2172 isxdigit(* (
const unsigned char *) (src + i + 2))) {
2173 a = tolower(* (
const unsigned char *) (src + i + 1));
2174 b = tolower(* (
const unsigned char *) (src + i + 2));
2177 }
else if (is_form_url_encoded && src[i] ==
'+') {
2186 return i >= src_len ? j : -1;
2189 int mg_get_var(
const char *data,
size_t data_len,
const char *name,
2190 char *dst,
size_t dst_len)
2192 return mg_get_var2(data,data_len,name,dst,dst_len,0);
2196 char *dst,
size_t dst_len,
size_t occurrence)
2198 const char *p, *e, *s;
2202 if (dst ==
NULL || dst_len == 0) {
2204 }
else if (data ==
NULL || name ==
NULL || data_len == 0) {
2208 name_len = strlen(name);
2209 e = data + data_len;
2214 for (p = data; p + name_len < e; p++) {
2215 if ((p == data || p[-1] ==
'&') && p[name_len] ==
'=' &&
2222 s = (
const char *) memchr(p,
'&', (
size_t)(e - p));
2245 char *dst,
size_t dst_size)
2247 const char *s, *p, *end;
2248 int name_len, len = -1;
2250 if (dst ==
NULL || dst_size == 0) {
2252 }
else if (var_name ==
NULL || (s = cookie_header) ==
NULL) {
2256 name_len = (int) strlen(var_name);
2257 end = s + strlen(s);
2261 if (s[name_len] ==
'=') {
2263 if ((p = strchr(s,
' ')) ==
NULL)
2267 if (*s ==
'"' && p[-1] ==
'"' && p > s + 1) {
2271 if ((
size_t) (p - s) < dst_size) {
2285 size_t buf_len,
struct file *filep,
2286 int * is_script_ressource)
2289 const char *rewrite, *uri = conn->request_info.uri,
2294 char const* accept_encoding;
2296 *is_script_ressource = 0;
2298 #if defined(USE_WEBSOCKET)
2299 if (is_websocket_request(conn) && conn->ctx->config[WEBSOCKET_ROOT]) {
2300 root = conn->ctx->config[WEBSOCKET_ROOT];
2308 root ==
NULL ?
"" : root,
2309 root ==
NULL ?
"" : uri);
2311 rewrite = conn->ctx->config[
REWRITE];
2313 if ((match_len =
match_prefix(a.ptr, (
int) a.len, uri)) > 0) {
2314 mg_snprintf(conn, buf, buf_len - 1,
"%.*s%s", (
int) b.len, b.ptr,
2320 if (
mg_stat(conn, buf, filep))
return;
2329 if (strstr(accept_encoding,
"gzip") !=
NULL) {
2330 snprintf(gz_path,
sizeof(gz_path),
"%s.gz", buf);
2331 if (
mg_stat(conn, gz_path, filep)) {
2339 for (p = buf + strlen(buf); p > buf + 1; p--) {
2347 (
int)strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), buf) > 0
2349 ) &&
mg_stat(conn, buf, filep)) {
2355 conn->path_info = p + 1;
2356 memmove(p + 2, p + 1, strlen(p + 1) + 1);
2359 *is_script_ressource = 1;
2377 for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
2379 if (!isprint(* (
const unsigned char *) s) && *s !=
'\r' &&
2380 *s !=
'\n' && * (
const unsigned char *) s < 128) {
2385 }
else if (s[0] ==
'\n' && s[1] ==
'\n') {
2386 len = (int) (s - buf) + 2;
2387 }
else if (s[0] ==
'\n' && &s[1] < e &&
2388 s[1] ==
'\r' && s[2] ==
'\n') {
2389 len = (int) (s - buf) + 3;
2400 for (i = 0; i <
ARRAY_SIZE(month_names); i++)
2401 if (!strcmp(s, month_names[i]))
2409 return year / 4 - year / 100 + year / 400;
2415 static const unsigned short days_before_month[] = {
2416 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
2419 int second, minute, hour, day, month, year, leap_days, days;
2420 time_t result = (time_t) 0;
2422 if (((sscanf(datetime,
"%d/%3s/%d %d:%d:%d",
2423 &day, month_str, &year, &hour, &minute, &second) == 6) ||
2424 (sscanf(datetime,
"%d %3s %d %d:%d:%d",
2425 &day, month_str, &year, &hour, &minute, &second) == 6) ||
2426 (sscanf(datetime,
"%*3s, %d %3s %d %d:%d:%d",
2427 &day, month_str, &year, &hour, &minute, &second) == 6) ||
2428 (sscanf(datetime,
"%d-%3s-%d %d:%d:%d",
2429 &day, month_str, &year, &hour, &minute, &second) == 6)) &&
2434 days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
2435 result = (time_t) days * 24 * 3600 + (time_t) hour * 3600 +
2436 minute * 60 + second;
2448 while (*s !=
'\0') {
2450 if (s[-1] ==
'/' || s[-1] ==
'\\') {
2452 while (s[0] !=
'\0') {
2453 if (s[0] ==
'/' || s[0] ==
'\\') {
2455 }
else if (s[0] ==
'.' && s[1] ==
'.') {
2466 static const struct {
2473 {
".doc", 4,
"application/msword"},
2474 {
".eps", 4,
"application/postscript"},
2475 {
".exe", 4,
"application/octet-stream"},
2476 {
".js", 3,
"application/javascript"},
2477 {
".json", 5,
"application/json"},
2478 {
".pdf", 4,
"application/pdf"},
2479 {
".ps", 3,
"application/postscript"},
2480 {
".rtf", 4,
"application/rtf"},
2481 {
".xhtml", 6,
"application/xhtml+xml"},
2482 {
".xsl", 4,
"application/xml"},
2483 {
".xslt", 5,
"application/xml"},
2486 {
".mp3", 4,
"audio/mpeg"},
2487 {
".oga", 4,
"audio/ogg"},
2488 {
".ogg", 4,
"audio/ogg"},
2491 {
".gif", 4,
"image/gif"},
2492 {
".ief", 4,
"image/ief"},
2493 {
".jpeg", 5,
"image/jpeg"},
2494 {
".jpg", 4,
"image/jpeg"},
2495 {
".jpm", 4,
"image/jpm"},
2496 {
".jpx", 4,
"image/jpx"},
2497 {
".png", 4,
"image/png"},
2498 {
".svg", 4,
"image/svg+xml"},
2499 {
".tif", 4,
"image/tiff"},
2500 {
".tiff", 5,
"image/tiff"},
2503 {
".wrl", 4,
"model/vrml"},
2506 {
".css", 4,
"text/css"},
2507 {
".csv", 4,
"text/csv"},
2508 {
".htm", 4,
"text/html"},
2509 {
".html", 5,
"text/html"},
2510 {
".sgm", 4,
"text/sgml"},
2511 {
".shtm", 5,
"text/html"},
2512 {
".shtml", 6,
"text/html"},
2513 {
".txt", 4,
"text/plain"},
2514 {
".xml", 4,
"text/xml"},
2517 {
".mov", 4,
"video/quicktime"},
2518 {
".mp4", 4,
"video/mp4"},
2519 {
".mpeg", 5,
"video/mpeg"},
2520 {
".mpg", 4,
"video/mpeg"},
2521 {
".ogv", 4,
"video/ogg"},
2522 {
".qt", 3,
"video/quicktime"},
2527 {
".arj", 4,
"application/x-arj-compressed"},
2528 {
".gz", 3,
"application/x-gunzip"},
2529 {
".rar", 4,
"application/x-arj-compressed"},
2530 {
".swf", 4,
"application/x-shockwave-flash"},
2531 {
".tar", 4,
"application/x-tar"},
2532 {
".tgz", 4,
"application/x-tar-gz"},
2533 {
".torrent", 8,
"application/x-bittorrent"},
2534 {
".ppt", 4,
"application/x-mspowerpoint"},
2535 {
".xls", 4,
"application/x-msexcel"},
2536 {
".zip", 4,
"application/x-zip-compressed"},
2537 {
".aac", 4,
"audio/aac"},
2538 {
".aif", 4,
"audio/x-aif"},
2539 {
".m3u", 4,
"audio/x-mpegurl"},
2540 {
".mid", 4,
"audio/x-midi"},
2541 {
".ra", 3,
"audio/x-pn-realaudio"},
2542 {
".ram", 4,
"audio/x-pn-realaudio"},
2543 {
".wav", 4,
"audio/x-wav"},
2544 {
".bmp", 4,
"image/bmp"},
2545 {
".ico", 4,
"image/x-icon"},
2546 {
".pct", 4,
"image/x-pct"},
2547 {
".pict", 5,
"image/pict"},
2548 {
".rgb", 4,
"image/x-rgb"},
2549 {
".webm", 5,
"video/webm"},
2550 {
".asf", 4,
"video/x-ms-asf"},
2551 {
".avi", 4,
"video/x-msvideo"},
2552 {
".m4v", 4,
"video/x-m4v"},
2561 path_len = strlen(path);
2571 return "text/plain";
2579 struct vec ext_vec, mime_vec;
2580 const char *list, *ext;
2583 path_len = strlen(path);
2590 ext = path + path_len - ext_vec.len;
2598 vec->len = strlen(vec->ptr);
2603 static void bin2str(
char *to,
const unsigned char *p,
size_t len)
2605 static const char *
hex =
"0123456789abcdef";
2607 for (; len--; p++) {
2608 *to++ = hex[p[0] >> 4];
2609 *to++ = hex[p[0] & 0x0f];
2625 while ((p = va_arg(ap,
const char *)) !=
NULL) {
2631 bin2str(buf, hash,
sizeof(hash));
2637 const char *nonce,
const char *nc,
const char *cnonce,
2638 const char *qop,
const char *response)
2640 char ha2[32 + 1], expected_response[32 + 1];
2651 strlen(response) != 32
2658 mg_md5(expected_response, ha1,
":", nonce,
":", nc,
2659 ":", cnonce,
":", qop,
":", ha2,
NULL);
2673 if (gpass !=
NULL) {
2675 if (!
mg_fopen(conn, gpass,
"r", filep)) {
2677 mg_cry(conn,
"fopen(%s): %s", gpass, strerror(
ERRNO));
2684 }
else if (
mg_stat(conn, path, &file) && file.is_directory) {
2687 if (!
mg_fopen(conn, name,
"r", filep)) {
2689 mg_cry(conn,
"fopen(%s): %s", name, strerror(
ERRNO));
2694 for (p = path, e = p + strlen(p) - 1; e > p; e--)
2699 if (!
mg_fopen(conn, name,
"r", filep)) {
2701 mg_cry(conn,
"fopen(%s): %s", name, strerror(
ERRNO));
2709 char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
2714 size_t buf_size,
struct ah *ah)
2717 const char *auth_header;
2719 (
void) memset(ah, 0,
sizeof(*ah));
2732 while (isspace(* (
unsigned char *) s)) {
2747 if (*name ==
'\0') {
2751 if (!strcmp(name,
"username")) {
2753 }
else if (!strcmp(name,
"cnonce")) {
2755 }
else if (!strcmp(name,
"response")) {
2756 ah->response =
value;
2757 }
else if (!strcmp(name,
"uri")) {
2759 }
else if (!strcmp(name,
"qop")) {
2761 }
else if (!strcmp(name,
"nc")) {
2763 }
else if (!strcmp(name,
"nonce")) {
2769 if (ah->user !=
NULL) {
2770 conn->request_info.remote_user =
mg_strdup(ah->user);
2778 static char *
mg_fgets(
char *buf,
size_t size,
struct file *filep,
char **p)
2784 if (filep->membuf !=
NULL && *p !=
NULL) {
2785 memend = (
char *) &filep->membuf[filep->size];
2786 eof = (
char *) memchr(*p,
'\n', memend - *p);
2794 len = (
size_t) (eof - *p) > size - 1 ? size - 1 : (
size_t) (eof - *p);
2795 memcpy(buf, *p, len);
2798 return len ? eof :
NULL;
2799 }
else if (filep->fp !=
NULL) {
2800 return fgets(buf, (
int)size, filep->fp);
2807 static int authorize(
struct mg_connection *conn,
struct file *filep)
2810 char line[256], f_user[256] =
"", ha1[256] =
"", f_domain[256] =
"", buf[
MG_BUF_LEN], *p;
2817 p = (
char *) filep->membuf;
2819 if (sscanf(line,
"%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
2823 if (!strcmp(ah.user, f_user) &&
2825 return check_password(conn->request_info.request_method, ha1, ah.uri,
2826 ah.nonce, ah.nc, ah.cnonce, ah.qop, ah.response);
2836 struct vec uri_vec, filename_vec;
2843 if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
2845 (
int) filename_vec.len, filename_vec.ptr);
2846 if (!
mg_fopen(conn, fname,
"r", &file)) {
2847 mg_cry(conn,
"%s: cannot open %s: %s", __func__, fname, strerror(errno));
2868 time_t curtime = time(
NULL);
2870 conn->status_code = 401;
2871 conn->must_close = 1;
2876 "HTTP/1.1 401 Unauthorized\r\n"
2878 "Connection: %s\r\n"
2879 "Content-Length: 0\r\n"
2880 "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
2883 (
unsigned long) time(
NULL));
2892 if (passfile !=
NULL &&
mg_fopen(conn, passfile,
"r", &file)) {
2901 const char *user,
const char *pass)
2904 char line[512], u[512] =
"", d[512] =
"", ha1[33], tmp[
PATH_MAX+1];
2911 if (pass !=
NULL && pass[0] ==
'\0') {
2915 (
void) snprintf(tmp,
sizeof(tmp) - 1,
"%s.tmp", fname);
2916 tmp[
sizeof(tmp) - 1] = 0;
2919 if ((fp = fopen(fname,
"a+")) !=
NULL) {
2924 if ((fp = fopen(fname,
"r")) ==
NULL) {
2926 }
else if ((fp2 = fopen(tmp,
"w+")) ==
NULL) {
2932 while (fgets(line,
sizeof(line), fp) !=
NULL) {
2933 if (sscanf(line,
"%[^:]:%[^:]:%*s", u, d) != 2) {
2937 if (!strcmp(u, user) && !strcmp(d, domain)) {
2940 mg_md5(ha1, user,
":", domain,
":", pass,
NULL);
2941 fprintf(fp2,
"%s:%s:%s\n", user, domain, ha1);
2944 fprintf(fp2,
"%s", line);
2949 if (!found && pass !=
NULL) {
2950 mg_md5(ha1, user,
":", domain,
":", pass,
NULL);
2951 fprintf(fp2,
"%s:%s:%s\n", user, domain, ha1);
2965 static SOCKET
conn2(
struct mg_context *ctx ,
const char *host,
int port,
2966 int use_ssl,
char *ebuf,
size_t ebuf_len)
2968 struct sockaddr_in sain;
2973 snprintf(ebuf, ebuf_len,
"%s",
"NULL host");
2975 snprintf(ebuf, ebuf_len,
"%s",
"SSL is not initialized");
2977 }
else if ((he = gethostbyname(host)) ==
NULL) {
2978 snprintf(ebuf, ebuf_len,
"gethostbyname(%s): %s", host, strerror(
ERRNO));
2979 }
else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) ==
INVALID_SOCKET) {
2980 snprintf(ebuf, ebuf_len,
"socket(): %s", strerror(
ERRNO));
2983 memset(&sain,
'\0',
sizeof(sain));
2984 sain.sin_family = AF_INET;
2985 sain.sin_port = htons((uint16_t) port);
2986 sain.sin_addr = * (
struct in_addr *) he->h_addr_list[0];
2987 if (connect(sock, (
struct sockaddr *) &sain,
sizeof(sain)) != 0) {
2988 snprintf(ebuf, ebuf_len,
"connect(%s:%d): %s",
2989 host, port, strerror(
ERRNO));
2999 static const char *dont_escape =
"._-$,;~()";
3000 static const char *
hex =
"0123456789abcdef";
3002 const char *end = dst + dst_len - 1;
3004 for (; *src !=
'\0' && pos < end; src++, pos++) {
3005 if (isalnum(*(
const unsigned char *) src) ||
3006 strchr(dont_escape, * (
const unsigned char *) src) !=
NULL) {
3008 }
else if (pos + 2 < end) {
3010 pos[1] = hex[(* (
const unsigned char *) src) >> 4];
3011 pos[2] = hex[(* (
const unsigned char *) src) & 0xf];
3019 return (*src ==
'\0') ? (int)(pos - dst) : -1;
3024 char size[64], mod[64], href[
PATH_MAX];
3027 if (de->file.is_directory) {
3028 mg_snprintf(de->conn, size,
sizeof(size),
"%s",
"[DIRECTORY]");
3032 if (de->file.size < 1024) {
3033 mg_snprintf(de->conn, size,
sizeof(size),
"%d", (
int) de->file.size);
3034 }
else if (de->file.size < 0x100000) {
3036 "%.1fk", (
double) de->file.size / 1024.0);
3037 }
else if (de->file.size < 0x40000000) {
3039 "%.1fM", (
double) de->file.size / 1048576);
3042 "%.1fG", (
double) de->file.size / 1073741824);
3045 tm = localtime(&de->file.modification_time);
3047 strftime(mod,
sizeof(mod),
"%d-%b-%Y %H:%M", tm);
3049 strncpy(mod,
"01-Jan-1970 00:00",
sizeof(mod));
3050 mod[
sizeof(mod) - 1] =
'\0';
3053 de->conn->num_bytes_sent +=
mg_printf(de->conn,
3054 "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
3055 "<td> %s</td><td> %s</td></tr>\n",
3056 de->conn->request_info.uri, href, de->file.is_directory ?
"/" :
"",
3057 de->file_name, de->file.is_directory ?
"/" :
"", mod, size);
3066 const struct de *a = (
const struct de *) p1, *b = (
const struct de *)
p2;
3067 const char *query_string = a->conn->request_info.query_string;
3070 if (query_string ==
NULL) {
3071 query_string =
"na";
3074 if (a->file.is_directory && !b->file.is_directory) {
3076 }
else if (!a->file.is_directory && b->file.is_directory) {
3078 }
else if (*query_string ==
'n') {
3079 cmp_result = strcmp(a->file_name, b->file_name);
3080 }
else if (*query_string ==
's') {
3081 cmp_result = a->file.size == b->file.size ? 0 :
3082 a->file.size > b->file.size ? 1 : -1;
3083 }
else if (*query_string ==
'd') {
3084 cmp_result = a->file.modification_time == b->file.modification_time ? 0 :
3085 a->file.modification_time > b->file.modification_time ? 1 : -1;
3088 return query_string[1] ==
'd' ? -cmp_result : cmp_result;
3095 return match_prefix(pw_pattern, (
int)strlen(pw_pattern), path) > 0 ||
3096 (pattern !=
NULL &&
match_prefix(pattern, (
int)strlen(pattern), path) > 0);
3100 void *data,
void (*cb)(
struct de *,
void *))
3107 if ((dirp = opendir(dir)) ==
NULL) {
3112 while ((dp = readdir(dirp)) !=
NULL) {
3114 if (!strcmp(dp->d_name,
".") ||
3115 !strcmp(dp->d_name,
"..") ||
3120 mg_snprintf(conn, path,
sizeof(path),
"%s%c%s", dir,
'/', dp->d_name);
3127 memset(&de.file, 0,
sizeof(de.file));
3128 if (!
mg_stat(conn, path, &de.file)) {
3129 mg_cry(conn,
"%s: mg_stat(%s) failed: %s",
3130 __func__, path, strerror(
ERRNO));
3133 de.file_name = dp->d_name;
3136 (
void) closedir(dirp);
3148 if ((dirp = opendir(dir)) ==
NULL) {
3153 while ((dp = readdir(dirp)) !=
NULL) {
3156 if (!strcmp(dp->d_name,
".") ||
3157 !strcmp(dp->d_name,
"..")) {
3161 mg_snprintf(conn, path,
sizeof(path),
"%s%c%s", dir,
'/', dp->d_name);
3168 memset(&de.file, 0,
sizeof(de.file));
3169 if (!
mg_stat(conn, path, &de.file)) {
3170 mg_cry(conn,
"%s: mg_stat(%s) failed: %s",
3171 __func__, path, strerror(
ERRNO));
3173 if(de.file.modification_time) {
3174 if(de.file.is_directory) {
3182 (
void) closedir(dirp);
3190 struct dir_scan_data {
3199 void *new_ptr = realloc(ptr, size);
3200 if (new_ptr ==
NULL) {
3208 struct dir_scan_data *dsd = (
struct dir_scan_data *) data;
3210 if (dsd->entries ==
NULL || dsd->num_entries >= dsd->arr_size) {
3212 dsd->entries = (
struct de *)
realloc2(dsd->entries, dsd->arr_size *
3213 sizeof(dsd->entries[0]));
3215 if (dsd->entries ==
NULL) {
3217 dsd->num_entries = 0;
3219 dsd->entries[dsd->num_entries].file_name =
mg_strdup(de->file_name);
3220 dsd->entries[dsd->num_entries].file = de->file;
3221 dsd->entries[dsd->num_entries].conn = de->conn;
3229 int i, sort_direction;
3230 struct dir_scan_data data = {
NULL, 0, 128 };
3232 time_t curtime = time(
NULL);
3236 "Error: opendir(%s): %s", dir, strerror(
ERRNO));
3242 sort_direction = conn->request_info.query_string !=
NULL &&
3243 conn->request_info.query_string[1] ==
'd' ?
'a' :
'd';
3245 conn->must_close = 1;
3248 "Connection: close\r\n"
3249 "Content-Type: text/html; charset=utf-8\r\n\r\n",
3253 "<html><head><title>Index of %s</title>"
3254 "<style>th {text-align: left;}</style></head>"
3255 "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
3256 "<tr><th><a href=\"?n%c\">Name</a></th>"
3257 "<th><a href=\"?d%c\">Modified</a></th>"
3258 "<th><a href=\"?s%c\">Size</a></th></tr>"
3259 "<tr><td colspan=\"3\"><hr></td></tr>",
3260 conn->request_info.uri, conn->request_info.uri,
3261 sort_direction, sort_direction, sort_direction);
3265 "<tr><td><a href=\"%s%s\">%s</a></td>"
3266 "<td> %s</td><td> %s</td></tr>\n",
3267 conn->request_info.uri,
"..",
"Parent directory",
"-",
"-");
3270 if (data.entries !=
NULL) {
3271 qsort(data.entries, (
size_t) data.num_entries,
3273 for (i = 0; i < data.num_entries; i++) {
3275 free(data.entries[i].file_name);
3280 conn->num_bytes_sent +=
mg_printf(conn,
"%s",
"</table></body></html>");
3281 conn->status_code = 200;
3286 int64_t offset, int64_t len)
3289 int to_read, num_read, num_written;
3292 offset = offset < 0 ? 0 : offset > filep->size ? filep->size : offset;
3294 if (len > 0 && filep->membuf !=
NULL && filep->size > 0) {
3295 if (len > filep->size - offset) {
3296 len = filep->size - offset;
3298 mg_write(conn, filep->membuf + offset, (
size_t) len);
3299 }
else if (len > 0 && filep->fp !=
NULL) {
3300 if (offset > 0 && fseeko(filep->fp, offset, SEEK_SET) != 0) {
3301 mg_cry(conn,
"%s: fseeko() failed: %s",
3302 __func__, strerror(
ERRNO));
3306 to_read =
sizeof(buf);
3307 if ((int64_t) to_read > len) {
3308 to_read = (int) len;
3312 if ((num_read = (
int) fread(buf, 1, (
size_t) to_read, filep->fp)) <= 0) {
3317 if ((num_written =
mg_write(conn, buf, (
size_t) num_read)) != num_read) {
3322 conn->num_bytes_sent += num_written;
3334 const struct file *filep)
3336 snprintf(buf, buf_len,
"\"%lx.%" INT64_FMT "\"",
3337 (
unsigned long) filep->modification_time, filep->size);
3342 if (filep !=
NULL && filep->fp !=
NULL) {
3346 if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) {
3347 mg_cry(conn,
"%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
3348 __func__, strerror(
ERRNO));
3357 char date[64], lm[64], etag[64], range[64];
3358 const char *msg =
"OK", *hdr;
3359 time_t curtime = time(
NULL);
3361 struct vec mime_vec;
3364 char const* encoding =
"";
3368 conn->status_code = 200;
3374 if (filep->gzipped) {
3375 snprintf(gz_path,
sizeof(gz_path),
"%s.gz", path);
3377 encoding =
"Content-Encoding: gzip\r\n";
3380 if (!
mg_fopen(conn, path,
"rb", filep)) {
3382 "fopen(%s): %s", path, strerror(
ERRNO));
3392 r1 >= 0 && r2 >= 0) {
3395 if (filep->gzipped) {
3396 send_http_error(conn, 501,
"Not Implemented",
"range requests in gzipped files are not supported");
3400 conn->status_code = 206;
3401 cl = n == 2 ? (r2 > cl ? cl :
r2) - r1 + 1: cl - r1;
3403 "Content-Range: bytes "
3406 r1, r1 + cl - 1, filep->size);
3407 msg =
"Partial Content";
3417 "HTTP/1.1 %d %s\r\n"
3419 "Last-Modified: %s\r\n"
3421 "Content-Type: %.*s\r\n"
3423 "Connection: %s\r\n"
3424 "Accept-Ranges: bytes\r\n"
3426 conn->status_code, msg, date, lm, etag, (
int) mime_vec.len,
3429 if (strcmp(conn->request_info.request_method,
"HEAD") != 0) {
3438 if (
mg_stat(conn, path, &file)) {
3464 return !strcmp(method,
"GET") || !strcmp(method,
"POST") ||
3465 !strcmp(method,
"HEAD") || !strcmp(method,
"CONNECT") ||
3466 !strcmp(method,
"PUT") || !strcmp(method,
"DELETE") ||
3467 !strcmp(method,
"OPTIONS") || !strcmp(method,
"PROPFIND")
3468 || !strcmp(method,
"MKCOL")
3478 if (request_length > 0) {
3483 buf[request_length - 1] =
'\0';
3486 while (*buf !=
'\0' && isspace(* (
unsigned char *) buf)) {
3496 if ((is_request && memcmp(ri->
http_version,
"HTTP/", 5) != 0) ||
3498 request_length = -1;
3506 return request_length;
3515 char *buf,
int bufsiz,
int *nread)
3517 int request_len, n = 0;
3520 while (conn->ctx->stop_flag == 0 &&
3521 *nread < bufsiz && request_len == 0 &&
3522 (n =
pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
3524 assert(*nread <= bufsiz);
3528 return request_len <= 0 && n <= 0 ? -1 : request_len;
3535 size_t path_len,
struct file *filep)
3537 const char *list = conn->ctx->config[
INDEX_FILES];
3539 struct vec filename_vec;
3540 size_t n = strlen(path);
3546 while (n > 0 && path[n - 1] ==
'/') {
3556 if (filename_vec.len > path_len - (n + 2))
3560 mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
3563 if (
mg_stat(conn, path, &file)) {
3581 const struct file *filep)
3592 SOCKET sock,
SSL *ssl)
3594 const char *expect, *body;
3596 int to_read, nread, buffered_len, success = 0;
3601 if (conn->content_len == -1) {
3606 if (expect !=
NULL) {
3607 (
void)
mg_printf(conn,
"%s",
"HTTP/1.1 100 Continue\r\n\r\n");
3610 body = conn->buf + conn->request_len + conn->consumed_content;
3611 buffered_len = (int)(&conn->buf[conn->data_len] - body);
3612 assert(buffered_len >= 0);
3613 assert(conn->consumed_content == 0);
3615 if (buffered_len > 0) {
3616 if ((int64_t) buffered_len > conn->content_len) {
3617 buffered_len = (int) conn->content_len;
3619 push(fp, sock, ssl, body, (int64_t) buffered_len);
3620 conn->consumed_content += buffered_len;
3624 while (conn->consumed_content < conn->content_len) {
3625 to_read =
sizeof(buf);
3626 if ((int64_t) to_read > conn->content_len - conn->consumed_content) {
3627 to_read = (int) (conn->content_len - conn->consumed_content);
3629 nread =
pull(
NULL, conn, buf, to_read);
3630 if (nread <= 0 ||
push(fp, sock, ssl, buf, nread) != nread) {
3633 conn->consumed_content += nread;
3636 if (conn->consumed_content == conn->content_len) {
3637 success = nread >= 0;
3649 #if !defined(NO_CGI)
3658 struct cgi_env_block {
3659 struct mg_connection *conn;
3666 static char *
addenv(
struct cgi_env_block *block,
3672 static
char *
addenv(struct cgi_env_block *block, const
char *fmt, ...)
3679 space =
sizeof(block->buf) - block->len - 2;
3683 added = block->buf + block->len;
3687 n =
mg_vsnprintf(block->conn, added, (
size_t) space, fmt, ap);
3691 if (n > 0 && n + 1 < space &&
3692 block->nvars < (
int)
ARRAY_SIZE(block->vars) - 2) {
3694 block->vars[block->nvars++] = added;
3696 block->len += n + 1;
3698 mg_cry(block->conn,
"%s: CGI env buffer truncated for [%s]", __func__, fmt);
3706 struct cgi_env_block *blk)
3708 const char *s, *
slash;
3713 blk->len = blk->nvars = 0;
3723 addenv(blk,
"%s",
"GATEWAY_INTERFACE=CGI/1.1");
3724 addenv(blk,
"%s",
"SERVER_PROTOCOL=HTTP/1.1");
3725 addenv(blk,
"%s",
"REDIRECT_STATUS=200");
3728 addenv(blk,
"SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
3730 addenv(blk,
"REQUEST_METHOD=%s", conn->request_info.request_method);
3731 addenv(blk,
"REMOTE_ADDR=%s", src_addr);
3732 addenv(blk,
"REMOTE_PORT=%d", conn->request_info.remote_port);
3733 addenv(blk,
"REQUEST_URI=%s", conn->request_info.uri);
3736 assert(conn->request_info.uri[0] ==
'/');
3737 slash = strrchr(conn->request_info.uri,
'/');
3738 if ((s = strrchr(prog,
'/')) ==
NULL)
3740 addenv(blk,
"SCRIPT_NAME=%.*s%s", (
int) (slash - conn->request_info.uri),
3741 conn->request_info.uri, s);
3743 addenv(blk,
"SCRIPT_FILENAME=%s", prog);
3744 addenv(blk,
"PATH_TRANSLATED=%s", prog);
3745 addenv(blk,
"HTTPS=%s", conn->ssl ==
NULL ?
"off" :
"on");
3748 addenv(blk,
"CONTENT_TYPE=%s", s);
3750 if (conn->request_info.query_string !=
NULL)
3751 addenv(blk,
"QUERY_STRING=%s", conn->request_info.query_string);
3754 addenv(blk,
"CONTENT_LENGTH=%s", s);
3756 if ((s = getenv(
"PATH")) !=
NULL)
3757 addenv(blk,
"PATH=%s", s);
3759 if (conn->path_info !=
NULL) {
3760 addenv(blk,
"PATH_INFO=%s", conn->path_info);
3764 if ((s = getenv(
"COMSPEC")) !=
NULL) {
3765 addenv(blk,
"COMSPEC=%s", s);
3767 if ((s = getenv(
"SYSTEMROOT")) !=
NULL) {
3768 addenv(blk,
"SYSTEMROOT=%s", s);
3770 if ((s = getenv(
"SystemDrive")) !=
NULL) {
3771 addenv(blk,
"SystemDrive=%s", s);
3773 if ((s = getenv(
"ProgramFiles")) !=
NULL) {
3774 addenv(blk,
"ProgramFiles=%s", s);
3776 if ((s = getenv(
"ProgramFiles(x86)")) !=
NULL) {
3777 addenv(blk,
"ProgramFiles(x86)=%s", s);
3780 if ((s = getenv(
"LD_LIBRARY_PATH")) !=
NULL)
3781 addenv(blk,
"LD_LIBRARY_PATH=%s", s);
3784 if ((s = getenv(
"PERLLIB")) !=
NULL)
3785 addenv(blk,
"PERLLIB=%s", s);
3787 if (conn->request_info.remote_user !=
NULL) {
3788 addenv(blk,
"REMOTE_USER=%s", conn->request_info.remote_user);
3789 addenv(blk,
"%s",
"AUTH_TYPE=Digest");
3793 for (i = 0; i < conn->request_info.num_headers; i++) {
3794 p =
addenv(blk,
"HTTP_%s=%s",
3795 conn->request_info.http_headers[i].name,
3796 conn->request_info.http_headers[i].value);
3799 for (; *p !=
'=' && *p !=
'\0'; p++) {
3802 *p = (char) toupper(* (
unsigned char *) p);
3809 addenv(blk,
"%.*s", (
int) var_vec.len, var_vec.ptr);
3812 blk->vars[blk->nvars++] =
NULL;
3813 blk->buf[blk->len++] =
'\0';
3817 assert(blk->len < (
int)
sizeof(blk->buf));
3824 int headers_len, data_len, i, fdin[2] = { 0, 0 }, fdout[2] = { 0, 0 };
3825 const char *
status, *status_text, *connection_state;
3828 struct cgi_env_block blk;
3831 pid_t pid = (pid_t) -1;
3841 if ((p = strrchr(dir,
'/')) !=
NULL) {
3844 dir[0] =
'.', dir[1] =
'\0';
3848 if (pipe(fdin) != 0 || pipe(fdout) != 0) {
3850 "Cannot create CGI pipe: %s", strerror(
ERRNO));
3854 pid =
spawn_process(conn, p, blk.buf, blk.vars, fdin[0], fdout[1], dir);
3855 if (pid == (pid_t) -1) {
3857 "Cannot spawn CGI process [%s]: %s", prog, strerror(
ERRNO));
3873 fdin[0] = fdout[1] = -1;
3876 if ((in = fdopen(fdin[1],
"wb")) ==
NULL ||
3877 (out = fdopen(fdout[0],
"rb")) ==
NULL) {
3879 "fopen: %s", strerror(
ERRNO));
3888 if (!strcmp(conn->request_info.request_method,
"POST") &&
3906 "Not enough memory for buffer (%u bytes)",
3907 (
unsigned int) buflen);
3910 headers_len =
read_request(out, conn, buf, (
int) buflen, &data_len);
3911 if (headers_len <= 0) {
3913 "CGI program sent malformed or too big (>%u bytes) "
3914 "HTTP headers: [%.*s]",
3915 (
unsigned) buflen, data_len, buf);
3919 buf[headers_len - 1] =
'\0';
3925 conn->status_code = atoi(status);
3927 while (isdigit(* (
unsigned char *) status_text) || *status_text ==
' ') {
3931 conn->status_code = 302;
3933 conn->status_code = 200;
3935 connection_state =
get_header(&ri,
"Connection");
3936 if (connection_state ==
NULL ||
3938 conn->must_close = 1;
3940 (
void)
mg_printf(conn,
"HTTP/1.1 %d %s\r\n", conn->status_code,
3951 conn->num_bytes_sent +=
mg_write(conn, buf + headers_len,
3952 (
size_t)(data_len - headers_len));
3958 if (pid != (pid_t) -1) {
3960 #if !defined(_WIN32)
3963 while (waitpid(pid, &st, 0) != -1);
3967 if (fdin[0] != -1) {
3970 if (fdout[1] != -1) {
3976 }
else if (fdin[1] != -1) {
3982 }
else if (fdout[0] != -1) {
3994 static int put_dir(
struct mg_connection *conn,
const char *path)
4001 for (s = p = path + 2; (p = strchr(s,
'/')) !=
NULL; s = ++p) {
4002 len = (int)(p - path);
4003 if (len >= (
int)
sizeof(buf)) {
4007 memcpy(buf, path, len);
4026 static void mkcol(
struct mg_connection *conn,
const char *path)
4031 time_t curtime = time(
NULL);
4033 memset(&de.file, 0,
sizeof(de.file));
4034 if (!
mg_stat(conn, path, &de.file)) {
4035 mg_cry(conn,
"%s: mg_stat(%s) failed: %s",
4036 __func__, path, strerror(
ERRNO));
4039 if(de.file.modification_time) {
4041 "mkcol(%s): %s", path, strerror(
ERRNO));
4045 body_len = conn->data_len - conn->request_len;
4048 "mkcol(%s): %s", path, strerror(
ERRNO));
4055 conn->status_code = 201;
4057 mg_printf(conn,
"HTTP/1.1 %d Created\r\nDate: %s\r\nContent-Length: 0\r\nConnection: %s\r\n\r\n",
4059 }
else if (rc == -1) {
4062 "mkcol(%s): %s", path, strerror(
ERRNO));
4063 else if(errno == EACCES)
4065 "mkcol(%s): %s", path, strerror(
ERRNO));
4066 else if(errno == ENOENT)
4068 "mkcol(%s): %s", path, strerror(
ERRNO));
4071 "fopen(%s): %s", path, strerror(
ERRNO));
4075 static void put_file(
struct mg_connection *conn,
const char *path)
4082 time_t curtime = time(
NULL);
4084 conn->status_code =
mg_stat(conn, path, &file) ? 200 : 201;
4086 if ((rc =
put_dir(conn, path)) == 0) {
4088 mg_printf(conn,
"HTTP/1.1 %d OK\r\nDate: %s\r\nContent-Length: 0\r\nConnection: %s\r\n\r\n",
4090 }
else if (rc == -1) {
4092 "put_dir(%s): %s", path, strerror(
ERRNO));
4093 }
else if (!
mg_fopen(conn, path,
"wb+", &file) || file.fp ==
NULL) {
4096 "fopen(%s): %s", path, strerror(
ERRNO));
4102 conn->status_code = 206;
4103 fseeko(file.fp, r1, SEEK_SET);
4106 conn->status_code = 500;
4109 mg_printf(conn,
"HTTP/1.1 %d OK\r\nDate: %s\r\nContent-Length: 0\r\nConnection: %s\r\n\r\n",
4115 static void send_ssi_file(
struct mg_connection *,
const char *,
4116 struct file *,
int);
4119 char *tag,
int include_level)
4127 if (sscanf(tag,
" virtual=\"%[^\"]\"", file_name) == 1) {
4131 }
else if (sscanf(tag,
" abspath=\"%[^\"]\"", file_name) == 1) {
4135 }
else if (sscanf(tag,
" file=\"%[^\"]\"", file_name) == 1 ||
4136 sscanf(tag,
" \"%[^\"]\"", file_name) == 1) {
4139 if ((p = strrchr(path,
'/')) !=
NULL) {
4143 sizeof(path) - strlen(path),
"%s", file_name);
4145 mg_cry(conn,
"Bad SSI #include: [%s]", tag);
4149 if (!
mg_fopen(conn, path,
"rb", &file)) {
4150 mg_cry(conn,
"Cannot open SSI #include: [%s]: fopen(%s): %s",
4151 tag, path, strerror(
ERRNO));
4164 #if !defined(NO_POPEN)
4170 if (sscanf(tag,
" \"%[^\"]\"", cmd) != 1) {
4171 mg_cry(conn,
"Bad SSI #exec: [%s]", tag);
4172 }
else if ((file.fp = popen(cmd,
"r")) ==
NULL) {
4173 mg_cry(conn,
"Cannot SSI #exec: [%s]: %s", cmd, strerror(
ERRNO));
4183 if (filep->membuf !=
NULL && offset >=0 && offset < filep->size) {
4184 return ((
unsigned char *) filep->membuf)[offset];
4185 }
else if (filep->fp !=
NULL) {
4186 return fgetc(filep->fp);
4193 struct file *filep,
int include_level)
4196 int ch, offset, len, in_ssi_tag;
4198 if (include_level > 10) {
4199 mg_cry(conn,
"SSI #include level is too deep (%s)", path);
4203 in_ssi_tag = len = offset = 0;
4204 while ((ch =
mg_fgetc(filep, offset)) != EOF) {
4205 if (in_ssi_tag && ch ==
'>') {
4207 buf[len++] = (char) ch;
4209 assert(len <= (
int)
sizeof(buf));
4210 if (len < 6 || memcmp(buf,
"<!--#", 5) != 0) {
4214 if (!memcmp(buf + 5,
"include", 7)) {
4216 #if !defined(NO_POPEN)
4217 }
else if (!memcmp(buf + 5,
"exec", 4)) {
4221 mg_cry(conn,
"%s: unknown SSI " "command: \"%s\"", path, buf);
4225 }
else if (in_ssi_tag) {
4226 if (len == 5 && memcmp(buf,
"<!--#", 5) != 0) {
4229 }
else if (len == (
int)
sizeof(buf) - 2) {
4230 mg_cry(conn,
"%s: SSI tag is too large", path);
4233 buf[len++] = ch & 0xff;
4234 }
else if (ch ==
'<') {
4240 buf[len++] = ch & 0xff;
4242 buf[len++] = ch & 0xff;
4243 if (len == (
int)
sizeof(buf)) {
4261 time_t curtime = time(
NULL);
4263 if (!
mg_fopen(conn, path,
"rb", &file)) {
4267 conn->must_close = 1;
4272 "Content-Type: text/html\r\n"
4273 "Connection: %s\r\n\r\n",
4283 time_t curtime = time(
NULL);
4285 conn->status_code = 200;
4286 conn->must_close = 1;
4291 "Connection: %s\r\n"
4292 "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\n"
4305 "<d:href>%s</d:href>"
4308 "<d:resourcetype>%s</d:resourcetype>"
4309 "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
4310 "<d:getlastmodified>%s</d:getlastmodified>"
4312 "<d:status>HTTP/1.1 200 OK</d:status>"
4316 filep->is_directory ?
"<d:collection/>" :
"",
4325 struct mg_connection *conn = (
struct mg_connection *) data;
4327 conn->request_info.uri, de->file_name);
4337 time_t curtime = time(
NULL);
4341 conn->must_close = 1;
4342 conn->status_code = 207;
4343 mg_printf(conn,
"HTTP/1.1 207 Multi-Status\r\n"
4345 "Connection: %s\r\n"
4346 "Content-Type: text/xml; charset=utf-8\r\n\r\n",
4350 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
4351 "<d:multistatus xmlns:d='DAV:'>\n");
4357 if (filep->is_directory &&
4359 (depth ==
NULL || strcmp(depth,
"0") != 0)) {
4363 conn->num_bytes_sent +=
mg_printf(conn,
"%s\n",
"</d:multistatus>");
4368 (
void) pthread_mutex_lock(&conn->mutex);
4373 (
void) pthread_mutex_unlock(&conn->mutex);
4377 #include "mod_lua.inl"
4380 #if defined(USE_WEBSOCKET)
4384 #define SHA1HANDSOFF
4386 #include "solarisfixes.h"
4389 static int is_big_endian(
void)
4391 static const int n = 1;
4392 return ((
char *) &n)[0] == 0;
4395 union char64long16 {
4396 unsigned char c[64];
4400 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
4402 static uint32_t blk0(
union char64long16 *block,
int i)
4405 if (!is_big_endian()) {
4406 block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
4407 (rol(block->l[i], 8) & 0x00FF00FF);
4412 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
4413 ^block->l[(i+2)&15]^block->l[i&15],1))
4414 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
4415 #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
4416 #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
4417 #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
4418 #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
4423 unsigned char buffer[64];
4426 static void SHA1Transform(uint32_t state[5],
const unsigned char buffer[64])
4428 uint32_t
a, b,
c, d, e;
4429 union char64long16 block[1];
4431 memcpy(block, buffer, 64);
4522 a = b = c = d = e = 0;
4523 memset(block,
'\0',
sizeof(block));
4526 static void SHA1Init(SHA1_CTX* context)
4528 context->state[0] = 0x67452301;
4529 context->state[1] = 0xEFCDAB89;
4530 context->state[2] = 0x98BADCFE;
4531 context->state[3] = 0x10325476;
4532 context->state[4] = 0xC3D2E1F0;
4533 context->count[0] = context->count[1] = 0;
4536 static void SHA1Update(SHA1_CTX* context,
const unsigned char* data,
4541 j = context->count[0];
4542 if ((context->count[0] += len << 3) < j)
4543 context->count[1]++;
4544 context->count[1] += (len>>29);
4546 if ((j + len) > 63) {
4547 memcpy(&context->buffer[j], data, (i = 64-j));
4548 SHA1Transform(context->state, context->buffer);
4549 for ( ; i + 63 < len; i += 64) {
4550 SHA1Transform(context->state, &data[i]);
4554 memcpy(&context->buffer[j], &data[i], len - i);
4557 static void SHA1Final(
unsigned char digest[20], SHA1_CTX* context)
4560 unsigned char finalcount[8],
c;
4562 for (i = 0; i < 8; i++) {
4563 finalcount[i] = (
unsigned char)((context->count[(i >= 4 ? 0 : 1)]
4564 >> ((3-(i & 3)) * 8) ) & 255);
4567 SHA1Update(context, &c, 1);
4568 while ((context->count[0] & 504) != 448) {
4570 SHA1Update(context, &c, 1);
4572 SHA1Update(context, finalcount, 8);
4573 for (i = 0; i < 20; i++) {
4574 digest[i] = (
unsigned char)
4575 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
4577 memset(context,
'\0',
sizeof(*context));
4578 memset(&finalcount,
'\0',
sizeof(finalcount));
4582 static void base64_encode(
const unsigned char *src,
int src_len,
char *dst)
4584 static const char *b64 =
4585 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4588 for (i = j = 0; i < src_len; i += 3) {
4590 b = i + 1 >= src_len ? 0 : src[i + 1];
4591 c = i + 2 >= src_len ? 0 : src[i + 2];
4593 dst[j++] = b64[a >> 2];
4594 dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
4595 if (i + 1 < src_len) {
4596 dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
4598 if (i + 2 < src_len) {
4599 dst[j++] = b64[c & 63];
4602 while (j % 4 != 0) {
4608 static void send_websocket_handshake(
struct mg_connection *conn)
4610 static const char *magic =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
4611 char buf[100], sha[20], b64_sha[
sizeof(sha) * 2];
4617 SHA1Update(&sha_ctx, (
unsigned char *) buf, (uint32_t)strlen(buf));
4618 SHA1Final((
unsigned char *) sha, &sha_ctx);
4619 base64_encode((
unsigned char *) sha,
sizeof(sha), b64_sha);
4621 "HTTP/1.1 101 Switching Protocols\r\n"
4622 "Upgrade: websocket\r\n"
4623 "Connection: Upgrade\r\n"
4624 "Sec-WebSocket-Accept: ", b64_sha,
"\r\n\r\n");
4627 static void read_websocket(
struct mg_connection *conn)
4633 unsigned char *buf = (
unsigned char *) conn->buf + conn->request_len;
4640 size_t i, len, mask_len, data_len, header_len, body_len;
4644 unsigned char mask[4];
4655 assert(conn->content_len == 0);
4658 assert(conn->data_len >= conn->request_len);
4659 if ((body_len = conn->data_len - conn->request_len) >= 2) {
4661 mask_len = buf[1] & 128 ? 4 : 0;
4662 if (len < 126 && body_len >= mask_len) {
4664 header_len = 2 + mask_len;
4665 }
else if (len == 126 && body_len >= 4 + mask_len) {
4666 header_len = 4 + mask_len;
4667 data_len = ((((int) buf[2]) << 8) + buf[3]);
4668 }
else if (body_len >= 10 + mask_len) {
4669 header_len = 10 + mask_len;
4670 data_len = (((uint64_t) ntohl(* (uint32_t *) &buf[2])) << 32) +
4671 ntohl(* (uint32_t *) &buf[6]);
4675 if (header_len > 0) {
4678 if (data_len >
sizeof(mem)) {
4679 data = (
char *)
malloc(data_len);
4683 mg_cry(conn,
"websocket malloc() failed; closing connection");
4690 *(uint32_t*)mask = *(uint32_t*)(buf + header_len - mask_len);
4692 *(uint32_t*)mask = 0;
4697 assert(body_len >= header_len);
4698 if (data_len + header_len > body_len) {
4701 len = body_len - header_len;
4702 memcpy(data, buf + header_len, len);
4704 while (len < data_len) {
4705 int n =
pull(
NULL, conn, data + len, (
int)(data_len - len));
4713 mg_cry(conn,
"Websocket pull failed; closing connection");
4716 conn->data_len = conn->request_len;
4721 len = data_len + header_len;
4725 memcpy(data, buf + header_len, data_len);
4728 memmove(buf, buf + len, body_len - len);
4731 conn->data_len -= (int)len;
4736 for (i = 0; i < data_len; ++i) {
4737 data[i] ^= mask[i & 3];
4743 if ((conn->ctx->callbacks.websocket_data !=
NULL &&
4745 (conn->lua_websocket_state ==
NULL) &&
4747 !conn->ctx->callbacks.websocket_data(conn, mop, data, data_len)) ||
4749 (conn->lua_websocket_state &&
4750 !lua_websocket_data(conn, mop, data, data_len)) ||
4763 if ((n =
pull(
NULL, conn, conn->buf + conn->data_len,
4764 conn->buf_size - conn->data_len)) <= 0) {
4768 conn->data_len +=
n;
4773 int mg_websocket_write(
struct mg_connection* conn,
int opcode,
const char* data,
size_t dataLen)
4775 unsigned char header[10];
4776 size_t headerLen = 1;
4780 header[0] = 0x80 + (opcode & 0xF);
4783 if (dataLen < 126) {
4785 header[1] = (
unsigned char)dataLen;
4787 }
else if (dataLen <= 0xFFFF) {
4790 *(uint16_t*)(header + 2) = htons((uint16_t)dataLen);
4795 *(uint32_t*)(header + 2) = htonl((uint64_t)dataLen >> 32);
4796 *(uint32_t*)(header + 6) = htonl(dataLen & 0xFFFFFFFF);
4806 retval =
mg_write(conn, header, headerLen);
4807 retval =
mg_write(conn, data, dataLen);
4813 static void handle_websocket_request(
struct mg_connection *conn,
const char *path,
int is_script_resource)
4815 const char *version =
mg_get_header(conn,
"Sec-WebSocket-Version");
4817 int lua_websock, shared_lua_websock = 0;
4822 if (version ==
NULL || strcmp(version,
"13") != 0) {
4823 send_http_error(conn, 426,
"Upgrade Required",
"%s",
"Upgrade Required");
4824 }
else if (conn->ctx->callbacks.websocket_connect !=
NULL &&
4825 conn->ctx->callbacks.websocket_connect(conn) != 0) {
4830 lua_websock = conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS] ?
4831 match_prefix(conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS],
4832 (
int)strlen(conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS]),
4835 if (lua_websock || shared_lua_websock) {
4836 conn->lua_websocket_state = lua_websocket_new(path, conn, !!shared_lua_websock);
4837 if (conn->lua_websocket_state) {
4838 send_websocket_handshake(conn);
4839 if (lua_websocket_ready(conn)) {
4840 read_websocket(conn);
4847 send_websocket_handshake(conn);
4848 if (conn->ctx->callbacks.websocket_ready !=
NULL) {
4849 conn->ctx->callbacks.websocket_ready(conn);
4851 read_websocket(conn);
4856 static int is_websocket_request(
const struct mg_connection *conn)
4858 const char *host, *upgrade, *connection, *version, *key;
4866 return host !=
NULL && upgrade !=
NULL && connection !=
NULL &&
4875 return n >= 0 && n <= 255;
4878 static int parse_net(
const char *spec, uint32_t *net, uint32_t *mask)
4880 int n,
a, b,
c, d,
slash = 32, len = 0;
4882 if ((sscanf(spec,
"%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
4883 sscanf(spec,
"%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
4885 slash >= 0 && slash < 33) {
4887 *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d;
4888 *mask = slash ? 0xffffffffU << (32 -
slash) : 0;
4894 static int set_throttle(
const char *spec, uint32_t remote_ip,
const char *uri)
4897 struct vec vec, val;
4904 if (sscanf(val.ptr,
"%lf%c", &v, &mult) < 1 || v < 0 ||
4909 if (vec.len == 1 && vec.ptr[0] ==
'*') {
4911 }
else if (
parse_net(vec.ptr, &net, &mask) > 0) {
4912 if ((remote_ip & mask) == net) {
4915 }
else if (
match_prefix(vec.ptr, (
int)vec.len, uri) > 0) {
4925 return ntohl(* (uint32_t *) &conn->client.rsa.sin.sin_addr);
4928 int mg_upload(
struct mg_connection *conn,
const char *destination_dir)
4930 const char *content_type_header, *boundary_start;
4933 int bl,
n, i, j, headers_len, boundary_len, eof,
4934 len = 0, num_uploaded_files = 0;
4954 "boundary=")) ==
NULL ||
4955 (sscanf(boundary_start,
"boundary=\"%99[^\"]\"", boundary) == 0 &&
4956 sscanf(boundary_start,
"boundary=%99s", boundary) == 0) ||
4957 boundary[0] ==
'\0') {
4958 return num_uploaded_files;
4961 boundary_len = (int)strlen(boundary);
4962 bl = boundary_len + 4;
4965 assert(len >= 0 && len <= (
int)
sizeof(buf));
4966 while ((n =
mg_read(conn, buf + len,
sizeof(buf) - len)) > 0) {
4975 for (i = j = 0; i < headers_len; i++) {
4976 if (buf[i] ==
'\r' && buf[i + 1] ==
'\n') {
4977 buf[i] = buf[i + 1] =
'\0';
4987 if (fname[0] ==
'\0') {
4992 assert(len >= headers_len);
4993 memmove(buf, &buf[headers_len], len - headers_len);
5002 if ((s = strrchr(fname,
'/')) ==
NULL &&
5003 (s = strrchr(fname,
'\\')) ==
NULL) {
5008 snprintf(path,
sizeof(path),
"%s/%s", destination_dir, s);
5009 if ((fp = fopen(path,
"wb")) ==
NULL) {
5017 for (i = 0; i < len - bl; i++) {
5018 if (!memcmp(&buf[i],
"\r\n--", 4) &&
5019 !memcmp(&buf[i + 4], boundary, boundary_len)) {
5021 fwrite(buf, 1, i, fp);
5023 memmove(buf, &buf[i + bl], len - (i + bl));
5028 if (!eof && len > bl) {
5029 fwrite(buf, 1, len - bl, fp);
5030 memmove(buf, &buf[len - bl], bl);
5033 }
while (!eof && (n =
mg_read(conn, buf + len,
sizeof(buf) - len)) > 0);
5036 num_uploaded_files++;
5037 if (conn->ctx->callbacks.upload !=
NULL) {
5038 conn->ctx->callbacks.upload(conn, path);
5043 return num_uploaded_files;
5048 const char *s = conn->request_info.request_method;
5049 return s !=
NULL && (!strcmp(s,
"PUT") ||
5050 !strcmp(s,
"DELETE") ||
5051 !strcmp(s,
"MKCOL"));
5057 for (i = 0; idx == -1 && i < ctx->num_listening_sockets; i++) {
5058 idx = ctx->listening_sockets[i].is_ssl ? i : -1;
5066 const char *host_header;
5070 hostlen =
sizeof(host);
5071 if (host_header !=
NULL) {
5074 strncpy(host, host_header, hostlen);
5075 host[hostlen - 1] =
'\0';
5076 pos = strchr(host,
':');
5086 mg_printf(conn,
"HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s\r\n\r\n",
5087 host, (
int) ntohs(conn->ctx->listening_sockets[ssl_index].
5088 lsa.sin.sin_port), conn->request_info.uri);
5094 struct mg_request_handler_info *tmp_rh, *lastref = 0;
5095 size_t urilen = strlen(uri);
5098 for (tmp_rh = ctx->request_handlers;
5099 tmp_rh !=
NULL && strcmp(uri, tmp_rh->uri);
5100 lastref = tmp_rh, tmp_rh = tmp_rh->next) {
5102 if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri,uri)) {
5105 if (handler !=
NULL) {
5107 tmp_rh->handler = handler;
5108 tmp_rh->cbdata = cbdata;
5111 if (lastref !=
NULL)
5112 lastref->next = tmp_rh->next;
5114 ctx->request_handlers = tmp_rh->next;
5122 if (tmp_rh->uri_len < urilen
5123 && uri[tmp_rh->uri_len] ==
'/'
5124 && memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) {
5132 if (handler ==
NULL) {
5137 tmp_rh = (
struct mg_request_handler_info *)
malloc(
sizeof(
struct mg_request_handler_info));
5138 if (tmp_rh ==
NULL) {
5139 mg_cry(
fc(ctx),
"%s",
"Cannot create new request handler struct, OOM");
5143 tmp_rh->uri_len = strlen(uri);
5144 tmp_rh->handler = handler;
5145 tmp_rh->cbdata = cbdata;
5147 if (lastref ==
NULL) {
5148 tmp_rh->next = ctx->request_handlers;
5149 ctx->request_handlers = tmp_rh;
5151 tmp_rh->next = lastref->next;
5152 lastref->next = tmp_rh;
5160 const char *
uri = request_info->
uri;
5161 size_t urilen = strlen(uri);
5162 struct mg_request_handler_info *tmp_rh = conn->ctx->request_handlers;
5164 for (; tmp_rh !=
NULL; tmp_rh = tmp_rh->next) {
5167 if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri,uri)) {
5168 return tmp_rh->handler(conn, tmp_rh->cbdata);
5173 if (tmp_rh->uri_len < urilen
5174 && uri[tmp_rh->uri_len] ==
'/'
5175 && memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) {
5177 return tmp_rh->handler(conn, tmp_rh->cbdata);
5181 if (
match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0) {
5182 return tmp_rh->handler(conn, tmp_rh->cbdata);
5198 int uri_len, ssl_index, is_script_resource;
5201 time_t curtime = time(
NULL);
5203 if ((conn->request_info.query_string = strchr(ri->
uri,
'?')) !=
NULL) {
5204 * ((
char *) conn->request_info.query_string++) =
'\0';
5206 uri_len = (int) strlen(ri->
uri);
5218 if (!conn->client.is_ssl && conn->client.ssl_redir &&
5224 }
else if (conn->ctx->callbacks.begin_request !=
NULL &&
5225 conn->ctx->callbacks.begin_request(conn)) {
5227 #if defined(USE_WEBSOCKET)
5228 }
else if (is_websocket_request(conn)) {
5229 handle_websocket_request(conn, path, is_script_resource);
5231 }
else if (conn->ctx->request_handlers !=
NULL &&
5234 }
else if (!is_script_resource && !strcmp(ri->
request_method,
"OPTIONS")) {
5241 }
else if (!is_script_resource && !strcmp(ri->
request_method,
"PUT")) {
5243 }
else if (!is_script_resource && !strcmp(ri->
request_method,
"MKCOL")) {
5245 }
else if (!is_script_resource && !strcmp(ri->
request_method,
"DELETE")) {
5247 memset(&de.file, 0,
sizeof(de.file));
5248 if(!
mg_stat(conn, path, &de.file)) {
5251 if(de.file.modification_time) {
5252 if(de.file.is_directory) {
5266 }
else if ((file.membuf ==
NULL && file.modification_time == (time_t) 0) ||
5269 }
else if (file.is_directory && ri->
uri[uri_len - 1] !=
'/') {
5271 mg_printf(conn,
"HTTP/1.1 301 Moved Permanently\r\n"
5274 "Content-Length: 0\r\n"
5275 "Connection: %s\r\n\r\n",
5277 }
else if (!is_script_resource && !strcmp(ri->
request_method,
"PROPFIND")) {
5279 }
else if (file.is_directory &&
5285 "Directory listing denied");
5288 }
else if (
match_prefix(conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
5289 (
int)strlen(conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS]),
5292 handle_lsp_request(conn, path, &file,
NULL);
5293 }
else if (
match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS],
5294 (
int)strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]),
5297 mg_exec_lua_script(conn, path,
NULL);
5299 #if !defined(NO_CGI)
5328 for (i = 0; i < ctx->num_listening_sockets; i++) {
5331 free(ctx->listening_sockets);
5336 return port < 0xffff;
5344 unsigned int a, b,
c, d, ch, len, port;
5345 #if defined(USE_IPV6)
5352 memset(so, 0,
sizeof(*so));
5353 so->lsa.sin.sin_family = AF_INET;
5355 if (sscanf(vec->ptr,
"%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
5357 so->lsa.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
5358 so->lsa.sin.sin_port = htons((uint16_t) port);
5359 #if defined(USE_IPV6)
5361 }
else if (sscanf(vec->ptr,
"[%49[^]]]:%d%n", buf, &port, &len) == 2 &&
5362 inet_pton(AF_INET6, buf, &so->lsa.sin6.sin6_addr)) {
5364 so->lsa.sin6.sin6_family = AF_INET6;
5365 so->lsa.sin6.sin6_port = htons((uint16_t) port);
5367 }
else if (sscanf(vec->ptr,
"%u%n", &port, &len) == 1) {
5369 so->lsa.sin.sin_port = htons((uint16_t) port);
5375 so->is_ssl = ch ==
's';
5376 so->ssl_redir = ch ==
'r';
5380 (ch ==
'\0' || ch ==
's' || ch ==
'r' || ch ==
',');
5386 int on = 1, success = 1;
5387 #if defined(USE_IPV6)
5391 struct socket so, *ptr;
5394 struct sockaddr_in sin;
5397 memset(&sin, 0,
sizeof(sin));
5402 mg_cry(
fc(ctx),
"%s: %.*s: invalid port spec. Expecting list of: %s",
5403 __func__, (
int) vec.len, vec.ptr,
"[IP_ADDRESS:]PORT[s|r]");
5405 }
else if (so.is_ssl && ctx->ssl_ctx ==
NULL) {
5406 mg_cry(
fc(ctx),
"Cannot add SSL socket, is -ssl_certificate option set?");
5408 }
else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
5412 setsockopt(so.sock, SOL_SOCKET, SO_REUSEADDR,
5413 (
void *) &on,
sizeof(on)) != 0 ||
5414 #
if defined(USE_IPV6)
5415 (so.lsa.sa.sa_family == AF_INET6 &&
5416 setsockopt(so.sock, IPPROTO_IPV6, IPV6_V6ONLY, (
void *) &off,
5417 sizeof(off)) != 0) ||
5419 bind(so.sock, &so.lsa.sa, so.lsa.sa.sa_family == AF_INET ?
5420 sizeof(so.lsa.sin) :
sizeof(so.lsa)) != 0 ||
5422 getsockname(so.sock, (
struct sockaddr *)&sin, &len) != 0) {
5423 mg_cry(
fc(ctx),
"%s: cannot bind to %.*s: %d (%s)", __func__,
5424 (
int) vec.len, vec.ptr,
ERRNO, strerror(errno));
5429 }
else if ((ptr = (
struct socket *) realloc(ctx->listening_sockets,
5430 (ctx->num_listening_sockets + 1) *
5431 sizeof(ctx->listening_sockets[0]))) ==
NULL) {
5434 }
else if ((portPtr = (in_port_t*) realloc(ctx->listening_ports,
5435 (ctx->num_listening_sockets + 1) *
5436 sizeof(ctx->listening_ports[0]))) ==
NULL) {
5442 ctx->listening_sockets = ptr;
5443 ctx->listening_sockets[ctx->num_listening_sockets] = so;
5444 ctx->listening_ports = portPtr;
5445 ctx->listening_ports[ctx->num_listening_sockets] = ntohs(sin.sin_port);
5446 ctx->num_listening_sockets++;
5457 static void log_header(
const struct mg_connection *conn,
const char *header,
5460 const char *header_value;
5463 (
void) fprintf(fp,
"%s",
" -");
5465 (
void) fprintf(fp,
" \"%s\"", header_value);
5482 tm = localtime(&conn->birth_time);
5484 strftime(date,
sizeof(date),
"%d/%b/%Y:%H:%M:%S %z", tm);
5486 strncpy(date,
"01/Jan/1970:00:00:00 +0000",
sizeof(date));
5487 date[
sizeof(date) - 1] =
'\0';
5490 ri = &conn->request_info;
5494 fprintf(fp,
"%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
5498 conn->status_code, conn->num_bytes_sent);
5510 static int check_acl(
struct mg_context *ctx, uint32_t remote_ip)
5518 allowed = list ==
NULL ?
'+' :
'-';
5522 if ((flag !=
'+' && flag !=
'-') ||
5523 parse_net(&vec.ptr[1], &net, &mask) == 0) {
5524 mg_cry(
fc(ctx),
"%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
5528 if (net == (remote_ip & mask)) {
5533 return allowed ==
'+';
5536 #if !defined(_WIN32)
5546 if ((pw = getpwnam(uid)) ==
NULL) {
5547 mg_cry(
fc(ctx),
"%s: unknown user [%s]", __func__, uid);
5548 }
else if (setgid(pw->
pw_gid) == -1) {
5549 mg_cry(
fc(ctx),
"%s: setgid(%s): %s", __func__, uid, strerror(errno));
5550 }
else if (setuid(pw->
pw_uid) == -1) {
5551 mg_cry(
fc(ctx),
"%s: setuid(%s): %s", __func__, uid, strerror(errno));
5561 #if !defined(NO_SSL)
5567 SSL_set_fd(conn->ssl, conn->client.sock) == 1 &&
5568 func(conn->ssl) == 1;
5586 (
void) pthread_mutex_lock(&ssl_mutexes[mutex_num]);
5588 (
void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
5594 return (
unsigned long) pthread_self();
5597 #if !defined(NO_SSL_DL)
5598 static void *
load_dll(
struct mg_context *ctx,
const char *dll_name,
5599 struct ssl_func *sw)
5606 struct ssl_func *fp;
5608 if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) ==
NULL) {
5609 mg_cry(
fc(ctx),
"%s: cannot load %s", __func__, dll_name);
5613 for (fp = sw; fp->name !=
NULL; fp++) {
5616 u.fp = (
void (*)(
void)) dlsym(dll_handle, fp->name);
5621 u.p = dlsym(dll_handle, fp->name);
5624 mg_cry(
fc(ctx),
"%s: %s: cannot find %s", __func__, dll_name, fp->name);
5625 dlclose(dll_handle);
5645 ctx->callbacks.init_ssl ==
NULL) {
5649 #if !defined(NO_SSL_DL)
5652 if (!ctx->ssllib_dll_handle || !ctx->cryptolib_dll_handle) {
5668 if ((ctx->callbacks.init_ssl ==
NULL ||
5669 !ctx->callbacks.init_ssl(ctx->ssl_ctx, ctx->user_data)) &&
5683 if ((ssl_mutexes = (pthread_mutex_t *)
malloc((
size_t)size)) ==
NULL) {
5689 pthread_mutex_init(&ssl_mutexes[i],
NULL);
5701 if (ctx->ssl_ctx !=
NULL) {
5704 pthread_mutex_destroy(&ssl_mutexes[i]);
5717 mg_cry(
fc(ctx),
"Cannot open %s: %s", path, strerror(
ERRNO));
5725 return check_acl(ctx, (uint32_t) 0x7f000001UL) != -1;
5730 conn->path_info =
NULL;
5731 conn->num_bytes_sent = conn->consumed_content = 0;
5732 conn->status_code = -1;
5733 conn->must_close = conn->request_len = conn->throttle = 0;
5742 struct linger linger;
5747 linger.l_linger = 1;
5748 if (setsockopt(conn->client.sock, SOL_SOCKET, SO_LINGER,
5749 (
char *) &linger,
sizeof(linger)) != 0) {
5750 mg_cry(conn,
"%s: setsockopt(SOL_SOCKET SO_LINGER) failed: %s",
5751 __func__, strerror(
ERRNO));
5755 shutdown(conn->client.sock, SHUT_WR);
5765 n =
pull(
NULL, conn, buf,
sizeof(buf));
5775 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
5776 if (conn->lua_websocket_state) {
5777 lua_websocket_close(conn);
5782 if (conn->ctx->callbacks.connection_close !=
NULL)
5783 conn->ctx->callbacks.connection_close(conn);
5787 conn->must_close = 1;
5790 if (conn->ssl !=
NULL) {
5808 if (conn->client_ssl_ctx !=
NULL) {
5813 (
void) pthread_mutex_destroy(&conn->mutex);
5817 struct mg_connection *
mg_connect(
const char *host,
int port,
int use_ssl,
5818 char *ebuf,
size_t ebuf_len);
5820 struct mg_connection *
mg_connect(
const char *host,
int port,
int use_ssl,
5821 char *ebuf,
size_t ebuf_len)
5823 static struct mg_context fake_ctx;
5824 struct mg_connection *conn =
NULL;
5827 if ((sock =
conn2(&fake_ctx, host, port, use_ssl, ebuf,
5829 }
else if ((conn = (
struct mg_connection *)
5831 snprintf(ebuf, ebuf_len,
"calloc(): %s", strerror(
ERRNO));
5834 }
else if (use_ssl && (conn->client_ssl_ctx =
5836 snprintf(ebuf, ebuf_len,
"SSL_CTX_new error");
5842 socklen_t len =
sizeof(
struct sockaddr);
5844 conn->buf = (
char *) (conn + 1);
5845 conn->ctx = &fake_ctx;
5846 conn->client.sock = sock;
5847 if (getsockname(sock, &conn->client.rsa.sa, &len) != 0) {
5848 mg_cry(conn,
"%s: getsockname() failed: %s",
5849 __func__, strerror(
ERRNO));
5851 conn->client.is_ssl = use_ssl;
5852 (
void) pthread_mutex_init(&conn->mutex,
NULL);
5872 return uri[0] ==
'/' || (uri[0] ==
'*' && uri[1] ==
'\0');
5875 static int getreq(
struct mg_connection *conn,
char *ebuf,
size_t ebuf_len)
5883 assert(conn->request_len < 0 || conn->data_len >= conn->request_len);
5885 if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
5886 snprintf(ebuf, ebuf_len,
"%s",
"Request Too Large");
5887 }
else if (conn->request_len <= 0) {
5888 snprintf(ebuf, ebuf_len,
"%s",
"Client closed connection");
5890 &conn->request_info) <= 0) {
5891 snprintf(ebuf, ebuf_len,
"Bad request: [%.*s]", conn->data_len, conn->buf);
5894 if ((cl =
get_header(&conn->request_info,
"Content-Length")) !=
NULL) {
5895 conn->content_len = strtoll(cl,
NULL, 10);
5896 }
else if (!
mg_strcasecmp(conn->request_info.request_method,
"POST") ||
5898 conn->content_len = -1;
5900 conn->content_len = 0;
5902 conn->birth_time = time(
NULL);
5904 return ebuf[0] ==
'\0';
5907 struct mg_connection *
mg_download(
const char *host,
int port,
int use_ssl,
5908 char *ebuf,
size_t ebuf_len,
5909 const char *fmt, ...)
5911 struct mg_connection *conn;
5916 if ((conn =
mg_connect(host, port, use_ssl, ebuf, ebuf_len)) ==
NULL) {
5918 snprintf(ebuf, ebuf_len,
"%s",
"Error sending request");
5920 getreq(conn, ebuf, ebuf_len);
5922 if (ebuf[0] !=
'\0' && conn !=
NULL) {
5934 int keep_alive_enabled, keep_alive, discard_len;
5944 if (!
getreq(conn, ebuf,
sizeof(ebuf))) {
5946 conn->must_close = 1;
5948 snprintf(ebuf,
sizeof(ebuf),
"Invalid URI: [%s]", ri->
uri);
5952 snprintf(ebuf,
sizeof(ebuf),
"Bad HTTP version: [%s]", ri->
http_version);
5956 if (ebuf[0] ==
'\0') {
5958 if (conn->ctx->callbacks.end_request !=
NULL) {
5959 conn->ctx->callbacks.end_request(conn, conn->status_code);
5974 keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled &&
5978 discard_len = conn->content_len >= 0 && conn->request_len > 0 &&
5979 conn->request_len + conn->content_len < (int64_t) conn->data_len ?
5980 (
int) (conn->request_len + conn->content_len) : conn->data_len;
5981 assert(discard_len >= 0);
5982 memmove(conn->buf, conn->buf + discard_len, conn->data_len - discard_len);
5983 conn->data_len -= discard_len;
5984 assert(conn->data_len >= 0);
5985 assert(conn->data_len <= conn->buf_size);
5986 }
while (keep_alive);
5992 (
void) pthread_mutex_lock(&ctx->mutex);
5996 while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
5997 pthread_cond_wait(&ctx->sq_full, &ctx->mutex);
6001 if (ctx->sq_head > ctx->sq_tail) {
6003 *sp = ctx->queue[ctx->sq_tail %
ARRAY_SIZE(ctx->queue)];
6005 DEBUG_TRACE((
"grabbed socket %d, going busy", sp->sock));
6008 while (ctx->sq_tail > (
int)
ARRAY_SIZE(ctx->queue)) {
6014 (
void) pthread_cond_signal(&ctx->sq_empty);
6015 (
void) pthread_mutex_unlock(&ctx->mutex);
6017 return !ctx->stop_flag;
6022 struct mg_context *ctx = (
struct mg_context *) thread_func_param;
6023 struct mg_connection *conn;
6024 struct mg_workerTLS tls;
6027 #if defined(_WIN32) && !defined(__SYMBIAN32__)
6028 tls.pthread_cond_helper_mutex = CreateEvent(
NULL, FALSE, FALSE,
NULL);
6031 conn = (
struct mg_connection *) calloc(1,
sizeof(*conn) +
MAX_REQUEST_SIZE);
6033 mg_cry(
fc(ctx),
"%s",
"Cannot create new connection struct, OOM");
6035 pthread_setspecific(sTlsKey, &tls);
6037 conn->buf = (
char *) (conn + 1);
6039 conn->request_info.user_data = ctx->user_data;
6042 (
void) pthread_mutex_init(&conn->mutex,
NULL);
6048 conn->birth_time = time(
NULL);
6054 conn->request_info.remote_port = ntohs(conn->client.rsa.sin.sin_port);
6055 memcpy(&conn->request_info.remote_ip,
6056 &conn->client.rsa.sin.sin_addr.s_addr, 4);
6057 conn->request_info.remote_ip = ntohl(conn->request_info.remote_ip);
6058 conn->request_info.is_ssl = conn->client.is_ssl;
6060 if (!conn->client.is_ssl
6073 (
void) pthread_mutex_lock(&ctx->mutex);
6075 (
void) pthread_cond_signal(&ctx->cond);
6076 assert(ctx->num_threads >= 0);
6077 (
void) pthread_mutex_unlock(&ctx->mutex);
6079 pthread_setspecific(sTlsKey, 0);
6080 #if defined(_WIN32) && !defined(__SYMBIAN32__)
6081 CloseHandle(tls.pthread_cond_helper_mutex);
6092 static unsigned __stdcall
worker_thread(
void *thread_func_param)
6108 (
void) pthread_mutex_lock(&ctx->mutex);
6111 while (ctx->stop_flag == 0 &&
6112 ctx->sq_head - ctx->sq_tail >= (
int)
ARRAY_SIZE(ctx->queue)) {
6113 (
void) pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);
6116 if (ctx->sq_head - ctx->sq_tail < (
int)
ARRAY_SIZE(ctx->queue)) {
6118 ctx->queue[ctx->sq_head %
ARRAY_SIZE(ctx->queue)] = *sp;
6123 (
void) pthread_cond_signal(&ctx->sq_full);
6124 (
void) pthread_mutex_unlock(&ctx->mutex);
6130 DWORD t = milliseconds;
6133 t.tv_sec = milliseconds / 1000;
6134 t.tv_usec = (milliseconds * 1000) % 1000000;
6136 return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (
void *) &t,
sizeof(t)) ||
6137 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (
void *) &t,
sizeof(t));
6141 struct mg_context *ctx)
6145 socklen_t len =
sizeof(so.rsa);
6148 if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) ==
INVALID_SOCKET) {
6149 }
else if (!
check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) {
6151 mg_cry(
fc(ctx),
"%s: %s is not allowed to connect", __func__, src_addr);
6155 DEBUG_TRACE((
"Accepted socket %d", (
int) so.sock));
6157 so.is_ssl = listener->is_ssl;
6158 so.ssl_redir = listener->ssl_redir;
6159 if (getsockname(so.sock, &so.lsa.sa, &len) != 0) {
6160 mg_cry(
fc(ctx),
"%s: getsockname() failed: %s",
6161 __func__, strerror(
ERRNO));
6169 if (setsockopt(so.sock, SOL_SOCKET, SO_KEEPALIVE, (
void *) &on,
6172 "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
6173 __func__, strerror(
ERRNO));
6182 struct mg_context *ctx = (
struct mg_context *) thread_func_param;
6183 struct mg_workerTLS tls;
6186 int workerthreadcount;
6190 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
6191 #elif defined(USE_MASTER_THREAD_PRIORITY)
6192 int min_prio = sched_get_priority_min(SCHED_RR);
6193 int max_prio = sched_get_priority_max(SCHED_RR);
6194 if ((min_prio >=0) && (max_prio >= 0) &&
6195 ((USE_MASTER_THREAD_PRIORITY) <= max_prio) &&
6196 ((USE_MASTER_THREAD_PRIORITY) >= min_prio)
6198 struct sched_param sched_param = {0};
6199 sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY);
6200 pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
6204 #if defined(_WIN32) && !defined(__SYMBIAN32__)
6205 tls.pthread_cond_helper_mutex = CreateEvent(
NULL, FALSE, FALSE,
NULL);
6208 pthread_setspecific(sTlsKey, &tls);
6210 pfd = (
struct pollfd *) calloc(ctx->num_listening_sockets,
sizeof(pfd[0]));
6211 while (pfd !=
NULL && ctx->stop_flag == 0) {
6212 for (i = 0; i < ctx->num_listening_sockets; i++) {
6213 pfd[i].fd = ctx->listening_sockets[i].sock;
6214 pfd[i].events = POLLIN;
6217 if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
6218 for (i = 0; i < ctx->num_listening_sockets; i++) {
6224 if (ctx->stop_flag == 0 && (pfd[i].revents & POLLIN)) {
6237 pthread_cond_broadcast(&ctx->sq_full);
6240 (
void) pthread_mutex_lock(&ctx->mutex);
6241 while (ctx->num_threads > 0) {
6242 (
void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
6244 (
void) pthread_mutex_unlock(&ctx->mutex);
6247 workerthreadcount = ctx->workerthreadcount;
6248 for (i = 0; i < workerthreadcount; i++) {
6253 (
void) pthread_mutex_destroy(&ctx->mutex);
6254 (
void) pthread_cond_destroy(&ctx->cond);
6255 (
void) pthread_cond_destroy(&ctx->sq_empty);
6256 (
void) pthread_cond_destroy(&ctx->sq_full);
6258 #if !defined(NO_SSL)
6263 #if defined(_WIN32) && !defined(__SYMBIAN32__)
6264 CloseHandle(tls.pthread_cond_helper_mutex);
6266 pthread_setspecific(sTlsKey, 0);
6277 static unsigned __stdcall
master_thread(
void *thread_func_param)
6293 struct mg_request_handler_info *tmp_rh;
6300 if (ctx->config[i] !=
NULL)
6302 #pragma warning(suppress: 6001)
6304 free(ctx->config[i]);
6308 while (ctx->request_handlers) {
6309 tmp_rh = ctx->request_handlers;
6310 ctx->request_handlers = tmp_rh->next;
6317 if (ctx->ssl_ctx !=
NULL) {
6320 if (ssl_mutexes !=
NULL) {
6327 if (ctx->workerthreadids !=
NULL) {
6328 free(ctx->workerthreadids);
6334 pthread_key_delete(sTlsKey);
6346 while (ctx->stop_flag != 2) {
6352 #if defined(_WIN32) && !defined(__SYMBIAN32__)
6353 (
void) WSACleanup();
6359 const char **options)
6361 struct mg_context *ctx;
6362 const char *
name, *
value, *default_value;
6364 int workerthreadcount;
6366 #if defined(_WIN32) && !defined(__SYMBIAN32__)
6368 WSAStartup(MAKEWORD(2,2), &data);
6369 #pragma warning(suppress: 28125)
6370 InitializeCriticalSection(&global_log_file_lock);
6379 if ((ctx = (
struct mg_context *) calloc(1,
sizeof(*ctx))) ==
NULL) {
6384 if (0 != pthread_key_create(&sTlsKey,
NULL)) {
6385 mg_cry(
fc(ctx),
"Cannot initialize thread local storage");
6392 ctx->callbacks = *callbacks;
6393 ctx->user_data = user_data;
6394 ctx->request_handlers = 0;
6396 while (options && (name = *options++) !=
NULL) {
6398 mg_cry(
fc(ctx),
"Invalid option: %s", name);
6401 }
else if ((value = *options++) ==
NULL) {
6402 mg_cry(
fc(ctx),
"%s: option value cannot be NULL", name);
6406 if (ctx->config[i] !=
NULL) {
6407 mg_cry(
fc(ctx),
"warning: %s: duplicate option", name);
6408 free(ctx->config[i]);
6415 for (i = 0; config_options[i * 2] !=
NULL; i++) {
6416 default_value = config_options[i * 2 + 1];
6417 if (ctx->config[i] ==
NULL && default_value !=
NULL) {
6418 ctx->config[i] =
mg_strdup(default_value);
6425 #
if !defined(NO_SSL)
6429 #
if !defined(_WIN32)
6437 #if !defined(_WIN32) && !defined(__SYMBIAN32__)
6440 (
void) signal(SIGPIPE, SIG_IGN);
6443 (
void) pthread_mutex_init(&ctx->mutex,
NULL);
6444 (
void) pthread_cond_init(&ctx->cond,
NULL);
6445 (
void) pthread_cond_init(&ctx->sq_empty,
NULL);
6446 (
void) pthread_cond_init(&ctx->sq_full,
NULL);
6448 workerthreadcount = atoi(ctx->config[
NUM_THREADS]);
6451 mg_cry(
fc(ctx),
"Too many worker threads");
6456 if (workerthreadcount > 0) {
6457 ctx->workerthreadcount = workerthreadcount;
6458 ctx->workerthreadids = calloc(workerthreadcount,
sizeof(pthread_t));
6459 if (ctx->workerthreadids ==
NULL) {
6460 mg_cry(
fc(ctx),
"Not enough memory for worker thread ID array");
6470 for (i = 0; i < workerthreadcount; i++) {
6471 (
void) pthread_mutex_lock(&ctx->mutex);
6473 (
void) pthread_mutex_unlock(&ctx->mutex);
6475 &ctx->workerthreadids[i]) != 0) {
6476 (
void) pthread_mutex_lock(&ctx->mutex);
6478 (
void) pthread_mutex_unlock(&ctx->mutex);
6479 mg_cry(
fc(ctx),
"Cannot start worker thread: %ld", (
long)
ERRNO);
static int is_file_in_memory(struct mg_connection *conn, const char *path, struct file *filep)
int mg_get_var2(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len, size_t occurrence)
#define MAX_CGI_ENVIR_VARS
static int set_ports_option(struct mg_context *ctx)
static const char * next_option(const char *list, struct vec *val, struct vec *eq_val)
static void mg_fclose(struct file *filep)
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
static void close_all_listening_sockets(struct mg_context *ctx)
double read(const std::string &file_name)
reading
struct md5_state_s md5_state_t
size_t mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl)
static int match_prefix(const char *pattern, int pattern_len, const char *str)
int mg_websocket_write(struct mg_connection *conn, int opcode, const char *data, size_t data_len)
static int authorize(struct mg_connection *conn, struct file *filep)
static int is_valid_port(unsigned int port)
#define CRYPTO_set_id_callback
int(* mg_request_handler)(struct mg_connection *conn, void *cbdata)
static int num_leap_years(int year)
static char * mg_strndup(const char *ptr, size_t len)
static const char * ssl_error(void)
int mg_modify_passwords_file(const char *fname, const char *domain, const char *user, const char *pass)
#define SSL_CTX_use_certificate_chain_file
int mg_start_thread(mg_thread_func_t func, void *param)
static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len)
static void prepare_cgi_environment(struct mg_connection *conn, const char *prog, struct cgi_env_block *blk)
static void gmt_time_string(char *buf, size_t buf_len, time_t *t)
ClassImp(TSeqCollection) Int_t TSeqCollection TIter next(this)
Return index of object in collection.
static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level)
#define INVALID_HANDLE_VALUE
static int check_acl(struct mg_context *ctx, uint32_t remote_ip)
#define PRINTF_ARGS(x, y)
static int set_uid_option(struct mg_context *ctx)
RooArgList L(const RooAbsArg &v1)
static int set_acl_option(struct mg_context *ctx)
static int consume_socket(struct mg_context *ctx, struct socket *sp)
int mg_read(struct mg_connection *conn, void *buf, size_t len)
static void bin2str(char *to, const unsigned char *p, size_t len)
static void convert_uri_to_file_name(struct mg_connection *conn, char *buf, size_t buf_len, struct file *filep, int *is_script_ressource)
static void print_props(struct mg_connection *conn, const char *uri, struct file *filep)
static void produce_socket(struct mg_context *ctx, const struct socket *sp)
const char * mg_version(void)
static void handle_cgi_request(struct mg_connection *conn, const char *prog)
static void handle_request(struct mg_connection *conn)
static SOCKET conn2(struct mg_context *ctx, const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len)
static void log_header(const struct mg_connection *conn, const char *header, FILE *fp)
static const struct @204 builtin_mime_types[]
static void reset_per_request_attributes(struct mg_connection *conn)
void mg_stop(struct mg_context *ctx)
static void remove_double_dots_and_double_slashes(char *s)
const struct mg_context * mg_get_context(const struct mg_connection *conn)
#define ARRAY_SIZE(array)
static int get_option_index(const char *name)
static int is_valid_uri(const char *uri)
static const char * get_header(const struct mg_request_info *ri, const char *name)
#define MAX_WORKER_THREADS
int mg_upload(struct mg_connection *conn, const char *destination_dir)
int mg_get_cookie(const char *cookie_header, const char *var_name, char *dst, size_t dst_size)
struct mg_request_info::mg_header http_headers[64]
static void * master_thread(void *thread_func_param)
static int use_request_handler(struct mg_connection *conn)
static int parse_range_header(const char *header, int64_t *a, int64_t *b)
#define CGI_ENVIRONMENT_SIZE
static void set_close_on_exec(int fd, struct mg_connection *conn)
static pthread_mutex_t * ssl_mutexes
static void * load_dll(struct mg_context *ctx, const char *dll_name, struct ssl_func *sw)
static void close_connection(struct mg_connection *conn)
static void close_socket_gracefully(struct mg_connection *conn)
static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
static int check_authorization(struct mg_connection *conn, const char *path)
const char * mg_get_option(const struct mg_context *ctx, const char *name)
#define PRINTF_FORMAT_STRING(s)
static void parse_http_headers(char **buf, struct mg_request_info *ri)
static struct mg_connection * fc(struct mg_context *ctx)
static int read_request(FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread)
static int mg_start_thread_with_id(mg_thread_func_t func, void *param, pthread_t *threadidptr)
static void dir_scan_callback(struct de *de, void *data)
const char * request_method
static const char * mg_strcasestr(const char *big_str, const char *small_str)
Vc_ALWAYS_INLINE void free(T *p)
Frees memory that was allocated with Vc::malloc.
static int mg_strcasecmp(const char *s1, const char *s2)
static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen, const char *fmt, va_list ap)
MD5_STATIC void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
static void send_http_error(struct mg_connection *, int, const char *, PRINTF_FORMAT_STRING(const char *fmt),...)
static void handle_propfind(struct mg_connection *conn, const char *path, struct file *filep)
static int is_authorized_for_put(struct mg_connection *conn)
void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata)
static char * skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar)
static double p2(double t, double a, double b, double c)
static int is_put_or_delete_request(const struct mg_connection *conn)
static void send_authorization_request(struct mg_connection *conn)
if(pyself &&pyself!=Py_None)
static int is_not_modified(const struct mg_connection *conn, const struct file *filep)
static int parse_net(const char *spec, uint32_t *net, uint32_t *mask)
#define SSL_CTX_use_PrivateKey_file
static int alloc_vprintf2(char **buf, const char *fmt, va_list ap)
static void master_thread_run(void *thread_func_param)
static const char * suggest_connection_header(const struct mg_connection *conn)
static int get_request_len(const char *buf, int buflen)
static int mg_fgetc(struct file *filep, int offset)
void mg_send_file(struct mg_connection *conn, const char *path)
static int set_non_blocking_mode(SOCKET sock)
#define SSLv23_client_method
struct mg_connection * mg_connect(const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len)
static void print_dav_dir_entry(struct de *de, void *data)
static int get_first_ssl_listener_index(const struct mg_context *ctx)
#define SSL_CTX_use_certificate_file
static char * mg_fgets(char *buf, size_t size, struct file *filep, char **p)
static int mg_join_thread(pthread_t threadid)
static int scan_directory(struct mg_connection *conn, const char *dir, void *data, void(*cb)(struct de *, void *))
int mg_printf(struct mg_connection *conn, const char *fmt,...)
static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(4
char * mg_md5(char buf[33],...)
static int set_ssl_option(struct mg_context *ctx)
static time_t parse_date_string(const char *datetime)
static int is_file_opened(const struct file *filep)
static void mkcol(struct mg_connection *conn, const char *path)
static int WINCDECL compare_dir_entries(const void *p1, const void *p2)
static void uninitialize_ssl(struct mg_context *ctx)
static void * worker_thread_run(void *thread_func_param)
void mg_unlock(struct mg_connection *conn)
static int substitute_index_file(struct mg_connection *conn, char *path, size_t path_len, struct file *filep)
struct ssl_method_st SSL_METHOD
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)
int mg_url_encode(const char *src, char *dst, size_t dst_len)
unsigned int r1[N_CITIES]
static char * addenv(struct cgi_env_block *block, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
static void ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
static double p1(double t, double a, double b)
static int put_dir(struct mg_connection *conn, const char *path)
static int must_hide_file(struct mg_connection *conn, const char *path)
static int mg_stat(struct mg_connection *conn, const char *path, struct file *filep)
const char * http_version
static void put_file(struct mg_connection *conn, const char *path)
struct mg_context * mg_start(const struct mg_callbacks *callbacks, void *user_data, const char **options)
static const char * config_options[]
int mg_write(struct mg_connection *conn, const void *buf, size_t len)
static int remove_directory(struct mg_connection *conn, const char *dir)
static void handle_ssi_file_request(struct mg_connection *conn, const char *path)
void mg_cry(struct mg_connection *conn, const char *fmt,...)
static void send_ssi_file(struct mg_connection *, const char *, struct file *, int)
static int should_keep_alive(const struct mg_connection *conn)
void mg_close_connection(struct mg_connection *conn)
static void handle_directory_request(struct mg_connection *conn, const char *dir)
static int lowercase(const char *s)
struct mg_connection * mg_download(const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len, const char *fmt,...)
static unsigned int total
static void get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec)
const char ** mg_get_valid_option_names(void)
static int set_throttle(const char *spec, uint32_t remote_ip, const char *uri)
const char * mg_get_header(const struct mg_connection *conn, const char *name)
MD5_STATIC void md5_finish(md5_state_t *pms, md5_byte_t digest[16])
void * mg_get_user_data(const struct mg_context *ctx)
static pid_t spawn_process(struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin, int fdout, const char *dir)
#define CRYPTO_set_locking_callback
static uint32_t get_remote_ip(const struct mg_connection *conn)
double func(double *x, double *p)
static void fclose_on_exec(struct file *filep, struct mg_connection *conn)
#define SSL_load_error_strings
static void process_new_connection(struct mg_connection *conn)
#define PASSWORDS_FILE_NAME
static void handle_file_request(struct mg_connection *conn, const char *path, struct file *filep)
static void * worker_thread(void *thread_func_param)
R__EXTERN C unsigned int sleep(unsigned int seconds)
static void construct_etag(char *buf, size_t buf_len, const struct file *filep)
#define SSL_CTX_set_verify
static int is_valid_http_method(const char *method)
typedef void((*Func_t)())
#define SSLv23_server_method
#define STRUCT_FILE_INITIALIZER
static void do_ssi_exec(struct mg_connection *conn, char *tag)
#define IGNORE_UNUSED_RESULT(a)
static void mg_strlcpy(register char *dst, register const char *src, size_t n)
static int parse_http_message(char *buf, int len, struct mg_request_info *ri)
int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
static int sslize(struct mg_connection *conn, SSL_CTX *s, int(*func)(SSL *))
static void send_file_data(struct mg_connection *conn, struct file *filep, int64_t offset, int64_t len)
static void print_dir_entry(struct de *de)
const char * mg_get_builtin_mime_type(const char *path)
static int set_sock_timeout(SOCKET sock, int milliseconds)
struct ssl_ctx_st SSL_CTX
static void send_options(struct mg_connection *conn)
static void open_auth_file(struct mg_connection *conn, const char *path, struct file *filep)
ClassImp(TSlaveInfo) Int_t TSlaveInfo const TSlaveInfo * si
Used to sort slaveinfos by ordinal.
void *(* mg_thread_func_t)(void *)
struct mg_request_info * mg_get_request_info(struct mg_connection *conn)
static int mg_fopen(struct mg_connection *conn, const char *path, const char *mode, struct file *filep)
static pthread_key_t sTlsKey
static int set_gpass_option(struct mg_context *ctx)
static void redirect_to_https_port(struct mg_connection *conn, int ssl_index)
static unsigned long ssl_id_callback(void)
static char * mg_strdup(const char *str)
MD5_STATIC void md5_init(md5_state_t *pms)
static void accept_new_connection(const struct socket *listener, struct mg_context *ctx)
static void sockaddr_to_string(char *buf, size_t len, const union usa *usa)
static void free_context(struct mg_context *ctx)
int mg_get_var(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len)
static int parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size, struct ah *ah)
Vc_ALWAYS_INLINE_L T *Vc_ALWAYS_INLINE_R malloc(size_t n)
Allocates memory on the Heap with alignment and padding suitable for vectorized access.
static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len)
static char * skip(char **buf, const char *delimiters)
static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap)
static int parse_port_string(const struct vec *vec, struct socket *so)
static int get_month_index(const char *s)
unsigned int r2[N_CITIES]
static int forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
static void log_access(const struct mg_connection *conn)
static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len)
int mg_strncasecmp(const char *s1, const char *s2, size_t len)
void mg_lock(struct mg_connection *conn)
static void * realloc2(void *ptr, size_t size)