MIDAS
Loading...
Searching...
No Matches
mongoose4.cxx File Reference
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <stdint.h>
#include <inttypes.h>
#include <netdb.h>
#include <pwd.h>
#include <unistd.h>
#include <dirent.h>
#include <pthread.h>
#include "mongoose4.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
Include dependency graph for mongoose4.cxx:

Go to the source code of this file.

Classes

union  usa
 
struct  vec
 
struct  file
 
struct  socket
 
struct  mg_context
 
struct  mg_connection
 
struct  de
 
struct  MD5Context
 
struct  ah
 
struct  dir_scan_data
 
struct  cgi_env_block
 

Macros

#define NO_SSL_DL   1
 
#define _LARGEFILE_SOURCE
 
#define __STDC_FORMAT_MACROS
 
#define __STDC_LIMIT_MACROS
 
#define SSL_LIB   "libssl.so"
 
#define CRYPTO_LIB   "libcrypto.so"
 
#define O_BINARY   0
 
#define closesocket(a)   close(a)
 
#define mg_mkdir(x, y)   mkdir(x, y)
 
#define mg_remove(x)   remove(x)
 
#define mg_sleep(x)   usleep((x) * 1000)
 
#define ERRNO   errno
 
#define INVALID_SOCKET   (-1)
 
#define INT64_FMT   PRId64
 
#define WINCDECL
 
#define MONGOOSE_VERSION   "4.2"
 
#define PASSWORDS_FILE_NAME   ".htpasswd"
 
#define CGI_ENVIRONMENT_SIZE   4096
 
#define MAX_CGI_ENVIR_VARS   64
 
#define MG_BUF_LEN   8192
 
#define MAX_REQUEST_SIZE   16384
 
#define ARRAY_SIZE(array)   (sizeof(array) / sizeof(array[0]))
 
#define DEBUG_TRACE(x)
 
#define _DARWIN_UNLIMITED_SELECT
 
#define IP_ADDR_STR_LEN   50
 
#define MSG_NOSIGNAL   0
 
#define SOMAXCONN   100
 
#define PATH_MAX   4096
 
#define MGSQLEN   20
 
#define EXTRA_HTTP_HEADERS   ""
 
#define STRUCT_FILE_INITIALIZER   { 0, 0, 0, 0 }
 
#define F1(x, y, z)   (z ^ (x & (y ^ z)))
 
#define F2(x, y, z)   F1(z, x, y)
 
#define F3(x, y, z)   (x ^ y ^ z)
 
#define F4(x, y, z)   (y ^ (x | ~z))
 
#define MD5STEP(f, w, x, y, z, data, s)    ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
 
#define HEXTOI(x)   (isdigit(x) ? x - '0' : x - 'W')
 

Typedefs

typedef int SOCKET
 
typedef struct MD5Context MD5_CTX
 

Enumerations

enum  {
  CGI_EXTENSIONS , CGI_ENVIRONMENT , PUT_DELETE_PASSWORDS_FILE , CGI_INTERPRETER ,
  PROTECT_URI , AUTHENTICATION_DOMAIN , SSI_EXTENSIONS , THROTTLE ,
  ACCESS_LOG_FILE , ENABLE_DIRECTORY_LISTING , ERROR_LOG_FILE , GLOBAL_PASSWORDS_FILE ,
  INDEX_FILES , ENABLE_KEEP_ALIVE , ACCESS_CONTROL_LIST , EXTRA_MIME_TYPES ,
  LISTENING_PORTS , DOCUMENT_ROOT , SSL_CERTIFICATE , NUM_THREADS ,
  RUN_AS_USER , REWRITE , HIDE_FILES , REQUEST_TIMEOUT ,
  NUM_OPTIONS
}
 

Functions

static FILEmg_fopen (const char *path, const char *mode)
 
static int mg_stat (const char *path, struct file *filep)
 
static void send_http_error (struct mg_connection *, int, const char *, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(4
 
static void static void cry (struct mg_connection *conn, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
 
static void static void static int getreq (struct mg_connection *conn, char *ebuf, size_t ebuf_len)
 
static struct mg_connectionfc (struct mg_context *ctx)
 
static void mg_strlcpy (register char *dst, register const char *src, size_t n)
 
static int lowercase (const char *s)
 
static int mg_strncasecmp (const char *s1, const char *s2, size_t len)
 
static int mg_strcasecmp (const char *s1, const char *s2)
 
static charmg_strndup (const char *ptr, size_t len)
 
static charmg_strdup (const char *str)
 
static const charmg_strcasestr (const char *big_str, const char *small_str)
 
static int mg_vsnprintf (char *buf, size_t buflen, const char *fmt, va_list ap)
 
static int mg_snprintf (char *buf, size_t buflen, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(3
 
static int static int mg_snprintf (char *buf, size_t buflen, const char *fmt,...)
 
static charskip_quoted (char **buf, const char *delimiters, const char *whitespace, char quotechar)
 
static charskip (char **buf, const char *delimiters)
 
static const charget_header (const struct mg_request_info *ri, const char *name)
 
const charmg_get_header (const struct mg_connection *conn, const char *name)
 
static const charnext_option (const char *list, struct vec *val, struct vec *eq_val)
 
static int match_prefix (const char *pattern, int pattern_len, const char *str)
 
static int get_month_index (const char *s)
 
static int num_leap_years (int year)
 
static time_t parse_date_string (const char *datetime)
 
const char ** mg_get_valid_option_names (void)
 
static int get_option_index (const char *name)
 
const charmg_get_option (const struct mg_context *ctx, const char *name)
 
static int is_big_endian (void)
 
static void byteReverse (unsigned char *buf, unsigned longs)
 
static void MD5Init (MD5_CTX *ctx)
 
static void MD5Transform (uint32_t buf[4], uint32_t const in[16])
 
static void MD5Update (MD5_CTX *ctx, unsigned char const *buf, unsigned len)
 
static void MD5Final (unsigned char digest[16], MD5_CTX *ctx)
 
static void bin2str (char *to, const unsigned char *p, size_t len)
 
charmg_md5 (char buf[33],...)
 
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)
 
static FILEopen_auth_file (struct mg_connection *conn, const char *path)
 
static int parse_auth_header (struct mg_connection *conn, char *buf, size_t buf_size, struct ah *ah)
 
static int authorize (struct mg_connection *conn, FILE *fp)
 
static int check_authorization (struct mg_connection *conn, const char *path)
 
static void send_authorization_request (struct mg_connection *conn)
 
static int is_authorized_for_put (struct mg_connection *conn)
 
int mg_modify_passwords_file (const char *fname, const char *domain, const char *user, const char *pass)
 
static void set_close_on_exec (int fd)
 
int mg_start_thread (mg_thread_func_t func, void *param)
 
static pid_t spawn_process (struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin, int fdout, const char *dir)
 
static int set_non_blocking_mode (SOCKET sock)
 
static int alloc_vprintf (char **buf, size_t size, const char *fmt, va_list ap)
 
int mg_vprintf (struct mg_connection *conn, const char *fmt, va_list ap)
 
int mg_printf (struct mg_connection *conn, const char *fmt,...)
 
static int mg_chunked_printf (struct mg_connection *conn, const char *fmt,...)
 
static int sslize (struct mg_connection *conn, SSL_CTX *s, int(*func)(SSL *))
 
static const charssl_error (void)
 
static void ssl_locking_callback (int mode, int mutex_num, const char *file, int line)
 
static unsigned long ssl_id_callback (void)
 
static int set_ssl_option (struct mg_context *ctx)
 
static void uninitialize_ssl (struct mg_context *ctx)
 
static SOCKET conn2 (const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len)
 
struct mg_connectionmg_connect (const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len)
 
struct mg_connectionmg_download (const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len, const char *fmt,...)
 
static int64_t left_to_read (const struct mg_connection *conn)
 
static int call_user (int type, struct mg_connection *conn, void *p)
 
static void sockaddr_to_string (char *buf, size_t len, const union usa *usa)
 
static void cry (struct mg_connection *conn, const char *fmt,...)
 
const charmg_version (void)
 
static int should_keep_alive (const struct mg_connection *conn)
 
static const charsuggest_connection_header (const struct mg_connection *conn)
 
static void send_http_error (struct mg_connection *conn, int status, const char *reason, const char *fmt,...)
 
static int64_t push (FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len)
 
static int pull (FILE *fp, struct mg_connection *conn, char *buf, int len)
 
static int pull_all (FILE *fp, struct mg_connection *conn, char *buf, int len)
 
int mg_read (struct mg_connection *conn, void *buf, int len)
 
int mg_write (struct mg_connection *conn, const void *buf, int len)
 
int mg_url_decode (const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
 
int mg_get_var (const char *data, size_t data_len, const char *name, char *dst, size_t dst_len)
 
int mg_get_cookie (const char *cookie_header, const char *var_name, char *dst, size_t dst_size)
 
static int convert_uri_to_file_name (struct mg_connection *conn, char *buf, size_t buf_len, struct file *filep)
 
static int get_request_len (const char *buf, int buf_len)
 
static void remove_double_dots_and_double_slashes (char *s)
 
const charmg_get_builtin_mime_type (const char *path)
 
static void get_mime_type (struct mg_context *ctx, const char *path, struct vec *vec)
 
void mg_url_encode (const char *src, char *dst, size_t dst_len)
 
static void print_dir_entry (const struct de *de)
 
static int WINCDECL compare_dir_entries (const void *p1, const void *p2)
 
static int must_hide_file (struct mg_connection *conn, const char *path)
 
static int scan_directory (struct mg_connection *conn, const char *dir, void *data, void(*cb)(struct de *, void *))
 
static int remove_directory (struct mg_connection *conn, const char *dir)
 
static voidrealloc2 (void *ptr, size_t size)
 
static void dir_scan_callback (struct de *de, void *data)
 
static void handle_directory_request (struct mg_connection *conn, const char *dir)
 
static void send_file_data (struct mg_connection *conn, FILE *fp, int64_t offset, int64_t len)
 
static int parse_range_header (const char *header, int64_t *a, int64_t *b)
 
static void gmt_time_string (char *buf, size_t buf_len, time_t *t)
 
static void construct_etag (char *buf, size_t buf_len, const struct file *filep)
 
static void fclose_on_exec (FILE *fp)
 
static void handle_file_request (struct mg_connection *conn, const char *path, struct file *filep)
 
void mg_send_file (struct mg_connection *conn, const char *path)
 
static void parse_http_headers (char **buf, struct mg_request_info *ri)
 
static int is_valid_http_method (const char *method)
 
static int parse_http_message (char *buf, int len, struct mg_request_info *ri)
 
static int read_request (FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread)
 
static int substitute_index_file (struct mg_connection *conn, char *path, size_t path_len, struct file *filep)
 
static int is_not_modified (const struct mg_connection *conn, const struct file *filep)
 
static int forward_body_data (struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 
static charaddenv (struct cgi_env_block *block, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
 
static char static charaddenv (struct cgi_env_block *block, const char *fmt,...)
 
static void prepare_cgi_environment (struct mg_connection *conn, const char *prog, struct cgi_env_block *blk)
 
static void handle_cgi_request (struct mg_connection *conn, const char *prog)
 
static int put_dir (const char *path)
 
static void mkcol (struct mg_connection *conn, const char *path)
 
static void put_file (struct mg_connection *conn, const char *path)
 
static void send_ssi_file (struct mg_connection *, const char *, FILE *, int)
 
static void do_ssi_include (struct mg_connection *conn, const char *ssi, char *tag, int include_level)
 
static void do_ssi_exec (struct mg_connection *conn, char *tag)
 
static void handle_ssi_file_request (struct mg_connection *conn, const char *path)
 
static void handle_options_request (struct mg_connection *conn)
 
static void print_props (struct mg_connection *conn, const char *uri, struct file *filep)
 
static void print_dav_dir_entry (struct de *de, void *data)
 
static void handle_propfind (struct mg_connection *conn, const char *path, struct file *filep)
 
static int isbyte (int n)
 
static int parse_net (const char *spec, uint32_t *net, uint32_t *mask)
 
static int set_throttle (const char *spec, uint32_t remote_ip, const char *uri)
 
static uint32_t get_remote_ip (const struct mg_connection *conn)
 
FILEmg_upload (struct mg_connection *conn, const char *destination_dir, char *path, int path_len)
 
static int is_put_or_delete_request (const struct mg_connection *conn)
 
static int get_first_ssl_listener_index (const struct mg_context *ctx)
 
static void redirect_to_https_port (struct mg_connection *conn, int ssl_index)
 
static void handle_delete_request (struct mg_connection *conn, const char *path)
 
static void handle_request (struct mg_connection *conn)
 
static void close_all_listening_sockets (struct mg_context *ctx)
 
static int is_valid_port (unsigned int port)
 
static int parse_port_string (const struct vec *vec, struct socket *so)
 
static int set_ports_option (struct mg_context *ctx)
 
static void log_header (const struct mg_connection *conn, const char *header, FILE *fp)
 
static void log_access (const struct mg_connection *conn)
 
static int check_acl (struct mg_context *ctx, uint32_t remote_ip)
 
static int set_uid_option (struct mg_context *ctx)
 
static int set_gpass_option (struct mg_context *ctx)
 
static int set_acl_option (struct mg_context *ctx)
 
static void reset_per_request_attributes (struct mg_connection *conn)
 
static void close_socket_gracefully (struct mg_connection *conn)
 
static void close_connection (struct mg_connection *conn)
 
void mg_close_connection (struct mg_connection *conn)
 
static int is_valid_uri (const char *uri)
 
static void process_new_connection (struct mg_connection *conn)
 
static int consume_socket (struct mg_context *ctx, struct socket *sp)
 
static voidworker_thread (void *thread_func_param)
 
static void produce_socket (struct mg_context *ctx, const struct socket *sp)
 
static int set_sock_timeout (SOCKET sock, int milliseconds)
 
static void accept_new_connection (const struct socket *listener, struct mg_context *ctx)
 
static voidmaster_thread (void *thread_func_param)
 
static void free_context (struct mg_context *ctx)
 
void mg_stop (struct mg_context *ctx)
 
struct mg_contextmg_start (const char **options, mg_event_handler_t func, void *user_data)
 

Variables

static const charhttp_500_error = "Internal Server Error"
 
static const charmonth_names []
 
static const charconfig_options []
 
static pthread_mutex_tssl_mutexes
 
struct { 
 
   const char *   extension 
 
   size_t   ext_len 
 
   const char *   mime_type 
 
builtin_mime_types [] 
 

Macro Definition Documentation

◆ __STDC_FORMAT_MACROS

#define __STDC_FORMAT_MACROS

Definition at line 41 of file mongoose4.cxx.

◆ __STDC_LIMIT_MACROS

#define __STDC_LIMIT_MACROS

Definition at line 42 of file mongoose4.cxx.

◆ _DARWIN_UNLIMITED_SELECT

#define _DARWIN_UNLIMITED_SELECT

Definition at line 293 of file mongoose4.cxx.

◆ _LARGEFILE_SOURCE

#define _LARGEFILE_SOURCE

Definition at line 39 of file mongoose4.cxx.

◆ ARRAY_SIZE

#define ARRAY_SIZE (   array)    (sizeof(array) / sizeof(array[0]))

Definition at line 267 of file mongoose4.cxx.

◆ CGI_ENVIRONMENT_SIZE

#define CGI_ENVIRONMENT_SIZE   4096

Definition at line 263 of file mongoose4.cxx.

◆ closesocket

#define closesocket (   a)    close(a)

Definition at line 247 of file mongoose4.cxx.

◆ CRYPTO_LIB

#define CRYPTO_LIB   "libcrypto.so"

Definition at line 241 of file mongoose4.cxx.

◆ DEBUG_TRACE

#define DEBUG_TRACE (   x)

Definition at line 285 of file mongoose4.cxx.

◆ ERRNO

#define ERRNO   errno

Definition at line 251 of file mongoose4.cxx.

◆ EXTRA_HTTP_HEADERS

#define EXTRA_HTTP_HEADERS   ""

Definition at line 316 of file mongoose4.cxx.

◆ F1

#define F1 (   x,
  y,
  z 
)    (z ^ (x & (y ^ z)))

Definition at line 906 of file mongoose4.cxx.

◆ F2

#define F2 (   x,
  y,
  z 
)    F1(z, x, y)

Definition at line 907 of file mongoose4.cxx.

◆ F3

#define F3 (   x,
  y,
  z 
)    (x ^ y ^ z)

Definition at line 908 of file mongoose4.cxx.

◆ F4

#define F4 (   x,
  y,
  z 
)    (y ^ (x | ~z))

Definition at line 909 of file mongoose4.cxx.

◆ HEXTOI

#define HEXTOI (   x)    (isdigit(x) ? x - '0' : x - 'W')

◆ INT64_FMT

#define INT64_FMT   PRId64

Definition at line 253 of file mongoose4.cxx.

◆ INVALID_SOCKET

#define INVALID_SOCKET   (-1)

Definition at line 252 of file mongoose4.cxx.

◆ IP_ADDR_STR_LEN

#define IP_ADDR_STR_LEN   50

Definition at line 295 of file mongoose4.cxx.

◆ MAX_CGI_ENVIR_VARS

#define MAX_CGI_ENVIR_VARS   64

Definition at line 264 of file mongoose4.cxx.

◆ MAX_REQUEST_SIZE

#define MAX_REQUEST_SIZE   16384

Definition at line 266 of file mongoose4.cxx.

◆ MD5STEP

#define MD5STEP (   f,
  w,
  x,
  y,
  z,
  data,
 
)     ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )

Definition at line 911 of file mongoose4.cxx.

915 {
916 ctx->buf[0] = 0x67452301;
917 ctx->buf[1] = 0xefcdab89;
918 ctx->buf[2] = 0x98badcfe;
919 ctx->buf[3] = 0x10325476;
920
921 ctx->bits[0] = 0;
922 ctx->bits[1] = 0;
923}
924
925static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
926 register uint32_t a, b, c, d;
927
928 a = buf[0];
929 b = buf[1];
930 c = buf[2];
931 d = buf[3];
932
933 MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
934 MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
935 MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
936 MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
937 MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
938 MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
939 MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
940 MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
941 MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
942 MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
943 MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
944 MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
945 MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
946 MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
947 MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
948 MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
949
950 MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
951 MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
952 MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
953 MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
954 MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
955 MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
956 MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
957 MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
958 MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
959 MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
960 MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
961 MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
962 MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
963 MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
964 MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
965 MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
966
967 MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
968 MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
969 MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
970 MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
971 MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
972 MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
973 MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
974 MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
975 MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
976 MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
977 MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
978 MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
979 MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
980 MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
981 MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
982 MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
983
984 MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
985 MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
986 MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
987 MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
988 MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
989 MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
990 MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
991 MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
992 MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
993 MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
994 MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
995 MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
996 MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
997 MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
998 MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
999 MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
1000
1001 buf[0] += a;
1002 buf[1] += b;
1003 buf[2] += c;
1004 buf[3] += d;
1005}
1006
1007static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
1008 uint32_t t;
1009
1010 t = ctx->bits[0];
1011 if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
1012 ctx->bits[1]++;
1013 ctx->bits[1] += len >> 29;
1014
1015 t = (t >> 3) & 0x3f;
1016
1017 if (t) {
1018 unsigned char *p = (unsigned char *) ctx->in + t;
1019
1020 t = 64 - t;
1021 if (len < t) {
1022 memcpy(p, buf, len);
1023 return;
1024 }
1025 memcpy(p, buf, t);
1026 byteReverse(ctx->in, 16);
1027 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1028 buf += t;
1029 len -= t;
1030 }
1031
1032 while (len >= 64) {
1033 memcpy(ctx->in, buf, 64);
1034 byteReverse(ctx->in, 16);
1035 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1036 buf += 64;
1037 len -= 64;
1038 }
1039
1040 memcpy(ctx->in, buf, len);
1041}
1042
1043static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
1044 unsigned count;
1045 unsigned char *p;
1046 uint32_t *a;
1047
1048 count = (ctx->bits[0] >> 3) & 0x3F;
1049
1050 p = ctx->in + count;
1051 *p++ = 0x80;
1052 count = 64 - 1 - count;
1053 if (count < 8) {
1054 memset(p, 0, count);
1055 byteReverse(ctx->in, 16);
1056 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1057 memset(ctx->in, 0, 56);
1058 } else {
1059 memset(p, 0, count - 8);
1060 }
1061 byteReverse(ctx->in, 14);
1062
1063 a = (uint32_t *)ctx->in;
1064 a[14] = ctx->bits[0];
1065 a[15] = ctx->bits[1];
1066
1067 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1068 byteReverse((unsigned char *) ctx->buf, 4);
1069 memcpy(digest, ctx->buf, 16);
1070 memset((char *) ctx, 0, sizeof(*ctx));
1071}
1072#endif // !HAVE_MD5
1073
1074
1075
1076// Stringify binary data. Output buffer must be twice as big as input,
1077// because each byte takes 2 bytes in string representation
1078static void bin2str(char *to, const unsigned char *p, size_t len) {
1079 static const char *hex = "0123456789abcdef";
1080
1081 for (; len--; p++) {
1082 *to++ = hex[p[0] >> 4];
1083 *to++ = hex[p[0] & 0x0f];
1084 }
1085 *to = '\0';
1086}
1087
1088// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
1089char *mg_md5(char buf[33], ...) {
1090 unsigned char hash[16];
1091 const char *p;
1092 va_list ap;
1093 MD5_CTX ctx;
1094
1095 MD5Init(&ctx);
1096
1097 va_start(ap, buf);
1098 while ((p = va_arg(ap, const char *)) != NULL) {
1099 MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
1100 }
1101 va_end(ap);
1102
1103 MD5Final(hash, &ctx);
1104 bin2str(buf, hash, sizeof(hash));
1105 return buf;
1106}
1107
1108// Check the user's password, return 1 if OK
1109static int check_password(const char *method, const char *ha1, const char *uri,
1110 const char *nonce, const char *nc, const char *cnonce,
1111 const char *qop, const char *response) {
1112 char ha2[32 + 1], expected_response[32 + 1];
1113
1114 // Some of the parameters may be NULL
1115 if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL ||
1116 qop == NULL || response == NULL) {
1117 return 0;
1118 }
1119
1120 // NOTE(lsm): due to a bug in MSIE, we do not compare the URI
1121 // TODO(lsm): check for authentication timeout
1122 if (// strcmp(dig->uri, c->ouri) != 0 ||
1123 strlen(response) != 32
1124 // || now - strtoul(dig->nonce, NULL, 10) > 3600
1125 ) {
1126 return 0;
1127 }
1128
1129 mg_md5(ha2, method, ":", uri, NULL);
1130 mg_md5(expected_response, ha1, ":", nonce, ":", nc,
1131 ":", cnonce, ":", qop, ":", ha2, NULL);
1132
1134}
1135
1136// Use the global passwords file, if specified by auth_gpass option,
1137// or search for .htpasswd in the requested directory.
1138static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
1139 char name[PATH_MAX];
1140 const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE];
1142 FILE *fp = NULL;
1143
1144 if (gpass != NULL) {
1145 // Use global passwords file
1146 fp = mg_fopen(gpass, "r");
1147 // Important: using local struct file to test path for is_directory flag.
1148 // If filep is used, mg_stat() makes it appear as if auth file was opened.
1149 } else if (mg_stat(path, &file) && file.is_directory) {
1150 mg_snprintf(name, sizeof(name), "%s%c%s",
1151 path, '/', PASSWORDS_FILE_NAME);
1152 fp = mg_fopen(name, "r");
1153 } else {
1154 // Try to find .htpasswd in requested directory.
1155 for (p = path, e = p + strlen(p) - 1; e > p; e--)
1156 if (e[0] == '/')
1157 break;
1158 mg_snprintf(name, sizeof(name), "%.*s%c%s",
1159 (int) (e - p), p, '/', PASSWORDS_FILE_NAME);
1160 fp = mg_fopen(name, "r");
1161 }
1162
1163 return fp;
1164}
1165
1166// Parsed Authorization header
1167struct ah {
1168 char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
1169};
1170
1171// Return 1 on success. Always initializes the ah structure.
1172static int parse_auth_header(struct mg_connection *conn, char *buf,
1173 size_t buf_size, struct ah *ah) {
1174 char *name, *value, *s;
1175 const char *auth_header;
1176
1177 (void) memset(ah, 0, sizeof(*ah));
1178 if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
1179 mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
1180 return 0;
1181 }
1182
1183 // Make modifiable copy of the auth header
1184 (void) mg_strlcpy(buf, auth_header + 7, buf_size);
1185 s = buf;
1186
1187 // Parse authorization header
1188 for (;;) {
1189 // Gobble initial spaces
1190 while (isspace(* (unsigned char *) s)) {
1191 s++;
1192 }
1193 name = skip_quoted(&s, "=", " ", 0);
1194 // Value is either quote-delimited, or ends at first comma or space.
1195 if (s[0] == '\"') {
1196 s++;
1197 value = skip_quoted(&s, "\"", " ", '\\');
1198 if (s[0] == ',') {
1199 s++;
1200 }
1201 } else {
1202 value = skip_quoted(&s, ", ", " ", 0); // IE uses commas, FF uses spaces
1203 }
1204 if (*name == '\0') {
1205 break;
1206 }
1207
1208 if (!strcmp(name, "username")) {
1209 ah->user = value;
1210 } else if (!strcmp(name, "cnonce")) {
1211 ah->cnonce = value;
1212 } else if (!strcmp(name, "response")) {
1213 ah->response = value;
1214 } else if (!strcmp(name, "uri")) {
1215 ah->uri = value;
1216 } else if (!strcmp(name, "qop")) {
1217 ah->qop = value;
1218 } else if (!strcmp(name, "nc")) {
1219 ah->nc = value;
1220 } else if (!strcmp(name, "nonce")) {
1221 ah->nonce = value;
1222 }
1223 }
1224
1225 // CGI needs it as REMOTE_USER
1226 if (ah->user != NULL) {
1228 } else {
1229 return 0;
1230 }
1231
1232 return 1;
1233}
1234
1235// Authorize against the opened passwords file. Return 1 if authorized.
1236static int authorize(struct mg_connection *conn, FILE *fp) {
1237 struct ah ah;
1238 char line[256], f_user[256], ha1[256], f_domain[256], buf[MG_BUF_LEN];
1239
1240 if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) {
1241 return 0;
1242 }
1243
1244 // Loop over passwords file
1245 while (fgets(line, sizeof(line), fp) != NULL) {
1246 if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
1247 continue;
1248 }
1249
1250 if (!strcmp(ah.user, f_user) &&
1254 }
1255
1256 return 0;
1257}
1258
1259// Return 1 if request is authorised, 0 otherwise.
1260static int check_authorization(struct mg_connection *conn, const char *path) {
1261 char fname[PATH_MAX];
1262 struct vec uri_vec, filename_vec;
1263 const char *list;
1264 FILE *fp = NULL;
1265 int authorized = 1;
1266
1267 list = conn->ctx->config[PROTECT_URI];
1268 while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
1269 if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
1270 mg_snprintf(fname, sizeof(fname), "%.*s",
1271 (int) filename_vec.len, filename_vec.ptr);
1272 fp = mg_fopen(fname, "r");
1273 break;
1274 }
1275 }
1276
1277 if (fp == NULL) {
1278 fp = open_auth_file(conn, path);
1279 }
1280
1281 if (fp != NULL) {
1282 authorized = authorize(conn, fp);
1283 fclose(fp);
1284 }
1285
1286 return authorized;
1287}
1288
1289static void send_authorization_request(struct mg_connection *conn) {
1290 conn->status_code = 401;
1291 mg_printf(conn,
1292 "HTTP/1.1 401 Unauthorized\r\n"
1293 "Content-Length: 0\r\n"
1294 "WWW-Authenticate: Digest qop=\"auth\", "
1295 "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
1297 (unsigned long) time(NULL));
1298}
1299
1300static int is_authorized_for_put(struct mg_connection *conn) {
1301 const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE];
1302 FILE *fp;
1303 int ret = 0;
1304
1305 if (passfile != NULL && (fp = mg_fopen(passfile, "r")) != NULL) {
1306 ret = authorize(conn, fp);
1307 fclose(fp);
1308 }
1309
1310 return ret;
1311}
1312
1313int mg_modify_passwords_file(const char *fname, const char *domain,
1314 const char *user, const char *pass) {
1315 int found;
1316 char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
1317 FILE *fp, *fp2;
1318
1319 found = 0;
1320 fp = fp2 = NULL;
1321
1322 // Regard empty password as no password - remove user record.
1323 if (pass != NULL && pass[0] == '\0') {
1324 pass = NULL;
1325 }
1326
1327 (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
1328
1329 // Create the file if does not exist
1330 if ((fp = fopen(fname, "a+")) != NULL) {
1331 fclose(fp);
1332 }
1333
1334 // Open the given file and temporary file
1335 if ((fp = fopen(fname, "r")) == NULL) {
1336 return 0;
1337 } else if ((fp2 = fopen(tmp, "w+")) == NULL) {
1338 fclose(fp);
1339 return 0;
1340 }
1341
1342 // Copy the stuff to temporary file
1343 while (fgets(line, sizeof(line), fp) != NULL) {
1344 if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
1345 continue;
1346 }
1347
1348 if (!strcmp(u, user) && !strcmp(d, domain)) {
1349 found++;
1350 if (pass != NULL) {
1351 mg_md5(ha1, user, ":", domain, ":", pass, NULL);
1352 fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
1353 }
1354 } else {
1355 fprintf(fp2, "%s", line);
1356 }
1357 }
1358
1359 // If new user, just add it
1360 if (!found && pass != NULL) {
1361 mg_md5(ha1, user, ":", domain, ":", pass, NULL);
1362 fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
1363 }
1364
1365 // Close files
1366 fclose(fp);
1367 fclose(fp2);
1368
1369 // Put the temp file in place of real file
1370 remove(fname);
1371 rename(tmp, fname);
1372
1373 return 1;
1374}
1375
1376#if defined(_WIN32)
1377static pthread_t pthread_self(void) {
1378 return GetCurrentThreadId();
1379}
1380
1381static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) {
1382 (void) unused;
1383 *mutex = CreateMutex(NULL, FALSE, NULL);
1384 return *mutex == NULL ? -1 : 0;
1385}
1386
1387static int pthread_mutex_destroy(pthread_mutex_t *mutex) {
1388 return CloseHandle(*mutex) == 0 ? -1 : 0;
1389}
1390
1391static int pthread_mutex_lock(pthread_mutex_t *mutex) {
1392 return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
1393}
1394
1395static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
1396 return ReleaseMutex(*mutex) == 0 ? -1 : 0;
1397}
1398
1399static int pthread_cond_init(pthread_cond_t *cv, const void *unused) {
1400 (void) unused;
1401 cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL);
1402 cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL);
1403 return cv->signal != NULL && cv->broadcast != NULL ? 0 : -1;
1404}
1405
1407 HANDLE handles[] = {cv->signal, cv->broadcast};
1408 ReleaseMutex(*mutex);
1410 return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
1411}
1412
1414 return SetEvent(cv->signal) == 0 ? -1 : 0;
1415}
1416
1418 // Implementation with PulseEvent() has race condition, see
1419 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
1420 return PulseEvent(cv->broadcast) == 0 ? -1 : 0;
1421}
1422
1424 return CloseHandle(cv->signal) && CloseHandle(cv->broadcast) ? 0 : -1;
1425}
1426
1427// For Windows, change all slashes to backslashes in path names.
1428static void change_slashes_to_backslashes(char *path) {
1429 int i;
1430
1431 for (i = 0; path[i] != '\0'; i++) {
1432 if (path[i] == '/')
1433 path[i] = '\\';
1434 // i > 0 check is to preserve UNC paths, like \\server\file.txt
1435 if (path[i] == '\\' && i > 0)
1436 while (path[i + 1] == '\\' || path[i + 1] == '/')
1437 (void) memmove(path + i + 1,
1438 path + i + 2, strlen(path + i + 1));
1439 }
1440}
1441
1442// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
1443// wbuf and wbuf_len is a target buffer and its length.
1444static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) {
1445 char buf[PATH_MAX * 2], buf2[PATH_MAX * 2];
1446
1447 mg_strlcpy(buf, path, sizeof(buf));
1449
1450 // Convert to Unicode and back. If doubly-converted string does not
1451 // match the original, something is fishy, reject.
1452 memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
1453 MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
1454 WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
1455 NULL, NULL);
1456 if (strcmp(buf, buf2) != 0) {
1457 wbuf[0] = L'\0';
1458 }
1459}
1460
1461#if defined(_WIN32_WCE)
1462static time_t time(time_t *ptime) {
1463 time_t t;
1464 SYSTEMTIME st;
1465 FILETIME ft;
1466
1467 GetSystemTime(&st);
1469 t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
1470
1471 if (ptime != NULL) {
1472 *ptime = t;
1473 }
1474
1475 return t;
1476}
1477
1478static struct tm *localtime(const time_t *ptime, struct tm *ptm) {
1480 FILETIME ft, lft;
1481 SYSTEMTIME st;
1483
1484 if (ptm == NULL) {
1485 return NULL;
1486 }
1487
1488 * (int64_t *) &ft = t;
1491 ptm->tm_year = st.wYear - 1900;
1492 ptm->tm_mon = st.wMonth - 1;
1493 ptm->tm_wday = st.wDayOfWeek;
1494 ptm->tm_mday = st.wDay;
1495 ptm->tm_hour = st.wHour;
1496 ptm->tm_min = st.wMinute;
1497 ptm->tm_sec = st.wSecond;
1498 ptm->tm_yday = 0; // hope nobody uses this
1499 ptm->tm_isdst =
1501
1502 return ptm;
1503}
1504
1505static struct tm *gmtime(const time_t *ptime, struct tm *ptm) {
1506 // FIXME(lsm): fix this.
1507 return localtime(ptime, ptm);
1508}
1509
1510static size_t strftime(char *dst, size_t dst_size, const char *fmt,
1511 const struct tm *tm) {
1512 (void) snprintf(dst, dst_size, "implement strftime() for WinCE");
1513 return 0;
1514}
1515#endif
1516
1517// Windows happily opens files with some garbage at the end of file name.
1518// For example, fopen("a.cgi ", "r") on Windows successfully opens
1519// "a.cgi", despite one would expect an error back.
1520// This function returns non-0 if path ends with some garbage.
1521static int path_cannot_disclose_cgi(const char *path) {
1522 static const char *allowed_last_characters = "_-";
1523 int last = path[strlen(path) - 1];
1525}
1526
1527static int mg_stat(const char *path, struct file *filep) {
1528 wchar_t wbuf[PATH_MAX] = L"\\\\?\\";
1530
1531 filep->modification_time = 0;
1532 to_unicode(path, wbuf + 4, ARRAY_SIZE(wbuf) - 4);
1534 filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
1535 filep->modification_time = SYS2UNIX_TIME(
1536 info.ftLastWriteTime.dwLowDateTime,
1537 info.ftLastWriteTime.dwHighDateTime);
1538 filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
1539 // If file name is fishy, reset the file structure and return error.
1540 // Note it is important to reset, not just return the error, cause
1541 // functions like is_file_opened() check the struct.
1542 if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
1543 memset(filep, 0, sizeof(*filep));
1544 }
1545 }
1546
1547 return filep->modification_time != 0;
1548}
1549
1550static int mg_remove(const char *path) {
1551 wchar_t wbuf[PATH_MAX];
1552 to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
1553 return DeleteFileW(wbuf) ? 0 : -1;
1554}
1555
1556static int mg_mkdir(const char *path, int mode) {
1557 char buf[PATH_MAX];
1558 wchar_t wbuf[PATH_MAX];
1559
1560 (void) mode;
1561 mg_strlcpy(buf, path, sizeof(buf));
1563
1565
1566 return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
1567}
1568
1569// Implementation of POSIX opendir/closedir/readdir for Windows.
1570static DIR * opendir(const char *name) {
1571 DIR *dir = NULL;
1572 wchar_t wpath[PATH_MAX];
1573 DWORD attrs;
1574
1575 if (name == NULL) {
1577 } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
1579 } else {
1582 if (attrs != 0xFFFFFFFF &&
1584 (void) wcscat(wpath, L"\\*");
1585 dir->handle = FindFirstFileW(wpath, &dir->info);
1586 dir->result.d_name[0] = '\0';
1587 } else {
1588 free(dir);
1589 dir = NULL;
1590 }
1591 }
1592
1593 return dir;
1594}
1595
1596static int closedir(DIR *dir) {
1597 int result = 0;
1598
1599 if (dir != NULL) {
1600 if (dir->handle != INVALID_HANDLE_VALUE)
1601 result = FindClose(dir->handle) ? 0 : -1;
1602
1603 free(dir);
1604 } else {
1605 result = -1;
1607 }
1608
1609 return result;
1610}
1611
1612static struct dirent *readdir(DIR *dir) {
1613 struct dirent *result = 0;
1614
1615 if (dir) {
1616 if (dir->handle != INVALID_HANDLE_VALUE) {
1617 result = &dir->result;
1619 dir->info.cFileName, -1, result->d_name,
1620 sizeof(result->d_name), NULL, NULL);
1621
1622 if (!FindNextFileW(dir->handle, &dir->info)) {
1623 (void) FindClose(dir->handle);
1624 dir->handle = INVALID_HANDLE_VALUE;
1625 }
1626
1627 } else {
1629 }
1630 } else {
1632 }
1633
1634 return result;
1635}
1636
1637#ifndef HAVE_POLL
1638static int poll(struct pollfd *pfd, int n, int milliseconds) {
1639 struct timeval tv;
1640 fd_set set;
1641 int i, result;
1642 SOCKET maxfd = 0;
1643
1644 tv.tv_sec = milliseconds / 1000;
1645 tv.tv_usec = (milliseconds % 1000) * 1000;
1646 FD_ZERO(&set);
1647
1648 for (i = 0; i < n; i++) {
1649 FD_SET((SOCKET) pfd[i].fd, &set);
1650 pfd[i].revents = 0;
1651
1652 if (pfd[i].fd > maxfd) {
1653 maxfd = pfd[i].fd;
1654 }
1655 }
1656
1657 if ((result = select(maxfd + 1, &set, NULL, NULL, &tv)) > 0) {
1658 for (i = 0; i < n; i++) {
1659 if (FD_ISSET(pfd[i].fd, &set)) {
1660 pfd[i].revents = POLLIN;
1661 }
1662 }
1663 }
1664
1665 return result;
1666}
1667#endif // HAVE_POLL
1668
1669static void set_close_on_exec(SOCKET sock) {
1671}
1672
1673int mg_start_thread(mg_thread_func_t f, void *p) {
1674 return (long)_beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0;
1675}
1676
1677static HANDLE dlopen(const char *dll_name, int flags) {
1678 wchar_t wbuf[PATH_MAX];
1679 (void) flags;
1681 return LoadLibraryW(wbuf);
1682}
1683
1684#if !defined(NO_CGI)
1685#define SIGKILL 0
1686static int kill(pid_t pid, int sig_num) {
1688 (void) CloseHandle(pid);
1689 return 0;
1690}
1691
1692static void trim_trailing_whitespaces(char *s) {
1693 char *e = s + strlen(s) - 1;
1694 while (e > s && isspace(* (unsigned char *) e)) {
1695 *e-- = '\0';
1696 }
1697}
1698
1699static pid_t spawn_process(struct mg_connection *conn, const char *prog,
1700 char *envblk, char *envp[], int fdin,
1701 int fdout, const char *dir) {
1702 HANDLE me;
1704 cmdline[PATH_MAX], buf[PATH_MAX];
1705 FILE *fp;
1707 PROCESS_INFORMATION pi = { 0 };
1708
1709 (void) envp;
1710
1711 memset(&si, 0, sizeof(si));
1712 si.cb = sizeof(si);
1713
1714 // TODO(lsm): redirect CGI errors to the error log file
1716 si.wShowWindow = SW_HIDE;
1717
1720 &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
1722 &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
1723
1724 // If CGI file is a script, try to read the interpreter line
1725 interp = conn->ctx->config[CGI_INTERPRETER];
1726 if (interp == NULL) {
1727 buf[0] = buf[1] = '\0';
1728
1729 // Read the first line of the script into the buffer
1730 snprintf(cmdline, sizeof(cmdline), "%s%c%s", dir, '/', prog);
1731 if ((fp = mg_fopen(cmdline, "r")) != NULL) {
1732 fgets(buf, sizeof(buf), fp);
1733 fclose(fp);
1734 buf[sizeof(buf) - 1] = '\0';
1735 }
1736
1737 if (buf[0] == '#' && buf[1] == '!') {
1739 } else {
1740 buf[2] = '\0';
1741 }
1742 interp = buf + 2;
1743 }
1744
1745 if (interp[0] != '\0') {
1748 }
1749 GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
1750
1751 mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\\%s\"",
1752 interp, interp[0] == '\0' ? "" : " ", full_dir, prog);
1753
1754 DEBUG_TRACE(("Running [%s]", cmdline));
1757 cry(conn, "%s: CreateProcess(%s): %ld",
1759 pi.hProcess = (pid_t) -1;
1760 }
1761
1762 (void) CloseHandle(si.hStdOutput);
1763 (void) CloseHandle(si.hStdInput);
1764 (void) CloseHandle(pi.hThread);
1765
1766 return (pid_t) pi.hProcess;
1767}
1768#endif // !NO_CGI
1769
1770static int set_non_blocking_mode(SOCKET sock) {
1771 unsigned long on = 1;
1772 return ioctlsocket(sock, FIONBIO, &on);
1773}
1774#endif
1775
1776#if !defined(_WIN32)
1777static int mg_stat(const char *path, struct file *filep) {
1778 struct stat st;
1779
1780 filep->modification_time = (time_t) 0;
1781 if (stat(path, &st) == 0) {
1782 filep->size = st.st_size;
1783 filep->modification_time = st.st_mtime;
1784 filep->is_directory = S_ISDIR(st.st_mode);
1785
1786 // See https://github.com/cesanta/mongoose/issues/109
1787 // Some filesystems report modification time as 0. Artificially
1788 // bump it up to mark mg_stat() success.
1789 if (filep->modification_time == (time_t) 0) {
1790 filep->modification_time = (time_t) 1;
1791 }
1792 }
1793
1794 return filep->modification_time != (time_t) 0;
1795}
1796
1797static void set_close_on_exec(int fd) {
1798 fcntl(fd, F_SETFD, FD_CLOEXEC);
1799}
1800
1801int mg_start_thread(mg_thread_func_t func, void *param) {
1802 pthread_t thread_id;
1804 int result;
1805
1808
1809#if USE_STACK_SIZE > 1
1810 // Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
1812#endif
1813
1814 result = pthread_create(&thread_id, &attr, func, param);
1816
1817 return result;
1818}
1819
1820#ifndef NO_CGI
1821static pid_t spawn_process(struct mg_connection *conn, const char *prog,
1822 char *envblk, char *envp[], int fdin,
1823 int fdout, const char *dir) {
1824 pid_t pid;
1825 const char *interp;
1826
1827 (void) envblk;
1828
1829 if ((pid = fork()) == -1) {
1830 // Parent
1831 send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
1832 } else if (pid == 0) {
1833 // Child
1834 if (chdir(dir) != 0) {
1835 cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
1836 } else if (dup2(fdin, 0) == -1) {
1837 cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO));
1838 } else if (dup2(fdout, 1) == -1) {
1839 cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO));
1840 } else {
1841 // Not redirecting stderr to stdout, to avoid output being littered
1842 // with the error messages.
1843 (void) close(fdin);
1844 (void) close(fdout);
1845
1846 // After exec, all signal handlers are restored to their default values,
1847 // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
1848 // implementation, SIGCHLD's handler will leave unchanged after exec
1849 // if it was set to be ignored. Restore it to default action.
1851
1852 interp = conn->ctx->config[CGI_INTERPRETER];
1853 if (interp == NULL) {
1854 (void) execle(prog, prog, NULL, envp);
1855 cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
1856 } else {
1858 cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
1859 strerror(ERRNO));
1860 }
1861 }
1862 exit(EXIT_FAILURE);
1863 }
1864
1865 return pid;
1866}
1867#endif // !NO_CGI
1868
1869static int set_non_blocking_mode(SOCKET sock) {
1870 int flags;
1871
1872 flags = fcntl(sock, F_GETFL, 0);
1873 (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
1874
1875 return 0;
1876}
1877#endif // _WIN32
1878
1879
1880// Print message to buffer. If buffer is large enough to hold the message,
1881// return buffer. If buffer is to small, allocate large enough buffer on heap,
1882// and return allocated buffer.
1883static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) {
1885 int len;
1886
1887 // Windows is not standard-compliant, and vsnprintf() returns -1 if
1888 // buffer is too small. Also, older versions of msvcrt.dll do not have
1889 // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
1890 // Therefore, we make two passes: on first pass, get required message length.
1891 // On second pass, actually print the message.
1892 va_copy(ap_copy, ap);
1893 len = vsnprintf(NULL, 0, fmt, ap_copy);
1894
1895 if (len > (int) size &&
1896 (size = len + 1) > 0 &&
1897 (*buf = (char *) malloc(size)) == NULL) {
1898 len = -1; // Allocation failed, mark failure
1899 } else {
1900 va_copy(ap_copy, ap);
1901 vsnprintf(*buf, size, fmt, ap_copy);
1902 }
1903
1904 return len;
1905}
1906
1907int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) {
1908 char mem[MG_BUF_LEN], *buf = mem;
1909 int len;
1910
1911 if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
1912 len = mg_write(conn, buf, (size_t) len);
1913 }
1914 if (buf != mem && buf != NULL) {
1915 free(buf);
1916 }
1917
1918 return len;
1919}
1920
1921int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
1922 va_list ap;
1923 va_start(ap, fmt);
1924 return mg_vprintf(conn, fmt, ap);
1925}
1926
1927static int mg_chunked_printf(struct mg_connection *conn, const char *fmt, ...) {
1928 char mem[MG_BUF_LEN], *buf = mem;
1929 int len;
1930
1931 va_list ap;
1932 va_start(ap, fmt);
1933 if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
1934 len = mg_printf(conn, "%X\r\n%s\r\n", len, buf);
1935 }
1936
1937 if (buf != mem && buf != NULL) {
1938 free(buf);
1939 }
1940
1941 return len;
1942}
1943
1944
1945
1946#if !defined(NO_SSL)
1947
1948#if !defined(NO_SSL_DL)
1949// set_ssl_option() function updates this array.
1950// It loads SSL library dynamically and changes NULLs to the actual addresses
1951// of respective functions. The macros above (like SSL_connect()) are really
1952// just calling these functions indirectly via the pointer.
1953static struct ssl_func ssl_sw[] = {
1954 {"SSL_free", NULL},
1955 {"SSL_accept", NULL},
1956 {"SSL_connect", NULL},
1957 {"SSL_read", NULL},
1958 {"SSL_write", NULL},
1959 {"SSL_get_error", NULL},
1960 {"SSL_set_fd", NULL},
1961 {"SSL_new", NULL},
1962 {"SSL_CTX_new", NULL},
1963 {"SSLv23_server_method", NULL},
1964 {"SSL_library_init", NULL},
1965 {"SSL_CTX_use_PrivateKey_file", NULL},
1966 {"SSL_CTX_use_certificate_file",NULL},
1967 {"SSL_CTX_set_default_passwd_cb",NULL},
1968 {"SSL_CTX_free", NULL},
1969 {"SSL_load_error_strings", NULL},
1970 {"SSL_CTX_use_certificate_chain_file", NULL},
1971 {"SSLv23_client_method", NULL},
1972 {"SSL_pending", NULL},
1973 {"SSL_CTX_set_verify", NULL},
1974 {"SSL_shutdown", NULL},
1975 {"SSL_CTX_ctrl", NULL},
1976 {"SSL_CTX_set_cipher_list", NULL},
1977 {"EC_KEY_new_by_curve_name", NULL},
1978 {"EC_KEY_free", NULL},
1979 {"SSL_get_cipher_list", NULL},
1980 {NULL, NULL}
1981};
1982
1983// Similar array as ssl_sw. These functions could be located in different lib.
1984static struct ssl_func crypto_sw[] = {
1985 {"CRYPTO_num_locks", NULL},
1986 {"CRYPTO_set_locking_callback", NULL},
1987 {"CRYPTO_set_id_callback", NULL},
1988 {"ERR_get_error", NULL},
1989 {"ERR_error_string", NULL},
1990 {NULL, NULL}
1991};
1992#endif
1993
1995
1996static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) {
1997 return (conn->ssl = SSL_new(s)) != NULL &&
1998 SSL_set_fd(conn->ssl, conn->client.sock) == 1 &&
1999 func(conn->ssl) == 1;
2000}
2001
2002// Return OpenSSL error message
2003static const char *ssl_error(void) {
2004 unsigned long err;
2005 err = ERR_get_error();
2006 return err == 0 ? "" : ERR_error_string(err, NULL);
2007}
2008
2009static void ssl_locking_callback(int mode, int mutex_num, const char *file,
2010 int line) {
2011 (void) line;
2012 (void) file;
2013
2014 if (mode & 1) { // 1 is CRYPTO_LOCK
2016 } else {
2018 }
2019}
2020
2021static unsigned long ssl_id_callback(void) {
2022 return (unsigned long) pthread_self();
2023}
2024
2025#if !defined(NO_SSL_DL)
2026static int load_dll(struct mg_context *ctx, const char *dll_name,
2027 struct ssl_func *sw) {
2028 union {void *p; void (*fp)(void);} u;
2029 void *dll_handle;
2030 struct ssl_func *fp;
2031
2032 if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
2033 cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
2034 return 0;
2035 }
2036
2037 for (fp = sw; fp->name != NULL; fp++) {
2038#ifdef _WIN32
2039 // GetProcAddress() returns pointer to function
2040 u.fp = (void (*)(void)) dlsym(dll_handle, fp->name);
2041#else
2042 // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to
2043 // function pointers. We need to use a union to make a cast.
2044 u.p = dlsym(dll_handle, fp->name);
2045#endif // _WIN32
2046 if (u.fp == NULL) {
2047 cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name);
2048 return 0;
2049 } else {
2050 fp->ptr = u.fp;
2051 }
2052 }
2053
2054 return 1;
2055}
2056#endif // NO_SSL_DL
2057
2058// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
2059static int set_ssl_option(struct mg_context *ctx) {
2060 int i, size;
2061 const char *pem;
2062 int ecdh_enabled = 0;
2063 int ecdh_available = 0;
2064
2065 // If PEM file is not specified and the init_ssl callback
2066 // is not specified, skip SSL initialization.
2067 if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) {
2068 // MG_INIT_SSL
2069 // ctx->callbacks.init_ssl == NULL) {
2070 return 1;
2071 }
2072
2073#if !defined(NO_SSL_DL)
2074 if (!load_dll(ctx, SSL_LIB, ssl_sw) ||
2075 !load_dll(ctx, CRYPTO_LIB, crypto_sw)) {
2076 return 0;
2077 }
2078#endif // NO_SSL_DL
2079
2080 // Initialize SSL library
2083
2084 if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
2085 cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error());
2086 return 0;
2087 }
2088
2091
2092#if 0
2093 {
2094 //
2095 // enable Diffie-Hellman key exchange - the DHE series of ciphers
2096 //
2097 // this code is from here:
2098 // https://www.openssl.org/docs/ssl/SSL_CTX_set_tmp_dh_callback.html
2099 // to generate the pem file: openssl dhparam -out dh_param_2048.pem 2048
2100
2101 /* Set up ephemeral DH parameters. */
2102 void *dh_2048 = NULL;
2103 FILE *paramfile;
2104 paramfile = fopen("dh_param_2048.pem", "r");
2105 if (paramfile) {
2108 } else {
2109 /* Error. */
2110 }
2111 if (dh_2048 == NULL) {
2112 /* Error. */
2113 }
2114 if (SSL_CTX_set_tmp_dh(ctx->ssl_ctx, dh_2048) != 1) {
2115 /* Error. */
2116 }
2117 }
2118#endif
2119
2120#ifdef OPENSSL_EC_NAMED_CURVE
2121 {
2122 //
2123 // enable Elliptic Curve Diffie-Hellman key exchange - the ECDHE series of ciphers
2124 //
2125
2127 if (! ecdh) {
2128 cry(fc(ctx), "%s: EC_KEY_new_by_curve_name (NID_X9_62_prime256v1) failed: %s", __func__, ssl_error());
2129 } else {
2130 if (1 != SSL_CTX_set_tmp_ecdh (ctx->ssl_ctx, ecdh)) {
2131 cry(fc(ctx), "%s: SSL_CTX_set_tmp_ecdh (ctx->ssl_ctx, ecdh) failed: %s", __func__, ssl_error());
2132 }
2133 EC_KEY_free (ecdh);
2134 ecdh_enabled = 1;
2135 }
2136 }
2137#else
2138#warning Very old openssl, no EC_KEY, no ECDH for modern criptography!
2139#endif
2140
2143
2144 // get cipher list
2145
2146 {
2147 int priority;
2148 SSL* ssl = SSL_new(ctx->ssl_ctx);
2149 for (priority=0; 1; priority++) {
2152 break;
2153 //printf("priority %d cipher list: %s\n", priority, available_cipher_list);
2154 if (strstr(available_cipher_list, "ECDHE") != NULL)
2155 ecdh_available = 1;
2156 }
2157 SSL_free(ssl);
2158 }
2159
2160 if (!ecdh_available || !ecdh_enabled) {
2161 cry(fc(ctx), "%s: openssl \"modern cryptography\" ECDH ciphers not available", __func__);
2162 }
2163
2164 // disable weak ciphers
2165 const char* cipher_list = "ALL:!RC4:!DES-CBC-SHA:!DES:!DES-CBC3-SHA:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW";
2166
2167 // enable only "modern cryptography" ciphers if ECDHE ciphers available
2169 cipher_list = "ALL:!AECDH:!RC4:!DES-CBC-SHA:!DES:!AES128-SHA+RSA:!AES256-SHA+RSA:!DES-CBC3-SHA:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW";
2170
2172
2173 // If user callback returned non-NULL, that means that user callback has
2174 // set up certificate itself. In this case, skip sertificate setting.
2175 // MG_INIT_SSL
2176 if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 ||
2177 SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) {
2178 cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
2179 return 0;
2180 }
2181
2182 if (pem != NULL) {
2184 }
2185
2186 // Initialize locking callbacks, needed for thread safety.
2187 // http://www.openssl.org/support/faq.html#PROG1
2188 size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
2189 if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
2190 cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
2191 return 0;
2192 }
2193
2194 for (i = 0; i < CRYPTO_num_locks(); i++) {
2196 }
2197
2200
2201 return 1;
2202}
2203
2204static void uninitialize_ssl(struct mg_context *ctx) {
2205 int i;
2206 if (ctx->ssl_ctx != NULL) {
2208 for (i = 0; i < CRYPTO_num_locks(); i++) {
2210 }
2213 }
2214}
2215#endif // !NO_SSL
2216
2217static SOCKET conn2(const char *host, int port, int use_ssl,
2218 char *ebuf, size_t ebuf_len) {
2219 struct sockaddr_in sin;
2220 struct hostent *he = NULL;
2221 SOCKET sock = INVALID_SOCKET;
2222
2223 (void) use_ssl; // Prevent warning for -DNO_SSL case
2224
2225 if (host == NULL) {
2226 snprintf(ebuf, ebuf_len, "%s", "NULL host");
2227#ifndef NO_SSL
2228 } else if (use_ssl && (void *)SSLv23_client_method == NULL) {
2229 snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized");
2230 // TODO(lsm): use something threadsafe instead of gethostbyname()
2231#endif
2232 } else if ((he = gethostbyname(host)) == NULL) {
2233 snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO));
2234 } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
2235 snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO));
2236 } else {
2237 set_close_on_exec(sock);
2238 sin.sin_family = AF_INET;
2239 sin.sin_port = htons((uint16_t) port);
2240 sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
2241 if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
2242 snprintf(ebuf, ebuf_len, "connect(%s:%d): %s",
2243 host, port, strerror(ERRNO));
2244 closesocket(sock);
2245 sock = INVALID_SOCKET;
2246 }
2247 }
2248 return sock;
2249}
2250
2251struct mg_connection *mg_connect(const char *host, int port, int use_ssl,
2252 char *ebuf, size_t ebuf_len) {
2253 static struct mg_context fake_ctx;
2254 struct mg_connection *conn = NULL;
2255 SOCKET sock;
2256
2257 if ((sock = conn2(host, port, use_ssl, ebuf, ebuf_len)) == INVALID_SOCKET) {
2258 } else if ((conn = (struct mg_connection *)
2259 calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
2260 snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO));
2262#ifndef NO_SSL
2263 } else if (use_ssl && (conn->client_ssl_ctx =
2265 snprintf(ebuf, ebuf_len, "SSL_CTX_new error");
2267 free(conn);
2268 conn = NULL;
2269#endif // NO_SSL
2270 } else {
2271 socklen_t len = sizeof(struct sockaddr);
2272 conn->buf_size = MAX_REQUEST_SIZE;
2273 conn->buf = (char *) (conn + 1);
2274 conn->ctx = &fake_ctx;
2275 conn->client.sock = sock;
2276 getsockname(sock, &conn->client.rsa.sa, &len);
2277 conn->client.is_ssl = use_ssl;
2278#ifndef NO_SSL
2279 if (use_ssl) {
2280 // SSL_CTX_set_verify call is needed to switch off server certificate
2281 // checking, which is off by default in OpenSSL and on in yaSSL.
2283 sslize(conn, conn->client_ssl_ctx, SSL_connect);
2284 }
2285#endif
2286 }
2287
2288 return conn;
2289}
2290
2291struct mg_connection *mg_download(const char *host, int port, int use_ssl,
2292 char *ebuf, size_t ebuf_len,
2293 const char *fmt, ...) {
2294 struct mg_connection *conn;
2295 va_list ap;
2296
2297 va_start(ap, fmt);
2298 ebuf[0] = '\0';
2299 if ((conn = mg_connect(host, port, use_ssl, ebuf, ebuf_len)) == NULL) {
2300 } else if (mg_vprintf(conn, fmt, ap) <= 0) {
2301 snprintf(ebuf, ebuf_len, "%s", "Error sending request");
2302 } else {
2303 getreq(conn, ebuf, ebuf_len);
2304 }
2305 if (ebuf[0] != '\0' && conn != NULL) {
2306 mg_close_connection(conn);
2307 conn = NULL;
2308 }
2309
2310 return conn;
2311}
2312
2313// Return number of bytes left to read for this connection
2314static int64_t left_to_read(const struct mg_connection *conn) {
2315 return conn->content_len + conn->request_len - conn->num_bytes_read;
2316}
2317
2318static int call_user(int type, struct mg_connection *conn, void *p) {
2319 if (conn != NULL && conn->ctx != NULL) {
2320 conn->event.user_data = conn->ctx->user_data;
2321 conn->event.type = type;
2322 conn->event.event_param = p;
2323 conn->event.request_info = &conn->request_info;
2324 conn->event.conn = conn;
2325 }
2326 return conn == NULL || conn->ctx == NULL || conn->ctx->event_handler == NULL ?
2327 0 : conn->ctx->event_handler(&conn->event);
2328}
2329
2330static FILE *mg_fopen(const char *path, const char *mode) {
2331#ifdef _WIN32
2332 wchar_t wbuf[PATH_MAX], wmode[20];
2333 to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
2335 return _wfopen(wbuf, wmode);
2336#else
2337 return fopen(path, mode);
2338#endif
2339}
2340
2341static void sockaddr_to_string(char *buf, size_t len,
2342 const union usa *usa) {
2343 buf[0] = '\0';
2344#if defined(USE_IPV6)
2345 inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
2346 (void *) &usa->sin.sin_addr :
2347 (void *) &usa->sin6.sin6_addr, buf, len);
2348#elif defined(_WIN32)
2349 // Only Windoze Vista (and newer) have inet_ntop()
2350 strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
2351#else
2352 inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
2353#endif
2354}
2355
2356// Print error message to the opened error log stream.
2357static void cry(struct mg_connection *conn, const char *fmt, ...) {
2359 va_list ap;
2360 FILE *fp;
2361 time_t timestamp;
2362
2363 va_start(ap, fmt);
2364 (void) vsnprintf(buf, sizeof(buf), fmt, ap);
2365 va_end(ap);
2366
2367 // Do not lock when getting the callback value, here and below.
2368 // I suppose this is fine, since function cannot disappear in the
2369 // same way string option can.
2370 if (call_user(MG_EVENT_LOG, conn, buf) == 0) {
2371 fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
2372 fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
2373
2374 if (fp != NULL) {
2375 flockfile(fp);
2376 timestamp = time(NULL);
2377
2378 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
2379 fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp,
2380 src_addr);
2381
2382 if (conn->request_info.request_method != NULL) {
2383 fprintf(fp, "%s %s: ", conn->request_info.request_method,
2384 conn->request_info.uri);
2385 }
2386
2387 fprintf(fp, "%s", buf);
2388 fputc('\n', fp);
2389 funlockfile(fp);
2390 fclose(fp);
2391 }
2392 }
2393}
2394
2395const char *mg_version(void) {
2396 return MONGOOSE_VERSION;
2397}
2398
2399// HTTP 1.1 assumes keep alive if "Connection:" header is not set
2400// This function must tolerate situations when connection info is not
2401// set up, for example if request parsing failed.
2402static int should_keep_alive(const struct mg_connection *conn) {
2403 const char *http_version = conn->request_info.http_version;
2404 const char *header = mg_get_header(conn, "Connection");
2405 if (conn->must_close ||
2406 conn->status_code == 401 ||
2407 mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 ||
2408 (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) ||
2409 (header == NULL && http_version && strcmp(http_version, "1.1"))) {
2410 return 0;
2411 }
2412 return 1;
2413}
2414
2415static const char *suggest_connection_header(const struct mg_connection *conn) {
2416 return should_keep_alive(conn) ? "keep-alive" : "close";
2417}
2418
2419static void send_http_error(struct mg_connection *conn, int status,
2420 const char *reason, const char *fmt, ...) {
2421 char buf[MG_BUF_LEN];
2422 va_list ap;
2423 int len = 0;
2424
2425 conn->status_code = status;
2426 buf[0] = '\0';
2427
2428 // Errors 1xx, 204 and 304 MUST NOT send a body
2429 if (status > 199 && status != 204 && status != 304) {
2430 len = mg_snprintf(buf, sizeof(buf), "Error %d: %s", status, reason);
2431 buf[len++] = '\n';
2432
2433 va_start(ap, fmt);
2434 len += mg_vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
2435 va_end(ap);
2436 }
2437 DEBUG_TRACE(("[%s]", buf));
2438
2439 if (call_user(MG_HTTP_ERROR, conn, (void *) (long) status) == 0) {
2440 mg_printf(conn, "HTTP/1.1 %d %s\r\n"
2441 "Content-Length: %d\r\n"
2442 "Connection: %s\r\n\r\n", status, reason, len,
2444 conn->num_bytes_sent += mg_printf(conn, "%s", buf);
2445 }
2446}
2447
2448// Write data to the IO channel - opened file descriptor, socket or SSL
2449// descriptor. Return number of bytes written.
2450static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
2451 int64_t len) {
2452 int64_t sent;
2453 int n, k;
2454
2455 (void) ssl; // Get rid of warning
2456 sent = 0;
2457 while (sent < len) {
2458
2459 // How many bytes we send in this iteration
2460 k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
2461
2462#if !defined(NO_SSL)
2463 if (ssl != NULL) {
2464 n = SSL_write(ssl, buf + sent, k);
2465 } else
2466#endif
2467 if (fp != NULL) {
2468 n = (int) fwrite(buf + sent, 1, (size_t) k, fp);
2469 if (ferror(fp))
2470 n = -1;
2471 } else {
2472 n = send(sock, buf + sent, (size_t) k, MSG_NOSIGNAL);
2473 }
2474
2475 if (n <= 0)
2476 break;
2477
2478 sent += n;
2479 }
2480
2481 return sent;
2482}
2483
2484// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
2485// Return negative value on error, or number of bytes read on success.
2486static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
2487 int nread;
2488
2489 if (len <= 0) return 0;
2490 if (fp != NULL) {
2491 // Use read() instead of fread(), because if we're reading from the CGI
2492 // pipe, fread() may block until IO buffer is filled up. We cannot afford
2493 // to block and must pass all read bytes immediately to the client.
2494 nread = read(fileno(fp), buf, (size_t) len);
2495#ifndef NO_SSL
2496 } else if (conn->ssl != NULL) {
2497 nread = SSL_read(conn->ssl, buf, len);
2498#endif
2499 } else {
2500 nread = recv(conn->client.sock, buf, (size_t) len, 0);
2501 }
2502 if (nread > 0) {
2503 conn->num_bytes_read += nread;
2504 }
2505
2506 return conn->ctx->stop_flag ? -1 : nread;
2507}
2508
2509static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) {
2510 int n, nread = 0;
2511
2512 while (len > 0 && conn->ctx->stop_flag == 0) {
2513 n = pull(fp, conn, buf + nread, len);
2514 if (n < 0) {
2515 nread = n; // Propagate the error
2516 break;
2517 } else if (n == 0) {
2518 break; // No more data to read
2519 } else {
2520 nread += n;
2521 len -= n;
2522 }
2523 }
2524
2525 return nread;
2526}
2527
2528int mg_read(struct mg_connection *conn, void *buf, int len) {
2529 int n, buffered_len, nread = 0;
2530 int64_t left;
2531
2532 if (conn->content_len <= 0) {
2533 return 0;
2534 }
2535
2536 // conn->buf body
2537 // |=================|==========|===============|
2538 // |<--request_len-->| |
2539 // |<-----------data_len------->| conn->buf + conn->buf_size
2540
2541 // First, check for data buffered in conn->buf by read_request().
2542 if (len > 0 && (buffered_len = conn->data_len - conn->request_len) > 0) {
2543 char *body = conn->buf + conn->request_len;
2544 if (buffered_len > len) buffered_len = len;
2545 if (buffered_len > conn->content_len) buffered_len = (int)conn->content_len;
2546
2547 memcpy(buf, body, (size_t) buffered_len);
2548 memmove(body, body + buffered_len,
2549 &conn->buf[conn->data_len] - &body[buffered_len]);
2550 len -= buffered_len;
2551 conn->data_len -= buffered_len;
2553 }
2554
2555 // Read data from the socket.
2556 if (len > 0 && (left = left_to_read(conn)) > 0) {
2557 if (left < len) {
2558 len = (int) left;
2559 }
2560 n = pull_all(NULL, conn, (char *) buf + nread, (int) len);
2561 nread = n >= 0 ? nread + n : n;
2562 }
2563
2564 return nread;
2565}
2566
2567int mg_write(struct mg_connection *conn, const void *buf, int len) {
2568 time_t now;
2570
2571 if (conn->throttle > 0) {
2572 if ((now = time(NULL)) != conn->last_throttle_time) {
2573 conn->last_throttle_time = now;
2574 conn->last_throttle_bytes = 0;
2575 }
2576 allowed = conn->throttle - conn->last_throttle_bytes;
2577 if (allowed > (int64_t) len) {
2578 allowed = len;
2579 }
2580 if ((total = push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
2581 (int64_t) allowed)) == allowed) {
2582 buf = (char *) buf + total;
2583 conn->last_throttle_bytes += total;
2584 while (total < (int64_t) len && conn->ctx->stop_flag == 0) {
2585 allowed = conn->throttle > (int64_t) len - total ?
2586 (int64_t) len - total : conn->throttle;
2587 if ((n = push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
2588 (int64_t) allowed)) != allowed) {
2589 break;
2590 }
2591 sleep(1);
2593 conn->last_throttle_time = time(NULL);
2594 buf = (char *) buf + n;
2595 total += n;
2596 }
2597 }
2598 } else {
2599 total = push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
2600 (int64_t) len);
2601 }
2602 return (int) total;
2603}
2604
2605int mg_url_decode(const char *src, int src_len, char *dst,
2606 int dst_len, int is_form_url_encoded) {
2607 int i, j, a, b;
2608#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
2609
2610 for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
2611 if (src[i] == '%' && i < src_len - 2 &&
2612 isxdigit(* (const unsigned char *) (src + i + 1)) &&
2613 isxdigit(* (const unsigned char *) (src + i + 2))) {
2614 a = tolower(* (const unsigned char *) (src + i + 1));
2615 b = tolower(* (const unsigned char *) (src + i + 2));
2616 dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
2617 i += 2;
2618 } else if (is_form_url_encoded && src[i] == '+') {
2619 dst[j] = ' ';
2620 } else {
2621 dst[j] = src[i];
2622 }
2623 }
2624
2625 dst[j] = '\0'; // Null-terminate the destination
2626
2627 return i >= src_len ? j : -1;
2628}
2629
2630int mg_get_var(const char *data, size_t data_len, const char *name,
2631 char *dst, size_t dst_len) {
2632 const char *p, *e, *s;
2633 size_t name_len;
2634 int len;
2635
2636 if (dst == NULL || dst_len == 0) {
2637 len = -2;
2638 } else if (data == NULL || name == NULL || data_len == 0) {
2639 len = -1;
2640 dst[0] = '\0';
2641 } else {
2642 name_len = strlen(name);
2643 e = data + data_len;
2644 len = -1;
2645 dst[0] = '\0';
2646
2647 // data is "var1=val1&var2=val2...". Find variable first
2648 for (p = data; p + name_len < e; p++) {
2649 if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
2650 !mg_strncasecmp(name, p, name_len)) {
2651
2652 // Point p to variable value
2653 p += name_len + 1;
2654
2655 // Point s to the end of the value
2656 s = (const char *) memchr(p, '&', (size_t)(e - p));
2657 if (s == NULL) {
2658 s = e;
2659 }
2660 assert(s >= p);
2661
2662 // Decode variable into destination buffer
2663 len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
2664
2665 // Redirect error code from -1 to -2 (destination buffer too small).
2666 if (len == -1) {
2667 len = -2;
2668 }
2669 break;
2670 }
2671 }
2672 }
2673
2674 return len;
2675}
2676
2677int mg_get_cookie(const char *cookie_header, const char *var_name,
2678 char *dst, size_t dst_size) {
2679 const char *s, *p, *end;
2680 int name_len, len = -1;
2681
2682 if (dst == NULL || dst_size == 0) {
2683 len = -2;
2684 } else if (var_name == NULL || (s = cookie_header) == NULL) {
2685 len = -1;
2686 dst[0] = '\0';
2687 } else {
2688 name_len = (int) strlen(var_name);
2689 end = s + strlen(s);
2690 dst[0] = '\0';
2691
2692 for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
2693 if (s[name_len] == '=') {
2694 s += name_len + 1;
2695 if ((p = strchr(s, ' ')) == NULL)
2696 p = end;
2697 if (p[-1] == ';')
2698 p--;
2699 if (*s == '"' && p[-1] == '"' && p > s + 1) {
2700 s++;
2701 p--;
2702 }
2703 if ((size_t) (p - s) < dst_size) {
2704 len = p - s;
2705 mg_strlcpy(dst, s, (size_t) len + 1);
2706 } else {
2707 len = -3;
2708 }
2709 break;
2710 }
2711 }
2712 }
2713 return len;
2714}
2715
2716// Return 1 if real file has been found, 0 otherwise
2717static int convert_uri_to_file_name(struct mg_connection *conn, char *buf,
2718 size_t buf_len, struct file *filep) {
2719 struct vec a, b;
2720 const char *rewrite, *uri = conn->request_info.uri,
2721 *root = conn->ctx->config[DOCUMENT_ROOT];
2722 char *p;
2723 int match_len;
2724 char gz_path[PATH_MAX];
2725 char const* accept_encoding;
2726
2727 // No filesystem access
2728 if (root == NULL) {
2729 return 0;
2730 }
2731
2732 // Using buf_len - 1 because memmove() for PATH_INFO may shift part
2733 // of the path one byte on the right.
2734 // If document_root is NULL, leave the file empty.
2735 mg_snprintf(buf, buf_len - 1, "%s%s", root, uri);
2736
2737 rewrite = conn->ctx->config[REWRITE];
2738 while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
2739 if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
2740 mg_snprintf(buf, buf_len - 1, "%.*s%s", (int) b.len, b.ptr,
2741 uri + match_len);
2742 break;
2743 }
2744 }
2745
2746 if (mg_stat(buf, filep)) {
2747 return 1;
2748 }
2749
2750 // if we can't find the actual file, look for the file
2751 // with the same name but a .gz extension. If we find it,
2752 // use that and set the gzipped flag in the file struct
2753 // to indicate that the response need to have the content-
2754 // encoding: gzip header
2755 // we can only do this if the browser declares support
2756 if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
2757 if (strstr(accept_encoding,"gzip") != NULL) {
2758 snprintf(gz_path, sizeof(gz_path), "%s.gz", buf);
2759 if (mg_stat(gz_path, filep)) {
2760 filep->gzipped = 1;
2761 return 1;
2762 }
2763 }
2764 }
2765
2766 // Support PATH_INFO for CGI scripts.
2767 for (p = buf + strlen(root == NULL ? "" : root); *p != '\0'; p++) {
2768 if (*p == '/') {
2769 *p = '\0';
2771 strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 &&
2772 mg_stat(buf, filep)) {
2773 // Shift PATH_INFO block one character right, e.g.
2774 // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
2775 // conn->path_info is pointing to the local variable "path" declared
2776 // in handle_request(), so PATH_INFO is not valid after
2777 // handle_request returns.
2778 conn->path_info = p + 1;
2779 memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0
2780 p[1] = '/';
2781 return 1;
2782 } else {
2783 *p = '/';
2784 }
2785 }
2786 }
2787
2788 return 0;
2789}
2790
2791// Check whether full request is buffered. Return:
2792// -1 if request is malformed
2793// 0 if request is not yet fully buffered
2794// >0 actual request length, including last \r\n\r\n
2795static int get_request_len(const char *buf, int buf_len) {
2796 int i;
2797
2798 for (i = 0; i < buf_len; i++) {
2799 // Control characters are not allowed but >=128 is.
2800 // Abort scan as soon as one malformed character is found;
2801 // don't let subsequent \r\n\r\n win us over anyhow
2802 if (!isprint(* (const unsigned char *) &buf[i]) && buf[i] != '\r' &&
2803 buf[i] != '\n' && * (const unsigned char *) &buf[i] < 128) {
2804 return -1;
2805 } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
2806 return i + 2;
2807 } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
2808 buf[i + 2] == '\n') {
2809 return i + 3;
2810 }
2811 }
2812
2813 return 0;
2814}
2815
2816// Protect against directory disclosure attack by removing '..',
2817// excessive '/' and '\' characters
2818static void remove_double_dots_and_double_slashes(char *s) {
2819 char *p = s;
2820
2821 while (*s != '\0') {
2822 *p++ = *s++;
2823 if (s[-1] == '/' || s[-1] == '\\') {
2824 // Skip all following slashes, backslashes and double-dots
2825 while (s[0] != '\0') {
2826 if (s[0] == '/' || s[0] == '\\') {
2827 s++;
2828 } else if (s[0] == '.' && s[1] == '.') {
2829 s += 2;
2830 } else {
2831 break;
2832 }
2833 }
2834 }
2835 }
2836 *p = '\0';
2837}
2838
2839static const struct {
2840 const char *extension;
2841 size_t ext_len;
2842 const char *mime_type;
2843} builtin_mime_types[] = {
2844 {".html", 5, "text/html"},
2845 {".htm", 4, "text/html"},
2846 {".shtm", 5, "text/html"},
2847 {".shtml", 6, "text/html"},
2848 {".css", 4, "text/css"},
2849 {".js", 3, "application/x-javascript"},
2850 {".ico", 4, "image/x-icon"},
2851 {".gif", 4, "image/gif"},
2852 {".jpg", 4, "image/jpeg"},
2853 {".jpeg", 5, "image/jpeg"},
2854 {".png", 4, "image/png"},
2855 {".svg", 4, "image/svg+xml"},
2856 {".txt", 4, "text/plain"},
2857 {".torrent", 8, "application/x-bittorrent"},
2858 {".wav", 4, "audio/x-wav"},
2859 {".mp3", 4, "audio/x-mp3"},
2860 {".mid", 4, "audio/mid"},
2861 {".m3u", 4, "audio/x-mpegurl"},
2862 {".ogg", 4, "application/ogg"},
2863 {".ram", 4, "audio/x-pn-realaudio"},
2864 {".xml", 4, "text/xml"},
2865 {".json", 5, "text/json"},
2866 {".xslt", 5, "application/xml"},
2867 {".xsl", 4, "application/xml"},
2868 {".ra", 3, "audio/x-pn-realaudio"},
2869 {".doc", 4, "application/msword"},
2870 {".exe", 4, "application/octet-stream"},
2871 {".zip", 4, "application/x-zip-compressed"},
2872 {".xls", 4, "application/excel"},
2873 {".tgz", 4, "application/x-tar-gz"},
2874 {".tar", 4, "application/x-tar"},
2875 {".gz", 3, "application/x-gunzip"},
2876 {".arj", 4, "application/x-arj-compressed"},
2877 {".rar", 4, "application/x-arj-compressed"},
2878 {".rtf", 4, "application/rtf"},
2879 {".pdf", 4, "application/pdf"},
2880 {".swf", 4, "application/x-shockwave-flash"},
2881 {".mpg", 4, "video/mpeg"},
2882 {".webm", 5, "video/webm"},
2883 {".mpeg", 5, "video/mpeg"},
2884 {".mov", 4, "video/quicktime"},
2885 {".mp4", 4, "video/mp4"},
2886 {".m4v", 4, "video/x-m4v"},
2887 {".asf", 4, "video/x-ms-asf"},
2888 {".avi", 4, "video/x-msvideo"},
2889 {".bmp", 4, "image/bmp"},
2890 {".ttf", 4, "application/x-font-ttf"},
2891 {NULL, 0, NULL}
2892};
2893
2894const char *mg_get_builtin_mime_type(const char *path) {
2895 const char *ext;
2896 size_t i, path_len;
2897
2898 path_len = strlen(path);
2899
2900 for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
2901 ext = path + (path_len - builtin_mime_types[i].ext_len);
2904 return builtin_mime_types[i].mime_type;
2905 }
2906 }
2907
2908 return "text/plain";
2909}
2910
2911// Look at the "path" extension and figure what mime type it has.
2912// Store mime type in the vector.
2913static void get_mime_type(struct mg_context *ctx, const char *path,
2914 struct vec *vec) {
2915 struct vec ext_vec, mime_vec;
2916 const char *list, *ext;
2917 size_t path_len;
2918
2919 path_len = strlen(path);
2920
2921 // Scan user-defined mime types first, in case user wants to
2922 // override default mime types.
2924 while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
2925 // ext now points to the path suffix
2926 ext = path + path_len - ext_vec.len;
2927 if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
2928 *vec = mime_vec;
2929 return;
2930 }
2931 }
2932
2934 vec->len = strlen(vec->ptr);
2935}
2936
2937void mg_url_encode(const char *src, char *dst, size_t dst_len) {
2938 static const char *dont_escape = "._-$,;~()";
2939 static const char *hex = "0123456789abcdef";
2940 const char *end = dst + dst_len - 1;
2941
2942 for (; *src != '\0' && dst < end; src++, dst++) {
2943 if (isalnum(*(const unsigned char *) src) ||
2944 strchr(dont_escape, * (const unsigned char *) src) != NULL) {
2945 *dst = *src;
2946 } else if (dst + 2 < end) {
2947 dst[0] = '%';
2948 dst[1] = hex[(* (const unsigned char *) src) >> 4];
2949 dst[2] = hex[(* (const unsigned char *) src) & 0xf];
2950 dst += 2;
2951 }
2952 }
2953
2954 *dst = '\0';
2955}
2956
2957static void print_dir_entry(const struct de *de) {
2958 char size[64], mod[64], href[PATH_MAX * 3];
2959 const char *slash = de->file.is_directory ? "/" : "";
2960
2961 if (de->file.is_directory) {
2962 mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]");
2963 } else {
2964 // We use (signed) cast below because MSVC 6 compiler cannot
2965 // convert unsigned __int64 to double. Sigh.
2966 if (de->file.size < 1024) {
2967 mg_snprintf(size, sizeof(size), "%d", (int) de->file.size);
2968 } else if (de->file.size < 0x100000) {
2969 mg_snprintf(size, sizeof(size),
2970 "%.1fk", (double) de->file.size / 1024.0);
2971 } else if (de->file.size < 0x40000000) {
2972 mg_snprintf(size, sizeof(size),
2973 "%.1fM", (double) de->file.size / 1048576);
2974 } else {
2975 mg_snprintf(size, sizeof(size),
2976 "%.1fG", (double) de->file.size / 1073741824);
2977 }
2978 }
2979 strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
2981 mg_url_encode(de->file_name, href, sizeof(href));
2983 "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
2984 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
2986}
2987
2988// This function is called from send_directory() and used for
2989// sorting directory entries by size, or name, or modification time.
2990// On windows, __cdecl specification is needed in case if project is built
2991// with __stdcall convention. qsort always requires __cdels callback.
2992static int WINCDECL compare_dir_entries(const void *p1, const void *p2) {
2993 const struct de *a = (const struct de *) p1, *b = (const struct de *) p2;
2994 const char *query_string = a->conn->request_info.query_string;
2995 int cmp_result = 0;
2996
2997 if (query_string == NULL) {
2998 query_string = "na";
2999 }
3000
3001 if (a->file.is_directory && !b->file.is_directory) {
3002 return -1; // Always put directories on top
3003 } else if (!a->file.is_directory && b->file.is_directory) {
3004 return 1; // Always put directories on top
3005 } else if (*query_string == 'n') {
3006 cmp_result = strcmp(a->file_name, b->file_name);
3007 } else if (*query_string == 's') {
3008 cmp_result = a->file.size == b->file.size ? 0 :
3009 a->file.size > b->file.size ? 1 : -1;
3010 } else if (*query_string == 'd') {
3011 cmp_result = a->file.modification_time == b->file.modification_time ? 0 :
3012 a->file.modification_time > b->file.modification_time ? 1 : -1;
3013 }
3014
3015 return query_string[1] == 'd' ? -cmp_result : cmp_result;
3016}
3017
3018static int must_hide_file(struct mg_connection *conn, const char *path) {
3019 const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
3020 const char *pattern = conn->ctx->config[HIDE_FILES];
3021 return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
3022 (pattern != NULL && match_prefix(pattern, strlen(pattern), path) > 0);
3023}
3024
3025static int scan_directory(struct mg_connection *conn, const char *dir,
3026 void *data, void (*cb)(struct de *, void *)) {
3027 char path[PATH_MAX];
3028 struct dirent *dp;
3029 DIR *dirp;
3030 struct de de;
3031
3032 if ((dirp = opendir(dir)) == NULL) {
3033 return 0;
3034 } else {
3035 de.conn = conn;
3036
3037 while ((dp = readdir(dirp)) != NULL) {
3038 // Do not show current dir and hidden files
3039 if (!strcmp(dp->d_name, ".") ||
3040 !strcmp(dp->d_name, "..") ||
3041 must_hide_file(conn, dp->d_name)) {
3042 continue;
3043 }
3044
3045 mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
3046
3047 // If we don't memset stat structure to zero, mtime will have
3048 // garbage and strftime() will segfault later on in
3049 // print_dir_entry(). memset is required only if mg_stat()
3050 // fails. For more details, see
3051 // http://code.google.com/p/mongoose/issues/detail?id=79
3052 memset(&de.file, 0, sizeof(de.file));
3053 mg_stat(path, &de.file);
3054
3055 de.file_name = dp->d_name;
3056 cb(&de, data);
3057 }
3058 (void) closedir(dirp);
3059 }
3060 return 1;
3061}
3062
3063static int remove_directory(struct mg_connection *conn, const char *dir) {
3064 char path[PATH_MAX];
3065 struct dirent *dp;
3066 DIR *dirp;
3067 struct de de;
3068
3069 if ((dirp = opendir(dir)) == NULL) {
3070 return 0;
3071 } else {
3072 de.conn = conn;
3073
3074 while ((dp = readdir(dirp)) != NULL) {
3075 // Do not show current dir, but show hidden files
3076 if (!strcmp(dp->d_name, ".") ||
3077 !strcmp(dp->d_name, "..")) {
3078 continue;
3079 }
3080
3081 mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
3082
3083 // If we don't memset stat structure to zero, mtime will have
3084 // garbage and strftime() will segfault later on in
3085 // print_dir_entry(). memset is required only if mg_stat()
3086 // fails. For more details, see
3087 // http://code.google.com/p/mongoose/issues/detail?id=79
3088 memset(&de.file, 0, sizeof(de.file));
3089 mg_stat(path, &de.file);
3091 if(de.file.is_directory) {
3092 remove_directory(conn, path);
3093 } else {
3094 mg_remove(path);
3095 }
3096 }
3097
3098 }
3099 (void) closedir(dirp);
3100
3101 rmdir(dir);
3102 }
3103
3104 return 1;
3105}
3106
3107struct dir_scan_data {
3108 struct de *entries;
3109 int num_entries;
3110 int arr_size;
3111};
3112
3113// Behaves like realloc(), but frees original pointer on failure
3114static void *realloc2(void *ptr, size_t size) {
3115 void *new_ptr = realloc(ptr, size);
3116 if (new_ptr == NULL) {
3117 free(ptr);
3118 }
3119 return new_ptr;
3120}
3121
3122static void dir_scan_callback(struct de *de, void *data) {
3123 struct dir_scan_data *dsd = (struct dir_scan_data *) data;
3124
3125 if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) {
3126 dsd->arr_size *= 2;
3127 dsd->entries = (struct de *) realloc2(dsd->entries, dsd->arr_size *
3128 sizeof(dsd->entries[0]));
3129 }
3130 if (dsd->entries == NULL) {
3131 // TODO(lsm): propagate an error to the caller
3132 dsd->num_entries = 0;
3133 } else {
3134 dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
3135 dsd->entries[dsd->num_entries].file = de->file;
3136 dsd->entries[dsd->num_entries].conn = de->conn;
3137 dsd->num_entries++;
3138 }
3139}
3140
3141static void handle_directory_request(struct mg_connection *conn,
3142 const char *dir) {
3143 int i, sort_direction;
3144 struct dir_scan_data data = { NULL, 0, 128 };
3145
3146 if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
3147 send_http_error(conn, 500, "Cannot open directory",
3148 "Error: opendir(%s): %s", dir, strerror(ERRNO));
3149 return;
3150 }
3151
3153 conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
3154
3155 conn->must_close = 1;
3156 mg_printf(conn, "%s",
3157 "HTTP/1.1 200 OK\r\n"
3158 "Transfer-Encoding: Chunked\r\n"
3159 "Content-Type: text/html; charset=utf-8\r\n\r\n");
3160
3161 conn->num_bytes_sent += mg_chunked_printf(conn,
3162 "<html><head><title>Index of %s</title>"
3163 "<style>th {text-align: left;}</style></head>"
3164 "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
3165 "<tr><th><a href=\"?n%c\">Name</a></th>"
3166 "<th><a href=\"?d%c\">Modified</a></th>"
3167 "<th><a href=\"?s%c\">Size</a></th></tr>"
3168 "<tr><td colspan=\"3\"><hr></td></tr>",
3169 conn->request_info.uri, conn->request_info.uri,
3171
3172 // Print first entry - link to a parent directory
3173 conn->num_bytes_sent += mg_chunked_printf(conn,
3174 "<tr><td><a href=\"%s%s\">%s</a></td>"
3175 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
3176 conn->request_info.uri, "..", "Parent directory", "-", "-");
3177
3178 // Sort and print directory entries
3179 qsort(data.entries, (size_t) data.num_entries, sizeof(data.entries[0]),
3181 for (i = 0; i < data.num_entries; i++) {
3182 print_dir_entry(&data.entries[i]);
3183 free(data.entries[i].file_name);
3184 }
3185 free(data.entries);
3186
3187 conn->num_bytes_sent += mg_chunked_printf(conn, "%s",
3188 "</table></body></html>");
3189 conn->num_bytes_sent += mg_write(conn, "0\r\n\r\n", 5);
3190 conn->status_code = 200;
3191}
3192
3193// Send len bytes from the opened file to the client.
3194static void send_file_data(struct mg_connection *conn, FILE *fp,
3195 int64_t offset, int64_t len) {
3196 char buf[MG_BUF_LEN];
3198
3199 // If offset is beyond file boundaries, don't send anything
3200 if (offset > 0 && fseeko(fp, offset, SEEK_SET) != 0) {
3201 return;
3202 }
3203
3204 while (len > 0) {
3205 // Calculate how much to read from the file in the buffer
3206 to_read = sizeof(buf);
3207 if ((int64_t) to_read > len) {
3208 to_read = (int) len;
3209 }
3210
3211 // Read from file, exit the loop on error
3212 if ((num_read = fread(buf, 1, (size_t) to_read, fp)) <= 0) {
3213 break;
3214 }
3215
3216 // Send read bytes to the client, exit the loop on error
3217 if ((num_written = mg_write(conn, buf, (size_t) num_read)) != num_read) {
3218 break;
3219 }
3220
3221 // Both read and were successful, adjust counters
3222 conn->num_bytes_sent += num_written;
3223 len -= num_written;
3224 }
3225}
3226
3227static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
3228 return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
3229}
3230
3231static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
3232 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
3233}
3234
3235static void construct_etag(char *buf, size_t buf_len,
3236 const struct file *filep) {
3237 snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
3238 (unsigned long) filep->modification_time, filep->size);
3239}
3240
3241static void fclose_on_exec(FILE *fp) {
3242 if (fp != NULL) {
3243#ifndef _WIN32
3245#endif
3246 }
3247}
3248
3249static void handle_file_request(struct mg_connection *conn, const char *path,
3250 struct file *filep) {
3251 char date[64], lm[64], etag[64], range[64];
3252 const char *msg = "OK", *hdr;
3253 time_t curtime = time(NULL);
3254 int64_t cl, r1, r2;
3255 struct vec mime_vec;
3256 int n;
3257 char gz_path[PATH_MAX];
3258 char const* encoding = "";
3259 FILE *fp;
3260
3261 get_mime_type(conn->ctx, path, &mime_vec);
3262 cl = filep->size;
3263 conn->status_code = 200;
3264 range[0] = '\0';
3265
3266 // if this file is in fact a pre-gzipped file, rewrite its filename
3267 // it's important to rewrite the filename after resolving
3268 // the mime type from it, to preserve the actual file's type
3269 if (filep->gzipped) {
3270 snprintf(gz_path, sizeof(gz_path), "%s.gz", path);
3271 path = gz_path;
3272 encoding = "Content-Encoding: gzip\r\n";
3273 }
3274
3275 if ((fp = mg_fopen(path, "rb")) == NULL) {
3277 "fopen(%s): %s", path, strerror(ERRNO));
3278 return;
3279 }
3280
3282
3283 // If Range: header specified, act accordingly
3284 r1 = r2 = 0;
3285 hdr = mg_get_header(conn, "Range");
3286 if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
3287 r1 >= 0 && r2 >= 0) {
3288 // actually, range requests don't play well with a pre-gzipped
3289 // file (since the range is specified in the uncmpressed space)
3290 if (filep->gzipped) {
3291 send_http_error(conn, 501, "Not Implemented",
3292 "range requests in gzipped files are not supported");
3293 return;
3294 }
3295 conn->status_code = 206;
3296 cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1;
3297 mg_snprintf(range, sizeof(range),
3298 "Content-Range: bytes "
3299 "%" INT64_FMT "-%"
3300 INT64_FMT "/%" INT64_FMT "\r\n",
3301 r1, r1 + cl - 1, filep->size);
3302 msg = "Partial Content";
3303 }
3304
3305 // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
3306 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
3307 gmt_time_string(date, sizeof(date), &curtime);
3308 gmt_time_string(lm, sizeof(lm), &filep->modification_time);
3309 construct_etag(etag, sizeof(etag), filep);
3310
3311 (void) mg_printf(conn,
3312 "HTTP/1.1 %d %s\r\n"
3313 "Date: %s\r\n"
3314 "Last-Modified: %s\r\n"
3315 "Etag: %s\r\n"
3316 "Content-Type: %.*s\r\n"
3317 "Content-Length: %" INT64_FMT "\r\n"
3318 "Connection: %s\r\n"
3319 "Accept-Ranges: bytes\r\n"
3320 "%s%s%s\r\n",
3321 conn->status_code, msg, date, lm, etag, (int) mime_vec.len,
3322 mime_vec.ptr, cl, suggest_connection_header(conn), range, encoding,
3324
3325 if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
3326 send_file_data(conn, fp, r1, cl);
3327 }
3328 fclose(fp);
3329}
3330
3331void mg_send_file(struct mg_connection *conn, const char *path) {
3333 if (mg_stat(path, &file)) {
3334 handle_file_request(conn, path, &file);
3335 } else {
3336 send_http_error(conn, 404, "Not Found", "%s", "File not found");
3337 }
3338}
3339
3340
3341// Parse HTTP headers from the given buffer, advance buffer to the point
3342// where parsing stopped.
3343static void parse_http_headers(char **buf, struct mg_request_info *ri) {
3344 int i;
3345
3346 for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
3347 ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
3348 ri->http_headers[i].value = skip(buf, "\r\n");
3349 if (ri->http_headers[i].name[0] == '\0')
3350 break;
3351 ri->num_headers = i + 1;
3352 }
3353}
3354
3355static int is_valid_http_method(const char *method) {
3356 return !strcmp(method, "GET") || !strcmp(method, "POST") ||
3357 !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
3358 !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
3359 !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND")
3360 || !strcmp(method, "MKCOL")
3361 ;
3362}
3363
3364// Parse HTTP request, fill in mg_request_info structure.
3365// This function modifies the buffer by NUL-terminating
3366// HTTP request components, header names and header values.
3367static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
3368 int is_request, request_length = get_request_len(buf, len);
3369 if (request_length > 0) {
3370 // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
3371 ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL;
3372 ri->num_headers = 0;
3373
3374 buf[request_length - 1] = '\0';
3375
3376 // RFC says that all initial whitespaces should be ingored
3377 while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
3378 buf++;
3379 }
3380 ri->request_method = skip(&buf, " ");
3381 ri->uri = skip(&buf, " ");
3382 ri->http_version = skip(&buf, "\r\n");
3383
3384 // HTTP message could be either HTTP request or HTTP response, e.g.
3385 // "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..."
3386 is_request = is_valid_http_method(ri->request_method);
3387 if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) ||
3388 (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) {
3389 request_length = -1;
3390 } else {
3391 if (is_request) {
3392 ri->http_version += 5;
3393 }
3394 parse_http_headers(&buf, ri);
3395 }
3396 }
3397 return request_length;
3398}
3399
3400// Keep reading the input (either opened file descriptor fd, or socket sock,
3401// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
3402// buffer (which marks the end of HTTP request). Buffer buf may already
3403// have some data. The length of the data is stored in nread.
3404// Upon every read operation, increase nread by the number of bytes read.
3405static int read_request(FILE *fp, struct mg_connection *conn,
3406 char *buf, int bufsiz, int *nread) {
3407 int request_len, n = 0;
3408
3409 request_len = get_request_len(buf, *nread);
3410 while (conn->ctx->stop_flag == 0 &&
3411 *nread < bufsiz &&
3412 request_len == 0 &&
3413 (n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
3414 *nread += n;
3415 assert(*nread <= bufsiz);
3416 request_len = get_request_len(buf, *nread);
3417 }
3418
3419 return request_len <= 0 && n <= 0 ? -1 : request_len;
3420}
3421
3422// For given directory path, substitute it to valid index file.
3423// Return 0 if index file has been found, -1 if not found.
3424// If the file is found, it's stats is returned in stp.
3425static int substitute_index_file(struct mg_connection *conn, char *path,
3426 size_t path_len, struct file *filep) {
3427 const char *list = conn->ctx->config[INDEX_FILES];
3429 struct vec filename_vec;
3430 size_t n = strlen(path);
3431 int found = 0;
3432
3433 // The 'path' given to us points to the directory. Remove all trailing
3434 // directory separator characters from the end of the path, and
3435 // then append single directory separator character.
3436 while (n > 0 && path[n - 1] == '/') {
3437 n--;
3438 }
3439 path[n] = '/';
3440
3441 // Traverse index files list. For each entry, append it to the given
3442 // path and see if the file exists. If it exists, break the loop
3443 while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
3444
3445 // Ignore too long entries that may overflow path buffer
3446 if (filename_vec.len > path_len - (n + 2))
3447 continue;
3448
3449 // Prepare full path to the index file
3450 mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
3451
3452 // Does it exist?
3453 if (mg_stat(path, &file)) {
3454 // Yes it does, break the loop
3455 *filep = file;
3456 found = 1;
3457 break;
3458 }
3459 }
3460
3461 // If no index file exists, restore directory path
3462 if (!found) {
3463 path[n] = '\0';
3464 }
3465
3466 return found;
3467}
3468
3469// Return True if we should reply 304 Not Modified.
3470static int is_not_modified(const struct mg_connection *conn,
3471 const struct file *filep) {
3472 char etag[64];
3473 const char *ims = mg_get_header(conn, "If-Modified-Since");
3474 const char *inm = mg_get_header(conn, "If-None-Match");
3475 construct_etag(etag, sizeof(etag), filep);
3476 return (inm != NULL && !mg_strcasecmp(etag, inm)) ||
3477 (ims != NULL && filep->modification_time <= parse_date_string(ims));
3478}
3479
3480static int forward_body_data(struct mg_connection *conn, FILE *fp,
3481 SOCKET sock, SSL *ssl) {
3482 const char *expect, *body;
3483 char buf[MG_BUF_LEN];
3484 int nread, buffered_len, success = 0;
3485 int64_t left;
3486
3487 expect = mg_get_header(conn, "Expect");
3488 assert(fp != NULL);
3489
3490 if (conn->content_len == INT64_MAX) {
3491 send_http_error(conn, 411, "Length Required", "%s", "");
3492 } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
3493 send_http_error(conn, 417, "Expectation Failed", "%s", "");
3494 } else {
3495 if (expect != NULL) {
3496 (void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
3497 }
3498
3499 buffered_len = conn->data_len - conn->request_len;
3500 body = conn->buf + conn->request_len;
3501 assert(buffered_len >= 0);
3502
3503 if (buffered_len > 0) {
3504 if ((int64_t) buffered_len > conn->content_len) {
3505 buffered_len = (int) conn->content_len;
3506 }
3507 push(fp, sock, ssl, body, (int64_t) buffered_len);
3508 memmove((char *) body, body + buffered_len, buffered_len);
3509 conn->data_len -= buffered_len;
3510 }
3511
3512 nread = 0;
3513 while (conn->num_bytes_read < conn->content_len + conn->request_len) {
3514 left = left_to_read(conn);
3515 if (left > (int64_t) sizeof(buf)) {
3516 left = sizeof(buf);
3517 }
3518 nread = pull(NULL, conn, buf, (int) left);
3519 if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) {
3520 break;
3521 }
3522 }
3523
3524 if (left_to_read(conn) == 0) {
3525 success = nread >= 0;
3526 }
3527
3528 // Each error code path in this function must send an error
3529 if (!success) {
3530 send_http_error(conn, 577, http_500_error, "%s", "");
3531 }
3532 }
3533
3534 return success;
3535}
3536
3537#if !defined(NO_CGI)
3538// This structure helps to create an environment for the spawned CGI program.
3539// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
3540// last element must be NULL.
3541// However, on Windows there is a requirement that all these VARIABLE=VALUE\0
3542// strings must reside in a contiguous buffer. The end of the buffer is
3543// marked by two '\0' characters.
3544// We satisfy both worlds: we create an envp array (which is vars), all
3545// entries are actually pointers inside buf.
3546struct cgi_env_block {
3547 struct mg_connection *conn;
3548 char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer
3549 int len; // Space taken
3550 char *vars[MAX_CGI_ENVIR_VARS]; // char **envp
3551 int nvars; // Number of variables
3552};
3553
3554static char *addenv(struct cgi_env_block *block,
3555 PRINTF_FORMAT_STRING(const char *fmt), ...)
3556 PRINTF_ARGS(2, 3);
3557
3558// Append VARIABLE=VALUE\0 string to the buffer, and add a respective
3559// pointer into the vars array.
3560static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
3561 int n, space;
3562 char *added;
3563 va_list ap;
3564
3565 // Calculate how much space is left in the buffer
3566 space = sizeof(block->buf) - block->len - 2;
3567 assert(space >= 0);
3568
3569 // Make a pointer to the free space int the buffer
3570 added = block->buf + block->len;
3571
3572 // Copy VARIABLE=VALUE\0 string into the free space
3573 va_start(ap, fmt);
3574 n = mg_vsnprintf(added, (size_t) space, fmt, ap);
3575 va_end(ap);
3576
3577 // Make sure we do not overflow buffer and the envp array
3578 if (n > 0 && n + 1 < space &&
3579 block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
3580 // Append a pointer to the added string into the envp array
3581 block->vars[block->nvars++] = added;
3582 // Bump up used length counter. Include \0 terminator
3583 block->len += n + 1;
3584 } else {
3585 cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt);
3586 }
3587
3588 return added;
3589}
3590
3591static void prepare_cgi_environment(struct mg_connection *conn,
3592 const char *prog,
3593 struct cgi_env_block *blk) {
3594 const struct mg_request_info *ri = &conn->request_info;
3595 const char *s, *slash;
3596 struct vec var_vec;
3597 char *p, src_addr[IP_ADDR_STR_LEN];
3598 int i;
3599
3600 blk->len = blk->nvars = 0;
3601 blk->conn = conn;
3602 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
3603
3604 addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]);
3605 addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
3606 addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
3607 addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", mg_version());
3608
3609 // Prepare the environment block
3610 addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
3611 addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
3612 addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
3613
3614 // TODO(lsm): fix this for IPv6 case
3615 addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
3616
3617 addenv(blk, "REQUEST_METHOD=%s", ri->request_method);
3618 addenv(blk, "REMOTE_ADDR=%s", src_addr);
3619 addenv(blk, "REMOTE_PORT=%d", ri->remote_port);
3620 addenv(blk, "REQUEST_URI=%s%s%s", ri->uri,
3621 ri->query_string == NULL ? "" : "?",
3622 ri->query_string == NULL ? "" : ri->query_string);
3623
3624 // SCRIPT_NAME
3625 if (conn->path_info != NULL) {
3626 addenv(blk, "SCRIPT_NAME=%.*s",
3627 (int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri);
3628 addenv(blk, "PATH_INFO=%s", conn->path_info);
3629 } else {
3630 s = strrchr(prog, '/');
3631 slash = strrchr(ri->uri, '/');
3632 addenv(blk, "SCRIPT_NAME=%.*s%s",
3633 slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri,
3634 s == NULL ? prog : s);
3635 }
3636
3637 addenv(blk, "SCRIPT_FILENAME=%s", prog);
3638 addenv(blk, "PATH_TRANSLATED=%s", prog);
3639 addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
3640
3641 if ((s = mg_get_header(conn, "Content-Type")) != NULL)
3642 addenv(blk, "CONTENT_TYPE=%s", s);
3643
3644 if (ri->query_string != NULL) {
3645 addenv(blk, "QUERY_STRING=%s", ri->query_string);
3646 }
3647
3648 if ((s = mg_get_header(conn, "Content-Length")) != NULL)
3649 addenv(blk, "CONTENT_LENGTH=%s", s);
3650
3651 if ((s = getenv("PATH")) != NULL)
3652 addenv(blk, "PATH=%s", s);
3653
3654#if defined(_WIN32)
3655 if ((s = getenv("COMSPEC")) != NULL) {
3656 addenv(blk, "COMSPEC=%s", s);
3657 }
3658 if ((s = getenv("SYSTEMROOT")) != NULL) {
3659 addenv(blk, "SYSTEMROOT=%s", s);
3660 }
3661 if ((s = getenv("SystemDrive")) != NULL) {
3662 addenv(blk, "SystemDrive=%s", s);
3663 }
3664 if ((s = getenv("ProgramFiles")) != NULL) {
3665 addenv(blk, "ProgramFiles=%s", s);
3666 }
3667 if ((s = getenv("ProgramFiles(x86)")) != NULL) {
3668 addenv(blk, "ProgramFiles(x86)=%s", s);
3669 }
3670 if ((s = getenv("CommonProgramFiles(x86)")) != NULL) {
3671 addenv(blk, "CommonProgramFiles(x86)=%s", s);
3672 }
3673#else
3674 if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
3675 addenv(blk, "LD_LIBRARY_PATH=%s", s);
3676#endif // _WIN32
3677
3678 if ((s = getenv("PERLLIB")) != NULL)
3679 addenv(blk, "PERLLIB=%s", s);
3680
3681 if (ri->remote_user != NULL) {
3682 addenv(blk, "REMOTE_USER=%s", ri->remote_user);
3683 addenv(blk, "%s", "AUTH_TYPE=Digest");
3684 }
3685
3686 // Add all headers as HTTP_* variables
3687 for (i = 0; i < ri->num_headers; i++) {
3688 p = addenv(blk, "HTTP_%s=%s",
3689 ri->http_headers[i].name, ri->http_headers[i].value);
3690
3691 // Convert variable name into uppercase, and change - to _
3692 for (; *p != '=' && *p != '\0'; p++) {
3693 if (*p == '-')
3694 *p = '_';
3695 *p = (char) toupper(* (unsigned char *) p);
3696 }
3697 }
3698
3699 // Add user-specified variables
3700 s = conn->ctx->config[CGI_ENVIRONMENT];
3701 while ((s = next_option(s, &var_vec, NULL)) != NULL) {
3702 addenv(blk, "%.*s", (int) var_vec.len, var_vec.ptr);
3703 }
3704
3705 blk->vars[blk->nvars++] = NULL;
3706 blk->buf[blk->len++] = '\0';
3707
3708 assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
3709 assert(blk->len > 0);
3710 assert(blk->len < (int) sizeof(blk->buf));
3711}
3712
3713static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
3714 int headers_len, data_len, i, fdin[2], fdout[2];
3715 const char *status, *status_text;
3716 char buf[16384], *pbuf, dir[PATH_MAX], *p;
3717 struct mg_request_info ri;
3718 struct cgi_env_block blk;
3719 FILE *in = NULL, *out = NULL;
3720 pid_t pid = (pid_t) -1;
3721
3723
3724 // CGI must be executed in its own directory. 'dir' must point to the
3725 // directory containing executable program, 'p' must point to the
3726 // executable program name relative to 'dir'.
3727 (void) mg_snprintf(dir, sizeof(dir), "%s", prog);
3728 if ((p = strrchr(dir, '/')) != NULL) {
3729 *p++ = '\0';
3730 } else {
3731 dir[0] = '.', dir[1] = '\0';
3732 p = (char *) prog;
3733 }
3734
3735 if (pipe(fdin) != 0 || pipe(fdout) != 0) {
3737 "Cannot create CGI pipe: %s", strerror(ERRNO));
3738 goto done;
3739 }
3740
3741 pid = spawn_process(conn, p, blk.buf, blk.vars, fdin[0], fdout[1], dir);
3742 if (pid == (pid_t) -1) {
3744 "Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO));
3745 goto done;
3746 }
3747
3748 // Make sure child closes all pipe descriptors. It must dup them to 0,1
3753
3754 // Parent closes only one side of the pipes.
3755 // If we don't mark them as closed, close() attempt before
3756 // return from this function throws an exception on Windows.
3757 // Windows does not like when closed descriptor is closed again.
3758 (void) close(fdin[0]);
3759 (void) close(fdout[1]);
3760 fdin[0] = fdout[1] = -1;
3761
3762
3763 if ((in = fdopen(fdin[1], "wb")) == NULL ||
3764 (out = fdopen(fdout[0], "rb")) == NULL) {
3766 "fopen: %s", strerror(ERRNO));
3767 goto done;
3768 }
3769
3770 setbuf(in, NULL);
3771 setbuf(out, NULL);
3772
3773 // Send POST data to the CGI process if needed
3774 if (!strcmp(conn->request_info.request_method, "POST") &&
3776 goto done;
3777 }
3778
3779 // Close so child gets an EOF.
3780 fclose(in);
3781 in = NULL;
3782 fdin[1] = -1;
3783
3784 // Now read CGI reply into a buffer. We need to set correct
3785 // status code, thus we need to see all HTTP headers first.
3786 // Do not send anything back to client, until we buffer in all
3787 // HTTP headers.
3788 data_len = 0;
3789 headers_len = read_request(out, conn, buf, sizeof(buf), &data_len);
3790 if (headers_len <= 0) {
3792 "CGI program sent malformed or too big (>%u bytes) "
3793 "HTTP headers: [%.*s]",
3794 (unsigned) sizeof(buf), data_len, buf);
3795 goto done;
3796 }
3797 pbuf = buf;
3798 buf[headers_len - 1] = '\0';
3800
3801 // Make up and send the status line
3802 status_text = "OK";
3803 if ((status = get_header(&ri, "Status")) != NULL) {
3806 while (isdigit(* (unsigned char *) status_text) || *status_text == ' ') {
3807 status_text++;
3808 }
3809 } else if (get_header(&ri, "Location") != NULL) {
3810 conn->status_code = 302;
3811 } else {
3812 conn->status_code = 200;
3813 }
3814 if (get_header(&ri, "Connection") != NULL &&
3815 !mg_strcasecmp(get_header(&ri, "Connection"), "keep-alive")) {
3816 conn->must_close = 1;
3817 }
3818 (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code,
3819 status_text);
3820
3821 // Send headers
3822 for (i = 0; i < ri.num_headers; i++) {
3823 mg_printf(conn, "%s: %s\r\n",
3824 ri.http_headers[i].name, ri.http_headers[i].value);
3825 }
3826 mg_write(conn, "\r\n", 2);
3827
3828 // Send chunk of data that may have been read after the headers
3830 (size_t)(data_len - headers_len));
3831
3832 // Read the rest of CGI output and send to the client
3834
3835done:
3836 if (pid != (pid_t) -1) {
3837 kill(pid, SIGKILL);
3838 }
3839 if (fdin[0] != -1) {
3840 close(fdin[0]);
3841 }
3842 if (fdout[1] != -1) {
3843 close(fdout[1]);
3844 }
3845
3846 if (in != NULL) {
3847 fclose(in);
3848 } else if (fdin[1] != -1) {
3849 close(fdin[1]);
3850 }
3851
3852 if (out != NULL) {
3853 fclose(out);
3854 } else if (fdout[0] != -1) {
3855 close(fdout[0]);
3856 }
3857}
3858#endif // !NO_CGI
3859
3860// For a given PUT path, create all intermediate subdirectories
3861// for given path. Return 0 if the path itself is a directory,
3862// or -1 on error, 1 if OK.
3863static int put_dir(const char *path) {
3864 char buf[PATH_MAX];
3865 const char *s, *p;
3867 int len, res = 1;
3868
3869 for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
3870 len = p - path;
3871 if (len >= (int) sizeof(buf)) {
3872 res = -1;
3873 break;
3874 }
3875 memcpy(buf, path, len);
3876 buf[len] = '\0';
3877
3878 // Try to create intermediate directory
3879 DEBUG_TRACE(("mkdir(%s)", buf));
3880 if (!mg_stat(buf, &file) && mg_mkdir(buf, 0755) != 0) {
3881 res = -1;
3882 break;
3883 }
3884
3885 // Is path itself a directory?
3886 if (p[1] == '\0') {
3887 res = 0;
3888 }
3889 }
3890
3891 return res;
3892}
3893
3894static void mkcol(struct mg_connection *conn, const char *path) {
3895 int rc, body_len;
3896 struct de de;
3897
3898 memset(&de.file, 0, sizeof(de.file));
3899 mg_stat(path, &de.file);
3900
3902 send_http_error(conn, 405, "Method Not Allowed",
3903 "mkcol(%s): %s", path, strerror(ERRNO));
3904 return;
3905 }
3906
3907 body_len = conn->data_len - conn->request_len;
3908 if(body_len > 0) {
3909 send_http_error(conn, 415, "Unsupported media type",
3910 "mkcol(%s): %s", path, strerror(ERRNO));
3911 return;
3912 }
3913
3914 rc = mg_mkdir(path, 0755);
3915
3916 if (rc == 0) {
3917 conn->status_code = 201;
3918 mg_printf(conn, "HTTP/1.1 %d Created\r\n\r\n", conn->status_code);
3919 } else if (rc == -1) {
3920 if(errno == EEXIST)
3921 send_http_error(conn, 405, "Method Not Allowed",
3922 "mkcol(%s): %s", path, strerror(ERRNO));
3923 else if(errno == EACCES)
3924 send_http_error(conn, 403, "Forbidden",
3925 "mkcol(%s): %s", path, strerror(ERRNO));
3926 else if(errno == ENOENT)
3927 send_http_error(conn, 409, "Conflict",
3928 "mkcol(%s): %s", path, strerror(ERRNO));
3929 else
3931 "fopen(%s): %s", path, strerror(ERRNO));
3932 }
3933}
3934
3935static void put_file(struct mg_connection *conn, const char *path) {
3937 FILE *fp;
3938 const char *range;
3939 int64_t r1, r2;
3940 int rc;
3941
3942 conn->status_code = mg_stat(path, &file) ? 200 : 201;
3943
3944 if ((rc = put_dir(path)) == 0) {
3945 mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn->status_code);
3946 } else if (rc == -1) {
3948 "put_dir(%s): %s", path, strerror(ERRNO));
3949 } else if ((fp = mg_fopen(path, "wb+")) == NULL) {
3950 fclose(fp);
3952 "fopen(%s): %s", path, strerror(ERRNO));
3953 } else {
3955 range = mg_get_header(conn, "Content-Range");
3956 r1 = r2 = 0;
3957 if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
3958 conn->status_code = 206;
3959 fseeko(fp, r1, SEEK_SET);
3960 }
3961 if (!forward_body_data(conn, fp, INVALID_SOCKET, NULL)) {
3962 conn->status_code = 500;
3963 }
3964 mg_printf(conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n",
3965 conn->status_code);
3966 fclose(fp);
3967 }
3968}
3969
3970static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
3971static void do_ssi_include(struct mg_connection *conn, const char *ssi,
3972 char *tag, int include_level) {
3973 char file_name[MG_BUF_LEN], path[PATH_MAX], *p;
3974 FILE *fp;
3975
3976 // sscanf() is safe here, since send_ssi_file() also uses buffer
3977 // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
3978 if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
3979 // File name is relative to the webserver root
3980 (void) mg_snprintf(path, sizeof(path), "%s%c%s",
3981 conn->ctx->config[DOCUMENT_ROOT], '/', file_name);
3982 } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) {
3983 // File name is relative to the webserver working directory
3984 // or it is absolute system path
3985 (void) mg_snprintf(path, sizeof(path), "%s", file_name);
3986 } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 ||
3987 sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
3988 // File name is relative to the currect document
3989 (void) mg_snprintf(path, sizeof(path), "%s", ssi);
3990 if ((p = strrchr(path, '/')) != NULL) {
3991 p[1] = '\0';
3992 }
3993 (void) mg_snprintf(path + strlen(path),
3994 sizeof(path) - strlen(path), "%s", file_name);
3995 } else {
3996 cry(conn, "Bad SSI #include: [%s]", tag);
3997 return;
3998 }
3999
4000 if ((fp = mg_fopen(path, "rb")) == NULL) {
4001 cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
4002 tag, path, strerror(ERRNO));
4003 } else {
4006 strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) {
4007 send_ssi_file(conn, path, fp, include_level + 1);
4008 } else {
4009 send_file_data(conn, fp, 0, INT64_MAX);
4010 }
4011 fclose(fp);
4012 }
4013}
4014
4015#if !defined(NO_POPEN)
4016static void do_ssi_exec(struct mg_connection *conn, char *tag) {
4017 char cmd[MG_BUF_LEN];
4018 FILE *fp;
4019
4020 if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
4021 cry(conn, "Bad SSI #exec: [%s]", tag);
4022 } else if ((fp = popen(cmd, "r")) == NULL) {
4023 cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
4024 } else {
4025 send_file_data(conn, fp, 0, INT64_MAX);
4026 pclose(fp);
4027 }
4028}
4029#endif // !NO_POPEN
4030
4031static void send_ssi_file(struct mg_connection *conn, const char *path,
4032 FILE *fp, int include_level) {
4033 char buf[MG_BUF_LEN];
4034 int ch, offset, len, in_ssi_tag;
4035
4036 if (include_level > 10) {
4037 cry(conn, "SSI #include level is too deep (%s)", path);
4038 return;
4039 }
4040
4041 in_ssi_tag = len = offset = 0;
4042 while ((ch = fgetc(fp)) != EOF) {
4043 if (in_ssi_tag && ch == '>') {
4044 in_ssi_tag = 0;
4045 buf[len++] = (char) ch;
4046 buf[len] = '\0';
4047 assert(len <= (int) sizeof(buf));
4048 if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
4049 // Not an SSI tag, pass it
4050 (void) mg_write(conn, buf, (size_t) len);
4051 } else {
4052 if (!memcmp(buf + 5, "include", 7)) {
4053 do_ssi_include(conn, path, buf + 12, include_level);
4054#if !defined(NO_POPEN)
4055 } else if (!memcmp(buf + 5, "exec", 4)) {
4056 do_ssi_exec(conn, buf + 9);
4057#endif // !NO_POPEN
4058 } else {
4059 cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
4060 }
4061 }
4062 len = 0;
4063 } else if (in_ssi_tag) {
4064 if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
4065 // Not an SSI tag
4066 in_ssi_tag = 0;
4067 } else if (len == (int) sizeof(buf) - 2) {
4068 cry(conn, "%s: SSI tag is too large", path);
4069 len = 0;
4070 }
4071 buf[len++] = ch & 0xff;
4072 } else if (ch == '<') {
4073 in_ssi_tag = 1;
4074 if (len > 0) {
4075 mg_write(conn, buf, (size_t) len);
4076 }
4077 len = 0;
4078 buf[len++] = ch & 0xff;
4079 } else {
4080 buf[len++] = ch & 0xff;
4081 if (len == (int) sizeof(buf)) {
4082 mg_write(conn, buf, (size_t) len);
4083 len = 0;
4084 }
4085 }
4086 }
4087
4088 // Send the rest of buffered data
4089 if (len > 0) {
4090 mg_write(conn, buf, (size_t) len);
4091 }
4092}
4093
4094static void handle_ssi_file_request(struct mg_connection *conn,
4095 const char *path) {
4096 struct vec mime_vec;
4097 FILE *fp;
4098
4099 if ((fp = mg_fopen(path, "rb")) == NULL) {
4100 send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path,
4101 strerror(ERRNO));
4102 } else {
4103 conn->must_close = 1;
4105 get_mime_type(conn->ctx, path, &mime_vec);
4106 mg_printf(conn, "HTTP/1.1 200 OK\r\n"
4107 "Content-Type: %.*s\r\n"
4108 "Connection: close\r\n\r\n",
4109 (int) mime_vec.len, mime_vec.ptr);
4110 send_ssi_file(conn, path, fp, 0);
4111 fclose(fp);
4112 }
4113}
4114
4115static void handle_options_request(struct mg_connection *conn) {
4116 static const char reply[] = "HTTP/1.1 200 OK\r\n"
4117 "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\n"
4118 "DAV: 1\r\n\r\n";
4119
4120 conn->status_code = 200;
4121 mg_write(conn, reply, sizeof(reply) - 1);
4122}
4123
4124// Writes PROPFIND properties for a collection element
4125static void print_props(struct mg_connection *conn, const char* uri,
4126 struct file *filep) {
4127 char mtime[64];
4128 gmt_time_string(mtime, sizeof(mtime), &filep->modification_time);
4129 conn->num_bytes_sent += mg_printf(conn,
4130 "<d:response>"
4131 "<d:href>%s</d:href>"
4132 "<d:propstat>"
4133 "<d:prop>"
4134 "<d:resourcetype>%s</d:resourcetype>"
4135 "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
4136 "<d:getlastmodified>%s</d:getlastmodified>"
4137 "</d:prop>"
4138 "<d:status>HTTP/1.1 200 OK</d:status>"
4139 "</d:propstat>"
4140 "</d:response>\n",
4141 uri,
4142 filep->is_directory ? "<d:collection/>" : "",
4143 filep->size,
4144 mtime);
4145}
4146
4147static void print_dav_dir_entry(struct de *de, void *data) {
4148 char href[PATH_MAX];
4149 char href_encoded[PATH_MAX];
4150 struct mg_connection *conn = (struct mg_connection *) data;
4151 mg_snprintf(href, sizeof(href), "%s%s",
4152 conn->request_info.uri, de->file_name);
4154 print_props(conn, href_encoded, &de->file);
4155}
4156
4157static void handle_propfind(struct mg_connection *conn, const char *path,
4158 struct file *filep) {
4159 const char *depth = mg_get_header(conn, "Depth");
4160
4161 conn->must_close = 1;
4162 conn->status_code = 207;
4163 mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
4164 "Connection: close\r\n"
4165 "Content-Type: text/xml; charset=utf-8\r\n\r\n");
4166
4167 conn->num_bytes_sent += mg_printf(conn,
4168 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
4169 "<d:multistatus xmlns:d='DAV:'>\n");
4170
4171 // Print properties for the requested resource itself
4172 print_props(conn, conn->request_info.uri, filep);
4173
4174 // If it is a directory, print directory entries too if Depth is not 0
4175 if (filep->is_directory &&
4177 (depth == NULL || strcmp(depth, "0") != 0)) {
4178 scan_directory(conn, path, conn, &print_dav_dir_entry);
4179 }
4180
4181 conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>");
4182}
4183
4184#if defined(USE_WEBSOCKET)
4185
4186// START OF SHA-1 code
4187// Copyright(c) By Steve Reid <steve@edmweb.com>
4188#define SHA1HANDSOFF
4189#if defined(__sun)
4190#include "solarisfixes.h"
4191#endif
4192
4193union char64long16 { unsigned char c[64]; uint32_t l[16]; };
4194
4195#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
4196
4197static uint32_t blk0(union char64long16 *block, int i) {
4198 // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN
4199 if (!is_big_endian()) {
4200 block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
4201 (rol(block->l[i], 8) & 0x00FF00FF);
4202 }
4203 return block->l[i];
4204}
4205
4206#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
4207 ^block->l[(i+2)&15]^block->l[i&15],1))
4208#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
4209#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
4210#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
4211#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
4212#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
4213
4214typedef struct {
4215 uint32_t state[5];
4216 uint32_t count[2];
4217 unsigned char buffer[64];
4218} SHA1_CTX;
4219
4220static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) {
4221 uint32_t a, b, c, d, e;
4222 union char64long16 block[1];
4223
4224 memcpy(block, buffer, 64);
4225 a = state[0];
4226 b = state[1];
4227 c = state[2];
4228 d = state[3];
4229 e = state[4];
4230 R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
4231 R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
4232 R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
4233 R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
4234 R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
4235 R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
4236 R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
4237 R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
4238 R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
4239 R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
4240 R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
4241 R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
4242 R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
4243 R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
4244 R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
4245 R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
4246 R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
4247 R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
4248 R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
4249 R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
4250 state[0] += a;
4251 state[1] += b;
4252 state[2] += c;
4253 state[3] += d;
4254 state[4] += e;
4255 a = b = c = d = e = 0;
4256 memset(block, '\0', sizeof(block));
4257}
4258
4259static void SHA1Init(SHA1_CTX* context) {
4260 context->state[0] = 0x67452301;
4261 context->state[1] = 0xEFCDAB89;
4262 context->state[2] = 0x98BADCFE;
4263 context->state[3] = 0x10325476;
4264 context->state[4] = 0xC3D2E1F0;
4265 context->count[0] = context->count[1] = 0;
4266}
4267
4268static void SHA1Update(SHA1_CTX* context, const unsigned char* data,
4269 uint32_t len) {
4270 uint32_t i, j;
4271
4272 j = context->count[0];
4273 if ((context->count[0] += len << 3) < j)
4274 context->count[1]++;
4275 context->count[1] += (len>>29);
4276 j = (j >> 3) & 63;
4277 if ((j + len) > 63) {
4278 memcpy(&context->buffer[j], data, (i = 64-j));
4279 SHA1Transform(context->state, context->buffer);
4280 for ( ; i + 63 < len; i += 64) {
4281 SHA1Transform(context->state, &data[i]);
4282 }
4283 j = 0;
4284 }
4285 else i = 0;
4286 memcpy(&context->buffer[j], &data[i], len - i);
4287}
4288
4289static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) {
4290 unsigned i;
4291 unsigned char finalcount[8], c;
4292
4293 for (i = 0; i < 8; i++) {
4294 finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
4295 >> ((3-(i & 3)) * 8) ) & 255);
4296 }
4297 c = 0200;
4298 SHA1Update(context, &c, 1);
4299 while ((context->count[0] & 504) != 448) {
4300 c = 0000;
4301 SHA1Update(context, &c, 1);
4302 }
4303 SHA1Update(context, finalcount, 8);
4304 for (i = 0; i < 20; i++) {
4305 digest[i] = (unsigned char)
4306 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
4307 }
4308 memset(context, '\0', sizeof(*context));
4309 memset(&finalcount, '\0', sizeof(finalcount));
4310}
4311// END OF SHA1 CODE
4312
4313static void base64_encode(const unsigned char *src, int src_len, char *dst) {
4314 static const char *b64 =
4315 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4316 int i, j, a, b, c;
4317
4318 for (i = j = 0; i < src_len; i += 3) {
4319 a = src[i];
4320 b = i + 1 >= src_len ? 0 : src[i + 1];
4321 c = i + 2 >= src_len ? 0 : src[i + 2];
4322
4323 dst[j++] = b64[a >> 2];
4324 dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
4325 if (i + 1 < src_len) {
4326 dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
4327 }
4328 if (i + 2 < src_len) {
4329 dst[j++] = b64[c & 63];
4330 }
4331 }
4332 while (j % 4 != 0) {
4333 dst[j++] = '=';
4334 }
4335 dst[j++] = '\0';
4336}
4337
4338void mg_websocket_handshake(struct mg_connection *conn) {
4339 static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
4340 char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
4342
4343 mg_snprintf(buf, sizeof(buf), "%s%s",
4344 mg_get_header(conn, "Sec-WebSocket-Key"), magic);
4345 SHA1Init(&sha_ctx);
4346 SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf));
4347 SHA1Final((unsigned char *) sha, &sha_ctx);
4348 base64_encode((unsigned char *) sha, sizeof(sha), b64_sha);
4349 mg_printf(conn, "%s%s%s",
4350 "HTTP/1.1 101 Switching Protocols\r\n"
4351 "Upgrade: websocket\r\n"
4352 "Connection: Upgrade\r\n"
4353 "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
4354}
4355
4356int mg_websocket_read(struct mg_connection *conn, int *bits, char **data) {
4357 // Pointer to the beginning of the portion of the incoming websocket message
4358 // queue. The original websocket upgrade request is never removed,
4359 // so the queue begins after it.
4360 unsigned char *buf = (unsigned char *) conn->buf + conn->request_len;
4361 int n, stop = 0;
4362 size_t i, len, mask_len, data_len, header_len, body_len;
4363 char mask[4];
4364
4365 assert(conn->content_len == 0);
4366
4367 // Loop continuously, reading messages from the socket, invoking the callback,
4368 // and waiting repeatedly until an error occurs.
4369 while (!stop) {
4370 header_len = 0;
4371 // body_len is the length of the entire queue in bytes
4372 // len is the length of the current message
4373 // data_len is the length of the current message's data payload
4374 // header_len is the length of the current message's header
4375 if ((body_len = conn->data_len - conn->request_len) >= 2) {
4376 len = buf[1] & 127;
4377 mask_len = buf[1] & 128 ? 4 : 0;
4379 data_len = len;
4380 header_len = 2 + mask_len;
4381 } else if (len == 126 && body_len >= 4 + mask_len) {
4382 header_len = 4 + mask_len;
4383 data_len = ((((int) buf[2]) << 8) + buf[3]);
4384 } else if (body_len >= 10 + mask_len) {
4385 header_len = 10 + mask_len;
4386 data_len = (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) +
4387 htonl(* (uint32_t *) &buf[6]);
4388 }
4389 }
4390
4391 // Data layout is as follows:
4392 // conn->buf buf
4393 // v v frame1 | frame2
4394 // |---------------------|----------------|--------------|-------
4395 // | |<--header_len-->|<--data_len-->|
4396 // |<-conn->request_len->|<-----body_len----------->|
4397 // |<-------------------conn->data_len------------->|
4398
4399 if (header_len > 0) {
4400 // Allocate space to hold websocket payload
4401 if ((*data = malloc(data_len)) == NULL) {
4402 // Allocation failed, exit the loop and then close the connection
4403 // TODO: notify user about the failure
4404 data_len = 0;
4405 break;
4406 }
4407
4408 // Save mask and bits, otherwise it may be clobbered by memmove below
4409 *bits = buf[0];
4411
4412 // Read frame payload into the allocated buffer.
4413 assert(body_len >= header_len);
4414 if (data_len + header_len > body_len) {
4415 len = body_len - header_len;
4416 memcpy(*data, buf + header_len, len);
4417 // TODO: handle pull error
4418 pull_all(NULL, conn, *data + len, data_len - len);
4419 conn->data_len = conn->request_len;
4420 } else {
4421 len = data_len + header_len;
4422 memcpy(*data, buf + header_len, data_len);
4423 memmove(buf, buf + len, body_len - len);
4424 conn->data_len -= len;
4425 }
4426
4427 // Apply mask if necessary
4428 if (mask_len > 0) {
4429 for (i = 0; i < data_len; i++) {
4430 (*data)[i] ^= mask[i % 4];
4431 }
4432 }
4433
4434 return data_len;
4435 } else {
4436 // Buffering websocket request
4437 if ((n = pull(NULL, conn, conn->buf + conn->data_len,
4438 conn->buf_size - conn->data_len)) <= 0) {
4439 break;
4440 }
4441 conn->data_len += n;
4442 }
4443 }
4444
4445 return 0;
4446}
4447
4448int mg_websocket_write(struct mg_connection* conn, int opcode,
4449 const char *data, size_t data_len) {
4450 unsigned char *copy;
4451 size_t copy_len = 0;
4452 int retval = -1;
4453
4454 if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) {
4455 return -1;
4456 }
4457
4458 copy[0] = 0x80 + (opcode & 0x0f);
4459
4460 // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2
4461 if (data_len < 126) {
4462 // Inline 7-bit length field
4463 copy[1] = data_len;
4464 memcpy(copy + 2, data, data_len);
4465 copy_len = 2 + data_len;
4466 } else if (data_len <= 0xFFFF) {
4467 // 16-bit length field
4468 copy[1] = 126;
4469 * (uint16_t *) (copy + 2) = htons(data_len);
4470 memcpy(copy + 4, data, data_len);
4471 copy_len = 4 + data_len;
4472 } else {
4473 // 64-bit length field
4474 copy[1] = 127;
4475 * (uint32_t *) (copy + 2) = htonl((uint64_t) data_len >> 32);
4476 * (uint32_t *) (copy + 6) = htonl(data_len & 0xffffffff);
4477 memcpy(copy + 10, data, data_len);
4478 copy_len = 10 + data_len;
4479 }
4480
4481 // Not thread safe
4482 if (copy_len > 0) {
4483 retval = mg_write(conn, copy, copy_len);
4484 }
4485 free(copy);
4486
4487 return retval;
4488}
4489#endif // !USE_WEBSOCKET
4490
4491static int isbyte(int n) {
4492 return n >= 0 && n <= 255;
4493}
4494
4495static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
4496 int n, a, b, c, d, slash = 32, len = 0;
4497
4498 if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
4499 sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
4500 isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) &&
4501 slash >= 0 && slash < 33) {
4502 len = n;
4503 *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d;
4504 *mask = slash ? 0xffffffffU << (32 - slash) : 0;
4505 }
4506
4507 return len;
4508}
4509
4510static int set_throttle(const char *spec, uint32_t remote_ip, const char *uri) {
4511 int throttle = 0;
4512 struct vec vec, val;
4513 uint32_t net, mask;
4514 char mult;
4515 double v;
4516
4517 while ((spec = next_option(spec, &vec, &val)) != NULL) {
4518 mult = ',';
4519 if (sscanf(val.ptr, "%lf%c", &v, &mult) < 1 || v < 0 ||
4520 (lowercase(&mult) != 'k' && lowercase(&mult) != 'm' && mult != ',')) {
4521 continue;
4522 }
4523 v *= lowercase(&mult) == 'k' ? 1024 : lowercase(&mult) == 'm' ? 1048576 : 1;
4524 if (vec.len == 1 && vec.ptr[0] == '*') {
4525 throttle = (int) v;
4526 } else if (parse_net(vec.ptr, &net, &mask) > 0) {
4527 if ((remote_ip & mask) == net) {
4528 throttle = (int) v;
4529 }
4530 } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
4531 throttle = (int) v;
4532 }
4533 }
4534
4535 return throttle;
4536}
4537
4538static uint32_t get_remote_ip(const struct mg_connection *conn) {
4539 return ntohl(* (uint32_t *) &conn->client.rsa.sin.sin_addr);
4540}
4541
4542FILE *mg_upload(struct mg_connection *conn, const char *destination_dir,
4543 char *path, int path_len) {
4544 const char *content_type_header, *boundary_start;
4545 char *buf, fname[1024], boundary[100], *s;
4546 int bl, n, i, j, headers_len, boundary_len, eof, buf_len, to_read, len = 0;
4547 FILE *fp;
4548
4549 // Request looks like this:
4550 //
4551 // POST /upload HTTP/1.1
4552 // Host: 127.0.0.1:8080
4553 // Content-Length: 244894
4554 // Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRVr
4555 //
4556 // ------WebKitFormBoundaryRVr
4557 // Content-Disposition: form-data; name="file"; filename="accum.png"
4558 // Content-Type: image/png
4559 //
4560 // <89>PNG
4561 // <PNG DATA>
4562 // ------WebKitFormBoundaryRVr
4563
4564 // Extract boundary string from the Content-Type header
4565 if ((content_type_header = mg_get_header(conn, "Content-Type")) == NULL ||
4567 "boundary=")) == NULL ||
4568 (sscanf(boundary_start, "boundary=\"%99[^\"]\"", boundary) == 0 &&
4569 sscanf(boundary_start, "boundary=%99s", boundary) == 0) ||
4570 boundary[0] == '\0') {
4571 return NULL;
4572 }
4573
4574 boundary_len = strlen(boundary);
4575 bl = boundary_len + 4; // \r\n--<boundary>
4576
4577 // buf
4578 // conn->buf |<--------- buf_len ------>|
4579 // |=================|==========|===============|
4580 // |<--request_len-->|<--len--->| |
4581 // |<-----------data_len------->| conn->buf + conn->buf_size
4582
4583 buf = conn->buf + conn->request_len;
4584 buf_len = conn->buf_size - conn->request_len;
4585 len = conn->data_len - conn->request_len;
4586
4587 for (;;) {
4588 // Pull in headers
4589 assert(len >= 0 && len <= buf_len);
4590 to_read = buf_len - len;
4591 if (to_read > left_to_read(conn)) {
4592 to_read = (int) left_to_read(conn);
4593 }
4594 while (len < buf_len &&
4595 (n = pull(NULL, conn, buf + len, to_read)) > 0) {
4596 len += n;
4597 }
4598 if ((headers_len = get_request_len(buf, len)) <= 0) {
4599 break;
4600 }
4601
4602 // Fetch file name.
4603 fname[0] = '\0';
4604 for (i = j = 0; i < headers_len; i++) {
4605 if (buf[i] == '\r' && buf[i + 1] == '\n') {
4606 buf[i] = buf[i + 1] = '\0';
4607 // TODO(lsm): don't expect filename to be the 3rd field,
4608 // parse the header properly instead.
4609 sscanf(&buf[j], "Content-Disposition: %*s %*s filename=\"%1023[^\"]",
4610 fname);
4611 j = i + 2;
4612 }
4613 }
4614
4615 // Give up if the headers are not what we expect
4616 if (fname[0] == '\0') {
4617 break;
4618 }
4619
4620 // Move data to the beginning of the buffer
4621 assert(len >= headers_len);
4622 memmove(buf, &buf[headers_len], len - headers_len);
4623 len -= headers_len;
4624 conn->data_len = conn->request_len + len;
4625
4626 // We open the file with exclusive lock held. This guarantee us
4627 // there is no other thread can save into the same file simultaneously.
4628 fp = NULL;
4629
4630 // Construct destination file name. Do not allow paths to have slashes.
4631 if ((s = strrchr(fname, '/')) == NULL &&
4632 (s = strrchr(fname, '\\')) == NULL) {
4633 s = fname;
4634 }
4635
4636 // Open file in binary mode. TODO: set an exclusive lock.
4637 snprintf(path, path_len, "%s/%s", destination_dir, s);
4638 if ((fp = fopen(path, "wb")) == NULL) {
4639 break;
4640 }
4641
4642 // Read POST data, write into file until boundary is found.
4643 eof = n = 0;
4644 do {
4645 len += n;
4646 for (i = 0; i < len - bl; i++) {
4647 if (!memcmp(&buf[i], "\r\n--", 4) &&
4648 !memcmp(&buf[i + 4], boundary, boundary_len)) {
4649 // Found boundary, that's the end of file data.
4650 fwrite(buf, 1, i, fp);
4651 eof = 1;
4652 memmove(buf, &buf[i + bl], len - (i + bl));
4653 len -= i + bl;
4654 break;
4655 }
4656 }
4657 if (!eof && len > bl) {
4658 fwrite(buf, 1, len - bl, fp);
4659 memmove(buf, &buf[len - bl], bl);
4660 len = bl;
4661 }
4662 to_read = buf_len - len;
4663 if (to_read > left_to_read(conn)) {
4664 to_read = (int) left_to_read(conn);
4665 }
4666 } while (!eof && (n = pull(NULL, conn, buf + len, to_read)) > 0);
4667 conn->data_len = conn->request_len + len;
4668
4669 if (eof) {
4670 rewind(fp);
4671 return fp;
4672 } else {
4673 fclose(fp);
4674 }
4675 }
4676
4677 return NULL;
4678}
4679
4680static int is_put_or_delete_request(const struct mg_connection *conn) {
4681 const char *s = conn->request_info.request_method;
4682 return s != NULL && (!strcmp(s, "PUT") ||
4683 !strcmp(s, "DELETE") ||
4684 !strcmp(s, "MKCOL"));
4685}
4686
4687static int get_first_ssl_listener_index(const struct mg_context *ctx) {
4688 int i, index = -1;
4689 for (i = 0; index == -1 && i < ctx->num_listening_sockets; i++) {
4690 index = ctx->listening_sockets[i].is_ssl ? i : -1;
4691 }
4692 return index;
4693}
4694
4695static void redirect_to_https_port(struct mg_connection *conn, int ssl_index) {
4696 char host[1025];
4697 const char *host_header;
4698
4699 if ((host_header = mg_get_header(conn, "Host")) == NULL ||
4700 sscanf(host_header, "%1024[^:]", host) == 0) {
4701 // Cannot get host from the Host: header. Fallback to our IP address.
4702 sockaddr_to_string(host, sizeof(host), &conn->client.lsa);
4703 }
4704
4705 mg_printf(conn, "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s\r\n\r\n",
4706 host, (int) ntohs(conn->ctx->listening_sockets[ssl_index].
4707 lsa.sin.sin_port), conn->request_info.uri);
4708}
4709
4710static void handle_delete_request(struct mg_connection *conn,
4711 const char *path) {
4713
4714 if (!mg_stat(path, &file)) {
4715 send_http_error(conn, 404, "Not Found", "%s", "File not found");
4716 } else if (!file.modification_time) {
4717 send_http_error(conn, 500, http_500_error, "remove(%s): %s", path,
4718 strerror(ERRNO));
4719 } else if (file.is_directory) {
4720 remove_directory(conn, path);
4721 send_http_error(conn, 204, "No Content", "%s", "");
4722 } else if (mg_remove(path) == 0) {
4723 send_http_error(conn, 204, "No Content", "%s", "");
4724 } else {
4725 send_http_error(conn, 423, "Locked", "remove(%s): %s", path,
4726 strerror(ERRNO));
4727 }
4728}
4729
4730// This is the heart of the Mongoose's logic.
4731// This function is called when the request is read, parsed and validated,
4732// and Mongoose must decide what action to take: serve a file, or
4733// a directory, or call embedded function, etcetera.
4734static void handle_request(struct mg_connection *conn) {
4735 struct mg_request_info *ri = &conn->request_info;
4736 char path[PATH_MAX];
4737 int uri_len, ssl_index;
4739
4740 if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
4741 * ((char *) conn->request_info.query_string++) = '\0';
4742 }
4743 uri_len = (int) strlen(ri->uri);
4744 mg_url_decode(ri->uri, uri_len, (char *) ri->uri, uri_len + 1, 0);
4746 conn->throttle = set_throttle(conn->ctx->config[THROTTLE],
4747 get_remote_ip(conn), ri->uri);
4748 path[0] = '\0';
4749 convert_uri_to_file_name(conn, path, sizeof(path), &file);
4750
4751 // Perform redirect and auth checks before calling begin_request() handler.
4752 // Otherwise, begin_request() would need to perform auth checks and redirects.
4753 if (!conn->client.is_ssl && conn->client.ssl_redir &&
4754 (ssl_index = get_first_ssl_listener_index(conn->ctx)) > -1) {
4756 } else if (!strcmp(ri->request_method, "OPTIONS") && (call_user(MG_REQUEST_BEGIN, conn, (void *) ri->uri) == 1)) {
4757 // CORS "pre-flight" "OPTIONS" requests are sent without authentication, duh!
4758 } else if (!is_put_or_delete_request(conn) &&
4759 !check_authorization(conn, path)) {
4761 } else if (call_user(MG_REQUEST_BEGIN, conn, (void *) ri->uri) == 1) {
4762 // Do nothing, callback has served the request
4763 } else if (!strcmp(ri->request_method, "OPTIONS")) {
4765 } else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
4766 send_http_error(conn, 404, "Not Found", "Not Found");
4767 } else if (is_put_or_delete_request(conn) &&
4768 (is_authorized_for_put(conn) != 1)) {
4770 } else if (!strcmp(ri->request_method, "PUT")) {
4771 put_file(conn, path);
4772 } else if (!strcmp(ri->request_method, "MKCOL")) {
4773 mkcol(conn, path);
4774 } else if (!strcmp(ri->request_method, "DELETE")) {
4775 handle_delete_request(conn, path);
4776 } else if (file.modification_time == (time_t) 0 ||
4777 must_hide_file(conn, path)) {
4778 send_http_error(conn, 404, "Not Found", "%s", "File not found");
4779 } else if (file.is_directory && ri->uri[uri_len - 1] != '/') {
4780 mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
4781 "Location: %s/\r\n\r\n", ri->uri);
4782 } else if (!strcmp(ri->request_method, "PROPFIND")) {
4783 handle_propfind(conn, path, &file);
4784 } else if (file.is_directory &&
4785 !substitute_index_file(conn, path, sizeof(path), &file)) {
4786 if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) {
4787 handle_directory_request(conn, path);
4788 } else {
4789 send_http_error(conn, 403, "Directory Listing Denied",
4790 "Directory listing denied");
4791 }
4792#ifdef USE_LUA
4793 } else if (match_prefix("**.lp$", 6, path) > 0) {
4794 handle_lsp_request(conn, path, &file, NULL);
4795#endif
4796#if !defined(NO_CGI)
4797 } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
4799 path) > 0) {
4800 if (strcmp(ri->request_method, "POST") &&
4801 strcmp(ri->request_method, "HEAD") &&
4802 strcmp(ri->request_method, "GET")) {
4803 send_http_error(conn, 501, "Not Implemented",
4804 "Method %s is not implemented", ri->request_method);
4805 } else {
4806 handle_cgi_request(conn, path);
4807 }
4808#endif // !NO_CGI
4809 } else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
4811 path) > 0) {
4812 handle_ssi_file_request(conn, path);
4813 } else if (is_not_modified(conn, &file)) {
4814 send_http_error(conn, 304, "Not Modified", "%s", "");
4815 } else {
4816 handle_file_request(conn, path, &file);
4817 }
4818}
4819
4820static void close_all_listening_sockets(struct mg_context *ctx) {
4821 int i;
4822 for (i = 0; i < ctx->num_listening_sockets; i++) {
4824 }
4825 free(ctx->listening_sockets);
4826}
4827
4828static int is_valid_port(unsigned int port) {
4829 return port > 0 && port < 0xffff;
4830}
4831
4832// Valid listening port specification is: [ip_address:]port[s]
4833// Examples: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s
4834// TODO(lsm): add parsing of the IPv6 address
4835static int parse_port_string(const struct vec *vec, struct socket *so) {
4836 unsigned int a, b, c, d, ch, port;
4837 int len;
4838#if defined(USE_IPV6)
4839 char buf[100];
4840#endif
4841
4842 // MacOS needs that. If we do not zero it, subsequent bind() will fail.
4843 // Also, all-zeroes in the socket address means binding to all addresses
4844 // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
4845 memset(so, 0, sizeof(*so));
4846 so->lsa.sin.sin_family = AF_INET;
4847
4848 if (sscanf(vec->ptr, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
4849 // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
4850 so->lsa.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
4851 so->lsa.sin.sin_port = htons((uint16_t) port);
4852#if defined(USE_IPV6)
4853
4854 } else if (sscanf(vec->ptr, "[%49[^]]]:%d%n", buf, &port, &len) == 2 &&
4855 inet_pton(AF_INET6, buf, &so->lsa.sin6.sin6_addr)) {
4856 // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
4857 so->lsa.sin6.sin6_family = AF_INET6;
4858 so->lsa.sin6.sin6_port = htons((uint16_t) port);
4859#endif
4860 } else if (sscanf(vec->ptr, "%u%n", &port, &len) == 1) {
4861 // If only port is specified, bind to IPv4, INADDR_ANY
4862 so->lsa.sin.sin_port = htons((uint16_t) port);
4863 } else {
4864 port = len = 0; // Parsing failure. Make port invalid.
4865 }
4866
4867 ch = vec->ptr[len]; // Next character after the port number
4868 so->is_ssl = ch == 's';
4869 so->ssl_redir = ch == 'r';
4870
4871 // Make sure the port is valid and vector ends with 's', 'r' or ','
4872 return is_valid_port(port) &&
4873 (ch == '\0' || ch == 's' || ch == 'r' || ch == ',');
4874}
4875
4876static int set_ports_option(struct mg_context *ctx) {
4877 const char *list = ctx->config[LISTENING_PORTS];
4878 int on = 1, success = 1;
4879#if defined(USE_IPV6)
4880 int off = 0;
4881#endif
4882 struct vec vec;
4883 struct socket so, *ptr;
4884
4885 while (success && (list = next_option(list, &vec, NULL)) != NULL) {
4886 if (!parse_port_string(&vec, &so)) {
4887 cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
4888 __func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|r]");
4889 success = 0;
4890 } else if (so.is_ssl && ctx->ssl_ctx == NULL) {
4891 cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
4892 success = 0;
4893 } else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
4895 // On Windows, SO_REUSEADDR is recommended only for
4896 // broadcast UDP sockets
4898 (void *) &on, sizeof(on)) != 0 ||
4899#if defined(USE_IPV6)
4900 (so.lsa.sa.sa_family == AF_INET6 &&
4901 setsockopt(so.sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &off,
4902 sizeof(off)) != 0) ||
4903#endif
4904 bind(so.sock, &so.lsa.sa, so.lsa.sa.sa_family == AF_INET ?
4905 sizeof(so.lsa.sin) : sizeof(so.lsa)) != 0 ||
4906 listen(so.sock, SOMAXCONN) != 0) {
4907 cry(fc(ctx), "%s: cannot bind to %.*s: %d (%s)", __func__,
4908 (int) vec.len, vec.ptr, ERRNO, strerror(errno));
4909 closesocket(so.sock);
4910 success = 0;
4911 } else if ((ptr = (struct socket *) realloc(ctx->listening_sockets,
4912 (ctx->num_listening_sockets + 1) *
4913 sizeof(ctx->listening_sockets[0]))) == NULL) {
4914 closesocket(so.sock);
4915 success = 0;
4916 } else {
4917 set_close_on_exec(so.sock);
4918 ctx->listening_sockets = ptr;
4920 ctx->num_listening_sockets++;
4921 }
4922 }
4923
4924 if (!success) {
4926 }
4927
4928 return success;
4929}
4930
4931static void log_header(const struct mg_connection *conn, const char *header,
4932 FILE *fp) {
4933 const char *header_value;
4934
4935 if ((header_value = mg_get_header(conn, header)) == NULL) {
4936 (void) fprintf(fp, "%s", " -");
4937 } else {
4938 (void) fprintf(fp, " \"%s\"", header_value);
4939 }
4940}
4941
4942static void log_access(const struct mg_connection *conn) {
4943 const struct mg_request_info *ri;
4944 FILE *fp;
4945 char date[64], src_addr[IP_ADDR_STR_LEN];
4946
4947 fp = conn->ctx->config[ACCESS_LOG_FILE] == NULL ? NULL :
4948 fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+");
4949
4950 if (fp == NULL)
4951 return;
4952
4953 strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
4954 localtime(&conn->birth_time));
4955
4956 ri = &conn->request_info;
4957 flockfile(fp);
4958
4959 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
4960 fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
4961 src_addr, ri->remote_user == NULL ? "-" : ri->remote_user, date,
4963 ri->uri ? ri->uri : "-", ri->http_version,
4964 conn->status_code, conn->num_bytes_sent);
4965 log_header(conn, "Referer", fp);
4966 log_header(conn, "User-Agent", fp);
4967 fputc('\n', fp);
4968 fflush(fp);
4969
4970 funlockfile(fp);
4971 fclose(fp);
4972}
4973
4974// Verify given socket address against the ACL.
4975// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
4976static int check_acl(struct mg_context *ctx, uint32_t remote_ip) {
4977 int allowed, flag;
4978 uint32_t net, mask;
4979 struct vec vec;
4980 const char *list = ctx->config[ACCESS_CONTROL_LIST];
4981
4982 // If any ACL is set, deny by default
4983 allowed = list == NULL ? '+' : '-';
4984
4985 while ((list = next_option(list, &vec, NULL)) != NULL) {
4986 flag = vec.ptr[0];
4987 if ((flag != '+' && flag != '-') ||
4988 parse_net(&vec.ptr[1], &net, &mask) == 0) {
4989 cry(fc(ctx), "%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
4990 return -1;
4991 }
4992
4993 if (net == (remote_ip & mask)) {
4994 allowed = flag;
4995 }
4996 }
4997
4998 return allowed == '+';
4999}
5000
5001#if !defined(_WIN32)
5002static int set_uid_option(struct mg_context *ctx) {
5003 struct passwd *pw;
5004 const char *uid = ctx->config[RUN_AS_USER];
5005 int success = 0;
5006
5007 if (uid == NULL) {
5008 success = 1;
5009 } else {
5010 if ((pw = getpwnam(uid)) == NULL) {
5011 cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
5012 } else if (setgid(pw->pw_gid) == -1) {
5013 cry(fc(ctx), "%s: setgid(%s): %s", __func__, uid, strerror(errno));
5014 } else if (setuid(pw->pw_uid) == -1) {
5015 cry(fc(ctx), "%s: setuid(%s): %s", __func__, uid, strerror(errno));
5016 } else {
5017 success = 1;
5018 }
5019 }
5020
5021 return success;
5022}
5023#endif // !_WIN32
5024
5025static int set_gpass_option(struct mg_context *ctx) {
5027 const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
5028 if (path != NULL && !mg_stat(path, &file)) {
5029 cry(fc(ctx), "Cannot open %s: %s", path, strerror(ERRNO));
5030 return 0;
5031 }
5032 return 1;
5033}
5034
5035static int set_acl_option(struct mg_context *ctx) {
5036 return check_acl(ctx, (uint32_t) 0x7f000001UL) != -1;
5037}
5038
5039static void reset_per_request_attributes(struct mg_connection *conn) {
5040 conn->path_info = NULL;
5041 conn->num_bytes_sent = conn->num_bytes_read = 0;
5042 conn->status_code = -1;
5043 conn->must_close = conn->request_len = conn->throttle = 0;
5044}
5045
5046static void close_socket_gracefully(struct mg_connection *conn) {
5047#if defined(_WIN32)
5048 char buf[MG_BUF_LEN];
5049 int n;
5050#endif
5051 struct linger linger;
5052
5053 // Set linger option to avoid socket hanging out after close. This prevent
5054 // ephemeral port exhaust problem under high QPS.
5055 linger.l_onoff = 1;
5056 linger.l_linger = 1;
5058 (char *) &linger, sizeof(linger));
5059
5060 // Send FIN to the client
5061 shutdown(conn->client.sock, SHUT_WR);
5063
5064#if defined(_WIN32)
5065 // Read and discard pending incoming data. If we do not do that and close the
5066 // socket, the data in the send buffer may be discarded. This
5067 // behaviour is seen on Windows, when client keeps sending data
5068 // when server decides to close the connection; then when client
5069 // does recv() it gets no data back.
5070 do {
5071 n = pull(NULL, conn, buf, sizeof(buf));
5072 } while (n > 0);
5073#endif
5074
5075 // Now we know that our FIN is ACK-ed, safe to close
5076 closesocket(conn->client.sock);
5077}
5078
5079static void close_connection(struct mg_connection *conn) {
5080 conn->must_close = 1;
5081
5082#ifndef NO_SSL
5083 if (conn->ssl != NULL) {
5084 // Run SSL_shutdown twice to ensure completly close SSL connection
5085 SSL_shutdown(conn->ssl);
5086 SSL_free(conn->ssl);
5087 conn->ssl = NULL;
5088 }
5089#endif
5090 if (conn->client.sock != INVALID_SOCKET) {
5092 conn->client.sock = INVALID_SOCKET;
5093 }
5094}
5095
5096void mg_close_connection(struct mg_connection *conn) {
5097#ifndef NO_SSL
5098 if (conn->client_ssl_ctx != NULL) {
5100 }
5101#endif
5102 close_connection(conn);
5103 free(conn);
5104}
5105
5106static int is_valid_uri(const char *uri) {
5107 // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
5108 // URI can be an asterisk (*) or should start with slash.
5109 return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
5110}
5111
5112static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len) {
5113 const char *cl;
5114
5115 ebuf[0] = '\0';
5117 conn->request_len = read_request(NULL, conn, conn->buf, conn->buf_size,
5118 &conn->data_len);
5119 assert(conn->request_len < 0 || conn->data_len >= conn->request_len);
5120
5121 if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
5122 snprintf(ebuf, ebuf_len, "%s", "Request Too Large");
5123 } else if (conn->request_len <= 0) {
5124 snprintf(ebuf, ebuf_len, "%s", "Client closed connection");
5125 } else if (parse_http_message(conn->buf, conn->buf_size,
5126 &conn->request_info) <= 0) {
5127 snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf);
5128 } else {
5129 // Request is valid. Set content_len attribute by parsing Content-Length
5130 // If Content-Length is absent, set content_len to 0 if request is GET,
5131 // and set it to INT64_MAX otherwise. Setting to INT64_MAX instructs
5132 // mg_read() to read from the socket until socket is closed.
5133 // The reason for treating GET and POST/PUT differently is that libraries
5134 // like jquery do not set Content-Length in GET requests, and we don't
5135 // want mg_read() to hang waiting until socket is timed out.
5136 // See https://github.com/cesanta/mongoose/pull/121 for more.
5137 conn->content_len = INT64_MAX;
5138 if (!mg_strcasecmp(conn->request_info.request_method, "GET")) {
5139 conn->content_len = 0;
5140 }
5141 if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) {
5142 conn->content_len = strtoll(cl, NULL, 10);
5143 }
5144 conn->birth_time = time(NULL);
5145 }
5146 return ebuf[0] == '\0';
5147}
5148
5149static void process_new_connection(struct mg_connection *conn) {
5150 struct mg_request_info *ri = &conn->request_info;
5151 int keep_alive_enabled, keep_alive, discard_len;
5152 char ebuf[100];
5153
5155 keep_alive = 0;
5156
5157 // Important: on new connection, reset the receiving buffer. Credit goes
5158 // to crule42.
5159 conn->data_len = 0;
5160 do {
5161 if (!getreq(conn, ebuf, sizeof(ebuf))) {
5162 send_http_error(conn, 500, "Server Error", "%s", ebuf);
5163 conn->must_close = 1;
5164 } else if (!is_valid_uri(conn->request_info.uri)) {
5165 snprintf(ebuf, sizeof(ebuf), "Invalid URI: [%s]", ri->uri);
5166 send_http_error(conn, 400, "Bad Request", "%s", ebuf);
5167 } else if (strcmp(ri->http_version, "1.0") &&
5168 strcmp(ri->http_version, "1.1")) {
5169 snprintf(ebuf, sizeof(ebuf), "Bad HTTP version: [%s]", ri->http_version);
5170 send_http_error(conn, 505, "Bad HTTP version", "%s", ebuf);
5171 }
5172
5173 if (ebuf[0] == '\0') {
5174 handle_request(conn);
5175 call_user(MG_REQUEST_END, conn, (void *) (long) conn->status_code);
5176 log_access(conn);
5177 }
5178 if (ri->remote_user != NULL) {
5179 free((void *) ri->remote_user);
5180 // Important! When having connections with and without auth
5181 // would cause double free and then crash
5182 ri->remote_user = NULL;
5183 }
5184
5185 // NOTE(lsm): order is important here. should_keep_alive() call
5186 // is using parsed request, which will be invalid after memmove's below.
5187 // Therefore, memorize should_keep_alive() result now for later use
5188 // in loop exit condition.
5189 keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled &&
5190 conn->content_len >= 0 && should_keep_alive(conn);
5191
5192 // Discard all buffered data for this request
5193 discard_len = conn->content_len >= 0 && conn->request_len > 0 &&
5194 conn->request_len + conn->content_len < (int64_t) conn->data_len ?
5195 (int) (conn->request_len + conn->content_len) : conn->data_len;
5196 assert(discard_len >= 0);
5197 memmove(conn->buf, conn->buf + discard_len, conn->data_len - discard_len);
5198 conn->data_len -= discard_len;
5199 assert(conn->data_len >= 0);
5200 assert(conn->data_len <= conn->buf_size);
5201 } while (keep_alive);
5202}
5203
5204// Worker threads take accepted socket from the queue
5205static int consume_socket(struct mg_context *ctx, struct socket *sp) {
5206 (void) pthread_mutex_lock(&ctx->mutex);
5207 DEBUG_TRACE(("going idle"));
5208
5209 // If the queue is empty, wait. We're idle at this point.
5210 while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
5211 pthread_cond_wait(&ctx->sq_full, &ctx->mutex);
5212 }
5213
5214 // If we're stopping, sq_head may be equal to sq_tail.
5215 if (ctx->sq_head > ctx->sq_tail) {
5216 // Copy socket from the queue and increment tail
5217 *sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];
5218 ctx->sq_tail++;
5219 DEBUG_TRACE(("grabbed socket %d, going busy", sp->sock));
5220
5221 // Wrap pointers if needed
5222 while (ctx->sq_tail > (int) ARRAY_SIZE(ctx->queue)) {
5223 ctx->sq_tail -= ARRAY_SIZE(ctx->queue);
5224 ctx->sq_head -= ARRAY_SIZE(ctx->queue);
5225 }
5226 }
5227
5230
5231 return !ctx->stop_flag;
5232}
5233
5234static void *worker_thread(void *thread_func_param) {
5235 struct mg_context *ctx = (struct mg_context *) thread_func_param;
5236 struct mg_connection *conn;
5237
5238 conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
5239 if (conn == NULL) {
5240 cry(fc(ctx), "%s", "Cannot create new connection struct, OOM");
5241 } else {
5242 conn->buf_size = MAX_REQUEST_SIZE;
5243 conn->buf = (char *) (conn + 1);
5244 conn->ctx = ctx;
5245 conn->event.user_data = ctx->user_data;
5246
5248
5249 // Call consume_socket() even when ctx->stop_flag > 0, to let it signal
5250 // sq_empty condvar to wake up the master waiting in produce_socket()
5251 while (consume_socket(ctx, &conn->client)) {
5252 conn->birth_time = time(NULL);
5253
5254 // Fill in IP, port info early so even if SSL setup below fails,
5255 // error handler would have the corresponding info.
5256 // Thanks to Johannes Winkelmann for the patch.
5257 // TODO(lsm): Fix IPv6 case
5258 conn->request_info.remote_port = ntohs(conn->client.rsa.sin.sin_port);
5260 &conn->client.rsa.sin.sin_addr.s_addr, 4);
5262 conn->request_info.is_ssl = conn->client.is_ssl;
5263
5264 if (!conn->client.is_ssl
5266 || sslize(conn, conn->ctx->ssl_ctx, SSL_accept)
5267#endif
5268 ) {
5270 }
5271
5272 close_connection(conn);
5273 }
5275 free(conn);
5276 }
5277
5278 // Signal master that we're done with connection and exiting
5280 ctx->num_threads--;
5282 assert(ctx->num_threads >= 0);
5284
5285 DEBUG_TRACE(("exiting"));
5286 return NULL;
5287}
5288
5289// Master thread adds accepted socket to a queue
5290static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
5292
5293 // If the queue is full, wait
5294 while (ctx->stop_flag == 0 &&
5295 ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
5297 }
5298
5299 if (ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue)) {
5300 // Copy socket to the queue and increment head
5302 ctx->sq_head++;
5303 DEBUG_TRACE(("queued socket %d", sp->sock));
5304 }
5305
5308}
5309
5310static int set_sock_timeout(SOCKET sock, int milliseconds) {
5311#ifdef _WIN32
5312 DWORD t = milliseconds;
5313#else
5314 struct timeval t;
5315 t.tv_sec = milliseconds / 1000;
5316 t.tv_usec = (milliseconds * 1000) % 1000000;
5317#endif
5318 return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *) &t, sizeof(t)) ||
5319 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *) &t, sizeof(t));
5320}
5321
5322static void accept_new_connection(const struct socket *listener,
5323 struct mg_context *ctx) {
5324 struct socket so;
5326 socklen_t len = sizeof(so.rsa);
5327 int on = 1;
5328 extern int check_midas_acl(const struct sockaddr *sa, int len);
5329
5330 if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) {
5331 } else if ((!check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) || (!check_midas_acl(&so.rsa.sa, len))) {
5332 sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
5333 cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
5334 closesocket(so.sock);
5335 } else {
5336 // Put so socket structure into the queue
5337 DEBUG_TRACE(("Accepted socket %d", (int) so.sock));
5338 set_close_on_exec(so.sock);
5339 so.is_ssl = listener->is_ssl;
5340 so.ssl_redir = listener->ssl_redir;
5341 getsockname(so.sock, &so.lsa.sa, &len);
5342 // Set TCP keep-alive. This is needed because if HTTP-level keep-alive
5343 // is enabled, and client resets the connection, server won't get
5344 // TCP FIN or RST and will keep the connection open forever. With TCP
5345 // keep-alive, next keep-alive handshake will figure out that the client
5346 // is down and will close the server end.
5347 // Thanks to Igor Klopov who suggested the patch.
5348 setsockopt(so.sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &on, sizeof(on));
5350 produce_socket(ctx, &so);
5351 }
5352}
5353
5354static void *master_thread(void *thread_func_param) {
5355 struct mg_context *ctx = (struct mg_context *) thread_func_param;
5356 struct pollfd *pfd;
5357 int i;
5358
5359 // Increase priority of the master thread
5360#if defined(_WIN32)
5362#endif
5363
5364#if defined(ISSUE_317)
5365 struct sched_param sched_param;
5368#endif
5369
5371
5372 pfd = (struct pollfd *) calloc(ctx->num_listening_sockets, sizeof(pfd[0]));
5373 while (pfd != NULL && ctx->stop_flag == 0) {
5374 for (i = 0; i < ctx->num_listening_sockets; i++) {
5375 pfd[i].fd = ctx->listening_sockets[i].sock;
5376 pfd[i].events = POLLIN;
5377 }
5378
5379 if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
5380 for (i = 0; i < ctx->num_listening_sockets; i++) {
5381 // NOTE(lsm): on QNX, poll() returns POLLRDNORM after the
5382 // successfull poll, and POLLIN is defined as (POLLRDNORM | POLLRDBAND)
5383 // Therefore, we're checking pfd[i].revents & POLLIN, not
5384 // pfd[i].revents == POLLIN.
5385 if (ctx->stop_flag == 0 && (pfd[i].revents & POLLIN)) {
5387 }
5388 }
5389 }
5390 }
5391 free(pfd);
5392 DEBUG_TRACE(("stopping workers"));
5393
5394 // Stop signal received: somebody called mg_stop. Quit.
5396
5397 // Wakeup workers that are waiting for connections to handle.
5399
5400 // Wait until all threads finish
5401 (void) pthread_mutex_lock(&ctx->mutex);
5402 while (ctx->num_threads > 0) {
5403 (void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
5404 }
5406
5407 // All threads exited, no sync is needed. Destroy mutex and condvars
5412
5413#if !defined(NO_SSL)
5414 uninitialize_ssl(ctx);
5415#endif
5416 DEBUG_TRACE(("exiting"));
5417
5419
5420 // Signal mg_stop() that we're done.
5421 // WARNING: This must be the very last thing this
5422 // thread does, as ctx becomes invalid after this line.
5423 ctx->stop_flag = 2;
5424 return NULL;
5425}
5426
5427static void free_context(struct mg_context *ctx) {
5428 int i;
5429
5430 // Deallocate config parameters
5431 for (i = 0; i < NUM_OPTIONS; i++) {
5432 if (ctx->config[i] != NULL)
5433 free(ctx->config[i]);
5434 }
5435
5436#ifndef NO_SSL
5437 // Deallocate SSL context
5438 if (ctx->ssl_ctx != NULL) {
5439 SSL_CTX_free(ctx->ssl_ctx);
5440 }
5441 if (ssl_mutexes != NULL) {
5442 free(ssl_mutexes);
5443 ssl_mutexes = NULL;
5444 }
5445#endif // !NO_SSL
5446
5447 // Deallocate context itself
5448 free(ctx);
5449}
5450
5451void mg_stop(struct mg_context *ctx) {
5452 ctx->stop_flag = 1;
5453
5454 // Wait until mg_fini() stops
5455 while (ctx->stop_flag != 2) {
5456 (void) mg_sleep(10);
5457 }
5458 free_context(ctx);
5459
5460#if defined(_WIN32) && !defined(__SYMBIAN32__)
5461 (void) WSACleanup();
5462#endif // _WIN32
5463}
5464
5465struct mg_context *mg_start(const char **options,
5466 mg_event_handler_t func,
5467 void *user_data) {
5468 struct mg_context *ctx;
5469 const char *name, *value, *default_value;
5470 int i;
5471
5472#if defined(_WIN32) && !defined(__SYMBIAN32__)
5473 WSADATA data;
5474 WSAStartup(MAKEWORD(2,2), &data);
5475#endif // _WIN32
5476
5477 // Allocate context and initialize reasonable general case defaults.
5478 // TODO(lsm): do proper error handling here.
5479 if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {
5480 return NULL;
5481 }
5482 ctx->event_handler = func;
5483 ctx->user_data = user_data;
5484
5485 while (options && (name = *options++) != NULL) {
5486 if ((i = get_option_index(name)) == -1) {
5487 cry(fc(ctx), "Invalid option: %s", name);
5488 free_context(ctx);
5489 return NULL;
5490 } else if ((value = *options++) == NULL) {
5491 cry(fc(ctx), "%s: option value cannot be NULL", name);
5492 free_context(ctx);
5493 return NULL;
5494 }
5495 if (ctx->config[i] != NULL) {
5496 cry(fc(ctx), "warning: %s: duplicate option", name);
5497 free(ctx->config[i]);
5498 }
5499 ctx->config[i] = mg_strdup(value);
5500 DEBUG_TRACE(("[%s] -> [%s]", name, value));
5501 }
5502
5503 // Set default value if needed
5504 for (i = 0; config_options[i * 2] != NULL; i++) {
5505 default_value = config_options[i * 2 + 1];
5506 if (ctx->config[i] == NULL && default_value != NULL) {
5508 }
5509 }
5510
5511 // NOTE(lsm): order is important here. SSL certificates must
5512 // be initialized before listening ports. UID must be set last.
5513 if (!set_gpass_option(ctx) ||
5514#if !defined(NO_SSL)
5515 !set_ssl_option(ctx) ||
5516#endif
5517 !set_ports_option(ctx) ||
5518#if !defined(_WIN32)
5519 !set_uid_option(ctx) ||
5520#endif
5521 !set_acl_option(ctx)) {
5522 free_context(ctx);
5523 return NULL;
5524 }
5525
5526#if !defined(_WIN32) && !defined(__SYMBIAN32__)
5527 // Ignore SIGPIPE signal, so if browser cancels the request, it
5528 // won't kill the whole process.
5530#endif // !_WIN32
5531
5533 (void) pthread_cond_init(&ctx->cond, NULL);
5536
5537 // Start master (listening) thread
5539
5540 // Start worker threads
5541 for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
5542 if (mg_start_thread(worker_thread, ctx) != 0) {
5543 cry(fc(ctx), "Cannot start worker thread: %ld", (long) ERRNO);
5544 } else {
5545 ctx->num_threads++;
5546 }
5547 }
5548
5549 return ctx;
5550}
5551
5552#ifdef USE_LUA
5553#ifdef _WIN32
5554static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
5555 int offset) {
5557 HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
5558 void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
5559 CloseHandle(mh);
5560 return p;
5561}
5562#define munmap(x, y) UnmapViewOfFile(x)
5563#define MAP_FAILED NULL
5564#define MAP_PRIVATE 0
5565#define PROT_READ 0
5566#else
5567#include <sys/mman.h>
5568#endif
5569
5570static const char *LUASOCKET = "luasocket";
5571
5572// Forward declarations
5573static int handle_lsp_request(struct mg_connection *, const char *,
5574 struct file *, struct lua_State *);
5575
5576static void reg_string(struct lua_State *L, const char *name, const char *val) {
5579 lua_rawset(L, -3);
5580}
5581
5582static void reg_int(struct lua_State *L, const char *name, int val) {
5585 lua_rawset(L, -3);
5586}
5587
5588static void reg_function(struct lua_State *L, const char *name,
5589 lua_CFunction func, struct mg_connection *conn) {
5591 lua_pushlightuserdata(L, conn);
5592 lua_pushcclosure(L, func, 1);
5593 lua_rawset(L, -3);
5594}
5595
5596static int lsp_sock_close(lua_State *L) {
5597 if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
5598 lua_getfield(L, -1, "sock");
5600 } else {
5601 return luaL_error(L, "invalid :close() call");
5602 }
5603 return 1;
5604}
5605
5606static int lsp_sock_recv(lua_State *L) {
5607 char buf[2000];
5608 int n;
5609
5610 if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
5611 lua_getfield(L, -1, "sock");
5612 n = recv((SOCKET) lua_tonumber(L, -1), buf, sizeof(buf), 0);
5613 if (n <= 0) {
5614 lua_pushnil(L);
5615 } else {
5616 lua_pushlstring(L, buf, n);
5617 }
5618 } else {
5619 return luaL_error(L, "invalid :close() call");
5620 }
5621 return 1;
5622}
5623
5624static int lsp_sock_send(lua_State *L) {
5625 const char *buf;
5626 size_t len, sent = 0;
5627 int n, sock;
5628
5629 if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) {
5630 buf = lua_tolstring(L, -1, &len);
5631 lua_getfield(L, -2, "sock");
5632 sock = (int) lua_tonumber(L, -1);
5633 while (sent < len) {
5634 if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) {
5635 break;
5636 }
5637 sent += n;
5638 }
5639 lua_pushnumber(L, n);
5640 } else {
5641 return luaL_error(L, "invalid :close() call");
5642 }
5643 return 1;
5644}
5645
5646static const struct luaL_Reg luasocket_methods[] = {
5647 {"close", lsp_sock_close},
5648 {"send", lsp_sock_send},
5649 {"recv", lsp_sock_recv},
5650 {NULL, NULL}
5651};
5652
5653static int lsp_connect(lua_State *L) {
5654 char ebuf[100];
5655 SOCKET sock;
5656
5657 if (lua_isstring(L, -3) && lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
5658 sock = conn2(lua_tostring(L, -3), (int) lua_tonumber(L, -2),
5659 (int) lua_tonumber(L, -1), ebuf, sizeof(ebuf));
5660 if (sock == INVALID_SOCKET) {
5661 return luaL_error(L, ebuf);
5662 } else {
5663 lua_newtable(L);
5664 reg_int(L, "sock", sock);
5665 reg_string(L, "host", lua_tostring(L, -4));
5667 lua_setmetatable(L, -2);
5668 }
5669 } else {
5670 return luaL_error(L, "connect(host,port,is_ssl): invalid parameter given.");
5671 }
5672 return 1;
5673}
5674
5675static int lsp_error(lua_State *L) {
5676 lua_getglobal(L, "mg");
5677 lua_getfield(L, -1, "onerror");
5678 lua_pushvalue(L, -3);
5679 lua_pcall(L, 1, 0, 0);
5680 return 0;
5681}
5682
5683// Silently stop processing chunks.
5684static void lsp_abort(lua_State *L) {
5685 int top = lua_gettop(L);
5686 lua_getglobal(L, "mg");
5687 lua_pushnil(L);
5688 lua_setfield(L, -2, "onerror");
5689 lua_settop(L, top);
5690 lua_pushstring(L, "aborting");
5691 lua_error(L);
5692}
5693
5694static int lsp(struct mg_connection *conn, const char *path,
5695 const char *p, int64_t len, lua_State *L) {
5696 int i, j, pos = 0, lines = 1, lualines = 0;
5697 char chunkname[MG_BUF_LEN];
5698
5699 for (i = 0; i < len; i++) {
5700 if (p[i] == '\n') lines++;
5701 if (p[i] == '<' && p[i + 1] == '?') {
5702 for (j = i + 1; j < len ; j++) {
5703 if (p[j] == '\n') lualines++;
5704 if (p[j] == '?' && p[j + 1] == '>') {
5705 mg_write(conn, p + pos, i - pos);
5706
5707 snprintf(chunkname, sizeof(chunkname), "@%s+%i", path, lines);
5708 lua_pushlightuserdata(L, conn);
5710 if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) {
5711 // Syntax error or OOM. Error message is pushed on stack.
5712 lua_pcall(L, 1, 0, 0);
5713 } else {
5714 // Success loading chunk. Call it.
5715 lua_pcall(L, 0, 0, 1);
5716 }
5717
5718 pos = j + 2;
5719 i = pos - 1;
5720 break;
5721 }
5722 }
5723 if (lualines > 0) {
5724 lines += lualines;
5725 lualines = 0;
5726 }
5727 }
5728 }
5729
5730 if (i > pos) {
5731 mg_write(conn, p + pos, i - pos);
5732 }
5733
5734 return 0;
5735}
5736
5737static int lsp_write(lua_State *L) {
5738 int i, num_args;
5739 const char *str;
5740 size_t size;
5741 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
5742
5744 for (i = 1; i <= num_args; i++) {
5745 if (lua_isstring(L, i)) {
5746 str = lua_tolstring(L, i, &size);
5747 mg_write(conn, str, size);
5748 }
5749 }
5750
5751 return 0;
5752}
5753
5754static int lsp_read(lua_State *L) {
5755 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
5756 char buf[1024];
5757 int len = mg_read(conn, buf, sizeof(buf));
5758
5759 if (len <= 0) return 0;
5760 lua_pushlstring(L, buf, len);
5761
5762 return 1;
5763}
5764
5765// mg.include: Include another .lp file
5766static int lsp_include(lua_State *L) {
5767 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
5769 if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) {
5770 // handle_lsp_request returned an error code, meaning an error occured in
5771 // the included page and mg.onerror returned non-zero. Stop processing.
5772 lsp_abort(L);
5773 }
5774 return 0;
5775}
5776
5777// mg.cry: Log an error. Default value for mg.onerror.
5778static int lsp_cry(lua_State *L){
5779 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
5780 cry(conn, "%s", lua_tostring(L, -1));
5781 return 0;
5782}
5783
5784// mg.redirect: Redirect the request (internally).
5785static int lsp_redirect(lua_State *L) {
5786 struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
5787 conn->request_info.uri = lua_tostring(L, -1);
5788 handle_request(conn);
5789 lsp_abort(L);
5790 return 0;
5791}
5792
5793static void prepare_lua_environment(struct mg_connection *conn, lua_State *L) {
5794 const struct mg_request_info *ri = &conn->request_info;
5795 extern void luaL_openlibs(lua_State *);
5796 int i;
5797
5799#ifdef USE_LUA_SQLITE3
5800 { extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); }
5801#endif
5802
5804 lua_pushliteral(L, "__index");
5806 lua_rawset(L, -3);
5807 lua_pop(L, 1);
5808 lua_register(L, "connect", lsp_connect);
5809
5810 if (conn == NULL) return;
5811
5812 // Register mg module
5813 lua_newtable(L);
5814
5815 reg_function(L, "read", lsp_read, conn);
5816 reg_function(L, "write", lsp_write, conn);
5817 reg_function(L, "cry", lsp_cry, conn);
5818 reg_function(L, "include", lsp_include, conn);
5819 reg_function(L, "redirect", lsp_redirect, conn);
5820 reg_string(L, "version", MONGOOSE_VERSION);
5821
5822 // Export request_info
5823 lua_pushstring(L, "request_info");
5824 lua_newtable(L);
5825 reg_string(L, "request_method", ri->request_method);
5826 reg_string(L, "uri", ri->uri);
5827 reg_string(L, "http_version", ri->http_version);
5828 reg_string(L, "query_string", ri->query_string);
5829 reg_int(L, "remote_ip", ri->remote_ip);
5830 reg_int(L, "remote_port", ri->remote_port);
5831 reg_int(L, "num_headers", ri->num_headers);
5832 lua_pushstring(L, "http_headers");
5833 lua_newtable(L);
5834 for (i = 0; i < ri->num_headers; i++) {
5835 reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
5836 }
5837 lua_rawset(L, -3);
5838 lua_rawset(L, -3);
5839
5840 lua_setglobal(L, "mg");
5841
5842 // Register default mg.onerror function
5843 luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
5844 "debug.traceback(e, 1)) end");
5845}
5846
5847static int lua_error_handler(lua_State *L) {
5848 const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
5849
5850 lua_getglobal(L, "mg");
5851 if (!lua_isnil(L, -1)) {
5852 lua_getfield(L, -1, "write"); // call mg.write()
5854 lua_pushliteral(L, "\n");
5855 lua_call(L, 2, 0);
5856 luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
5857 } else {
5858 printf("Lua error: [%s]\n", error_msg);
5859 luaL_dostring(L, "print(debug.traceback(), '\\n')");
5860 }
5861 // TODO(lsm): leave the stack balanced
5862
5863 return 0;
5864}
5865
5866void mg_exec_lua_script(struct mg_connection *conn, const char *path,
5867 const void **exports) {
5868 int i;
5869 lua_State *L;
5870
5871 if (path != NULL && (L = luaL_newstate()) != NULL) {
5874
5876 if (exports != NULL) {
5877 for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
5880 lua_rawset(L, -3);
5881 }
5882 }
5883
5884 if (luaL_loadfile(L, path) != 0) {
5886 }
5887 lua_pcall(L, 0, 0, -2);
5888 lua_close(L);
5889 }
5890}
5891
5892static void lsp_send_err(struct mg_connection *conn, struct lua_State *L,
5893 const char *fmt, ...) {
5894 char buf[MG_BUF_LEN];
5895 va_list ap;
5896
5897 va_start(ap, fmt);
5898 vsnprintf(buf, sizeof(buf), fmt, ap);
5899 va_end(ap);
5900
5901 if (L == NULL) {
5902 send_http_error(conn, 500, http_500_error, "%s", buf);
5903 } else {
5904 lua_pushstring(L, buf);
5905 lua_error(L);
5906 }
5907}
5908
5909static int handle_lsp_request(struct mg_connection *conn, const char *path,
5910 struct file *filep, struct lua_State *ls) {
5911 void *p = NULL;
5912 lua_State *L = NULL;
5913 FILE *fp = NULL;
5914 int error = 1;
5915
5916 // We need both mg_stat to get file size, and mg_fopen to get fd
5917 if (!mg_stat(path, filep) || (fp = mg_fopen(path, "r")) == NULL) {
5918 lsp_send_err(conn, ls, "File [%s] not found", path);
5919 } else if ((p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
5920 fileno(fp), 0)) == MAP_FAILED) {
5921 lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
5922 fileno(fp), strerror(errno));
5923 } else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
5924 send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
5925 } else {
5926 // We're not sending HTTP headers here, Lua page must do it.
5927 if (ls == NULL) {
5929 }
5930 error = lsp(conn, path, p, filep->size, L);
5931 }
5932
5933 if (L != NULL && ls == NULL) lua_close(L);
5934 if (p != NULL) munmap(p, filep->size);
5935 fclose(fp);
5936
5937 return error;
5938}
5939#endif // USE_LUA
#define FALSE
Definition cfortran.h:309
int done
unsigned int DWORD
Definition mcstd.h:51
int mg_modify_passwords_file(const char *fname, const char *domain, const char *user, const char *pass)
#define MG_EVENT_LOG
Definition mongoose4.h:58
int mg_websocket_read(struct mg_connection *, int *bits, char **data)
void *(* mg_thread_func_t)(void *)
Definition mongoose4.h:218
void mg_close_connection(struct mg_connection *conn)
int mg_websocket_write(struct mg_connection *conn, int opcode, const char *data, size_t data_len)
int type
Definition mongoose4.h:54
#define PRINTF_FORMAT_STRING(s)
Definition mongoose4.h:108
const char * mg_get_header(const struct mg_connection *conn, const char *name)
int mg_get_var(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len)
int mg_start_thread(mg_thread_func_t func, void *param)
const char * request_method
Definition mongoose4.h:37
struct mg_request_info * request_info
Definition mongoose4.h:67
struct mg_connection * conn
Definition mongoose4.h:66
char * mg_md5(char buf[33],...)
const char * query_string
Definition mongoose4.h:40
#define MG_REQUEST_BEGIN
Definition mongoose4.h:55
const char * uri
Definition mongoose4.h:38
#define MG_THREAD_END
Definition mongoose4.h:60
struct mg_context * mg_start(const char **options, mg_event_handler_t func, void *user_data)
int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
#define PRINTF_ARGS(x, y)
Definition mongoose4.h:114
int mg_write(struct mg_connection *conn, const void *buf, int len)
const char * mg_get_builtin_mime_type(const char *path)
FILE * mg_upload(struct mg_connection *conn, const char *destination_dir, char *path, int path_len)
void mg_send_file(struct mg_connection *conn, const char *path)
const char * http_version
Definition mongoose4.h:39
#define MG_THREAD_BEGIN
Definition mongoose4.h:59
#define MG_HTTP_ERROR
Definition mongoose4.h:57
void * user_data
Definition mongoose4.h:62
int mg_get_cookie(const char *cookie_header, const char *var_name, char *dst, size_t dst_size)
void mg_stop(struct mg_context *ctx)
void * event_param
Definition mongoose4.h:64
int(* mg_event_handler_t)(struct mg_event *event)
Definition mongoose4.h:70
const char * remote_user
Definition mongoose4.h:41
#define MG_REQUEST_END
Definition mongoose4.h:56
void mg_websocket_handshake(struct mg_connection *)
const char * mg_version(void)
int mg_read(struct mg_connection *conn, void *buf, int len)
int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
unsigned char in[64]
Definition mongoose6.h:933
void * SSL_CTX
Definition mongoose6.h:1175
uint32_t bits[2]
Definition mongoose6.h:932
uint32_t buf[4]
Definition mongoose6.h:931
void * SSL
Definition mongoose6.h:1174
int mg_printf(struct mg_connection *conn, const char *fmt,...)
void ** info
Definition fesimdaq.cxx:41
DWORD n[4]
Definition mana.cxx:247
INT index
Definition mana.cxx:271
char param[10][256]
Definition mana.cxx:250
void * data
Definition mana.cxx:268
INT type
Definition mana.cxx:269
char addr[128]
Definition mcnaf.cxx:104
double count
Definition mdump.cxx:33
INT i
Definition mdump.cxx:32
INT bl
Definition mdump.cxx:29
char response[10000]
Definition melog.cxx:90
void base64_encode(char *s, char *d)
Definition melog.cxx:47
static int offset
Definition mgd.cxx:1500
#define TRUE
Definition midas.h:182
MidasHistoryInterface * mh
#define end
#define read(n, a, f)
#define sleep(ms)
#define mask(slot)
Definition midas_macro.h:54
#define name(x)
Definition midas_macro.h:24
static FILE * fp
static int is_put_or_delete_request(const struct mg_connection *conn)
static void process_new_connection(struct mg_connection *conn)
static int mg_strncasecmp(const char *s1, const char *s2, size_t len)
static int is_authorized_for_put(struct mg_connection *conn)
#define mg_mkdir(x, y)
static void sockaddr_to_string(char *buf, size_t len, const union usa *usa)
#define SOMAXCONN
#define IP_ADDR_STR_LEN
#define MD5STEP(f, w, x, y, z, data, s)
static int consume_socket(struct mg_context *ctx, struct socket *sp)
static int check_authorization(struct mg_connection *conn, const char *path)
#define HEXTOI(x)
static void mkcol(struct mg_connection *conn, const char *path)
static int check_acl(struct mg_context *ctx, uint32_t remote_ip)
static int64_t left_to_read(const struct mg_connection *conn)
const char * mime_type
static int set_non_blocking_mode(SOCKET sock)
static void ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
static int set_ports_option(struct mg_context *ctx)
static void put_file(struct mg_connection *conn, const char *path)
static int set_uid_option(struct mg_context *ctx)
static void do_ssi_exec(struct mg_connection *conn, char *tag)
static void mg_strlcpy(register char *dst, register const char *src, size_t n)
void mg_url_encode(const char *src, char *dst, size_t dst_len)
static int set_sock_timeout(SOCKET sock, int milliseconds)
static int set_gpass_option(struct mg_context *ctx)
static int mg_strcasecmp(const char *s1, const char *s2)
static void * realloc2(void *ptr, size_t size)
#define INVALID_SOCKET
size_t ext_len
static void send_http_error(struct mg_connection *, int, const char *, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(4
static int set_throttle(const char *spec, uint32_t remote_ip, const char *uri)
static void uninitialize_ssl(struct mg_context *ctx)
static int WINCDECL compare_dir_entries(const void *p1, const void *p2)
static int should_keep_alive(const struct mg_connection *conn)
static SOCKET conn2(const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len)
static void handle_file_request(struct mg_connection *conn, const char *path, struct file *filep)
static const char * ssl_error(void)
static int must_hide_file(struct mg_connection *conn, const char *path)
static int parse_net(const char *spec, uint32_t *net, uint32_t *mask)
static int substitute_index_file(struct mg_connection *conn, char *path, size_t path_len, struct file *filep)
const char * extension
#define ERRNO
static void close_all_listening_sockets(struct mg_context *ctx)
#define F1(x, y, z)
static void prepare_cgi_environment(struct mg_connection *conn, const char *prog, struct cgi_env_block *blk)
#define closesocket(a)
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)
static const char * get_header(const struct mg_request_info *ri, const char *name)
static int mg_chunked_printf(struct mg_connection *conn, const char *fmt,...)
static void print_dav_dir_entry(struct de *de, void *data)
static void dir_scan_callback(struct de *de, void *data)
static void remove_double_dots_and_double_slashes(char *s)
static void reset_per_request_attributes(struct mg_connection *conn)
static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap)
static pthread_mutex_t * ssl_mutexes
static int is_not_modified(const struct mg_connection *conn, const struct file *filep)
static int get_option_index(const char *name)
static void print_props(struct mg_connection *conn, const char *uri, struct file *filep)
static void handle_propfind(struct mg_connection *conn, const char *path, struct file *filep)
static char * mg_strdup(const char *str)
static void accept_new_connection(const struct socket *listener, struct mg_context *ctx)
#define CRYPTO_LIB
#define CGI_ENVIRONMENT_SIZE
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx)
static int put_dir(const char *path)
struct mg_connection * mg_download(const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len, const char *fmt,...)
static void parse_http_headers(char **buf, struct mg_request_info *ri)
#define INT64_FMT
#define MONGOOSE_VERSION
static void get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec)
static void MD5Transform(uint32_t buf[4], uint32_t const in[16])
static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int64_t len)
#define F4(x, y, z)
static char * skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar)
static FILE * open_auth_file(struct mg_connection *conn, const char *path)
static char * skip(char **buf, const char *delimiters)
#define DEBUG_TRACE(x)
static int forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
static int set_ssl_option(struct mg_context *ctx)
static int sslize(struct mg_connection *conn, SSL_CTX *s, int(*func)(SSL *))
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)
static void set_close_on_exec(int fd)
#define ARRAY_SIZE(array)
static int get_first_ssl_listener_index(const struct mg_context *ctx)
static void log_header(const struct mg_connection *conn, const char *header, FILE *fp)
static int isbyte(int n)
static uint32_t get_remote_ip(const struct mg_connection *conn)
static void handle_directory_request(struct mg_connection *conn, const char *dir)
static void gmt_time_string(char *buf, size_t buf_len, time_t *t)
#define MAX_REQUEST_SIZE
static const char * mg_strcasestr(const char *big_str, const char *small_str)
static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
static const char * config_options[]
static void send_file_data(struct mg_connection *conn, FILE *fp, int64_t offset, int64_t len)
static int authorize(struct mg_connection *conn, FILE *fp)
static void bin2str(char *to, const unsigned char *p, size_t len)
static void byteReverse(unsigned char *buf, unsigned longs)
static void handle_options_request(struct mg_connection *conn)
static int is_valid_uri(const char *uri)
static const char * next_option(const char *list, struct vec *val, struct vec *eq_val)
static const char * suggest_connection_header(const struct mg_connection *conn)
#define EXTRA_HTTP_HEADERS
static time_t parse_date_string(const char *datetime)
int SOCKET
static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level)
#define F3(x, y, z)
static int read_request(FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread)
static void handle_ssi_file_request(struct mg_connection *conn, const char *path)
static void MD5Init(MD5_CTX *ctx)
static int lowercase(const char *s)
static int parse_range_header(const char *header, int64_t *a, int64_t *b)
#define SSL_LIB
static pid_t spawn_process(struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin, int fdout, const char *dir)
static void handle_delete_request(struct mg_connection *conn, const char *path)
#define MSG_NOSIGNAL
#define MAX_CGI_ENVIR_VARS
static int parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size, struct ah *ah)
static void * master_thread(void *thread_func_param)
@ ENABLE_DIRECTORY_LISTING
@ GLOBAL_PASSWORDS_FILE
@ SSL_CERTIFICATE
@ RUN_AS_USER
@ REWRITE
@ ENABLE_KEEP_ALIVE
@ ACCESS_CONTROL_LIST
@ AUTHENTICATION_DOMAIN
@ HIDE_FILES
@ ERROR_LOG_FILE
@ THROTTLE
@ CGI_EXTENSIONS
@ CGI_ENVIRONMENT
@ PUT_DELETE_PASSWORDS_FILE
@ NUM_OPTIONS
@ NUM_THREADS
@ DOCUMENT_ROOT
@ PROTECT_URI
@ ACCESS_LOG_FILE
@ REQUEST_TIMEOUT
@ SSI_EXTENSIONS
@ CGI_INTERPRETER
@ LISTENING_PORTS
@ INDEX_FILES
@ EXTRA_MIME_TYPES
struct mg_connection * mg_connect(const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len)
static void fclose_on_exec(FILE *fp)
#define mg_remove(x)
static void construct_etag(char *buf, size_t buf_len, const struct file *filep)
static int mg_stat(const char *path, struct file *filep)
static void send_ssi_file(struct mg_connection *, const char *, FILE *, int)
static void print_dir_entry(const struct de *de)
static int scan_directory(struct mg_connection *conn, const char *dir, void *data, void(*cb)(struct de *, void *))
static void produce_socket(struct mg_context *ctx, const struct socket *sp)
static void send_authorization_request(struct mg_connection *conn)
#define MG_BUF_LEN
static void free_context(struct mg_context *ctx)
static int convert_uri_to_file_name(struct mg_connection *conn, char *buf, size_t buf_len, struct file *filep)
static unsigned long ssl_id_callback(void)
static int parse_port_string(const struct vec *vec, struct socket *so)
static char * addenv(struct cgi_env_block *block, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
static const struct @22 builtin_mime_types[]
static int match_prefix(const char *pattern, int pattern_len, const char *str)
static void handle_request(struct mg_connection *conn)
static int is_big_endian(void)
static int remove_directory(struct mg_connection *conn, const char *dir)
static int get_request_len(const char *buf, int buf_len)
static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap)
#define WINCDECL
static void handle_cgi_request(struct mg_connection *conn, const char *prog)
static void static void static int getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len)
static int mg_snprintf(char *buf, size_t buflen, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(3
static struct mg_connection * fc(struct mg_context *ctx)
static void * worker_thread(void *thread_func_param)
static FILE * mg_fopen(const char *path, const char *mode)
static void static void cry(struct mg_connection *conn, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
#define F2(x, y, z)
static void close_socket_gracefully(struct mg_connection *conn)
static int call_user(int type, struct mg_connection *conn, void *p)
static int parse_http_message(char *buf, int len, struct mg_request_info *ri)
#define PATH_MAX
static const char * http_500_error
static int set_acl_option(struct mg_context *ctx)
#define mg_sleep(x)
static int is_valid_port(unsigned int port)
#define PASSWORDS_FILE_NAME
static void log_access(const struct mg_connection *conn)
static void close_connection(struct mg_connection *conn)
static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len)
static void redirect_to_https_port(struct mg_connection *conn, int ssl_index)
#define STRUCT_FILE_INITIALIZER
static int is_valid_http_method(const char *method)
char * inet_ntoa(struct in_addr n)
const char * inet_ntop(int af, const void *src, char *dst, socklen_t size)
int inet_pton(int af, const char *src, void *dst)
#define R1(v, w, x, y, z, i)
static int left(const struct frozen *f)
#define R2(v, w, x, y, z, i)
#define R0(v, w, x, y, z, i)
static int expect(struct frozen *f, const char *s, int len, enum json_type t)
#define blk(i)
#define R3(v, w, x, y, z, i)
#define rol(value, bits)
static uint32_t blk0(union char64long16 *block, int i)
#define R4(v, w, x, y, z, i)
timeval tv
Definition msysmon.cxx:1095
#define gmtime
Definition msystem.h:266
#define localtime
Definition msystem.h:265
MUTEX_T * tm
Definition odbedit.cxx:39
double total[100]
Definition odbhist.cxx:42
INT j
Definition odbhist.cxx:40
double value[100]
Definition odbhist.cxx:42
INT k
Definition odbhist.cxx:40
char str[256]
Definition odbhist.cxx:33
char file_name[256]
Definition odbhist.cxx:41
DWORD status
Definition odbhist.cxx:39
char var_name[256]
Definition odbhist.cxx:41
TH1X EXPRT * h1_book(const char *name, const char *title, int bins, double min, double max)
Definition rmidas.h:24
char * nc
char * nonce
char * cnonce
char * uri
char * user
char * qop
char * response
char * vars[MAX_CGI_ENVIR_VARS]
struct mg_connection * conn
char buf[CGI_ENVIRONMENT_SIZE]
char * file_name
struct mg_connection * conn
struct file file
struct de * entries
int64_t size
int is_directory
time_t modification_time
time_t last_throttle_time
time_t birth_time
int64_t content_len
SSL_CTX * client_ssl_ctx
int64_t num_bytes_sent
struct socket client
int64_t num_bytes_read
struct mg_event event
struct mg_request_info request_info
int64_t last_throttle_bytes
struct mg_context * ctx
volatile int stop_flag
pthread_cond_t sq_empty
int num_listening_sockets
SSL_CTX * ssl_ctx
mg_event_handler_t event_handler
volatile int sq_head
void * user_data
volatile int num_threads
struct socket * listening_sockets
struct socket queue[MGSQLEN]
pthread_cond_t sq_full
volatile int sq_tail
char * config[NUM_OPTIONS]
pthread_mutex_t mutex
pthread_cond_t cond
union usa lsa
unsigned ssl_redir
unsigned is_ssl
SOCKET sock
union usa rsa
size_t len
const char * ptr
double d
Definition system.cxx:1311
char c
Definition system.cxx:1310
@ DIR
Definition test_init.cxx:7
static double e(void)
Definition tinyexpr.c:136
static double pi(void)
Definition tinyexpr.c:135
static te_expr * list(state *s)
Definition tinyexpr.c:567
unsigned char c[64]
uint32_t l[16]
struct sockaddr sa
struct sockaddr_in sin

◆ MG_BUF_LEN

#define MG_BUF_LEN   8192

Definition at line 265 of file mongoose4.cxx.

◆ mg_mkdir

#define mg_mkdir (   x,
 
)    mkdir(x, y)

Definition at line 248 of file mongoose4.cxx.

◆ mg_remove

#define mg_remove (   x)    remove(x)

Definition at line 249 of file mongoose4.cxx.

◆ mg_sleep

#define mg_sleep (   x)    usleep((x) * 1000)

Definition at line 250 of file mongoose4.cxx.

◆ MGSQLEN

#define MGSQLEN   20

Definition at line 311 of file mongoose4.cxx.

◆ MONGOOSE_VERSION

#define MONGOOSE_VERSION   "4.2"

Definition at line 261 of file mongoose4.cxx.

◆ MSG_NOSIGNAL

#define MSG_NOSIGNAL   0

Definition at line 298 of file mongoose4.cxx.

◆ NO_SSL_DL

#define NO_SSL_DL   1

Definition at line 19 of file mongoose4.cxx.

◆ O_BINARY

#define O_BINARY   0

Definition at line 245 of file mongoose4.cxx.

◆ PASSWORDS_FILE_NAME

#define PASSWORDS_FILE_NAME   ".htpasswd"

Definition at line 262 of file mongoose4.cxx.

◆ PATH_MAX

#define PATH_MAX   4096

Definition at line 306 of file mongoose4.cxx.

◆ SOMAXCONN

#define SOMAXCONN   100

Definition at line 302 of file mongoose4.cxx.

◆ SSL_LIB

#define SSL_LIB   "libssl.so"

Definition at line 238 of file mongoose4.cxx.

◆ STRUCT_FILE_INITIALIZER

#define STRUCT_FILE_INITIALIZER   { 0, 0, 0, 0 }

Definition at line 434 of file mongoose4.cxx.

◆ WINCDECL

#define WINCDECL

Definition at line 255 of file mongoose4.cxx.

Typedef Documentation

◆ MD5_CTX

◆ SOCKET

Definition at line 254 of file mongoose4.cxx.

Enumeration Type Documentation

◆ anonymous enum

Enumerator
CGI_EXTENSIONS 
CGI_ENVIRONMENT 
PUT_DELETE_PASSWORDS_FILE 
CGI_INTERPRETER 
PROTECT_URI 
AUTHENTICATION_DOMAIN 
SSI_EXTENSIONS 
THROTTLE 
ACCESS_LOG_FILE 
ENABLE_DIRECTORY_LISTING 
ERROR_LOG_FILE 
GLOBAL_PASSWORDS_FILE 
INDEX_FILES 
ENABLE_KEEP_ALIVE 
ACCESS_CONTROL_LIST 
EXTRA_MIME_TYPES 
LISTENING_PORTS 
DOCUMENT_ROOT 
SSL_CERTIFICATE 
NUM_THREADS 
RUN_AS_USER 
REWRITE 
HIDE_FILES 
REQUEST_TIMEOUT 
NUM_OPTIONS 

Definition at line 447 of file mongoose4.cxx.

Function Documentation

◆ accept_new_connection()

static void accept_new_connection ( const struct socket listener,
struct mg_context ctx 
)
static

Definition at line 5323 of file mongoose4.cxx.

5324 {
5325 struct socket so;
5327 socklen_t len = sizeof(so.rsa);
5328 int on = 1;
5329 extern int check_midas_acl(const struct sockaddr *sa, int len);
5330
5331 if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) == INVALID_SOCKET) {
5332 } else if ((!check_acl(ctx, ntohl(* (uint32_t *) &so.rsa.sin.sin_addr))) || (!check_midas_acl(&so.rsa.sa, len))) {
5333 sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
5334 cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
5335 closesocket(so.sock);
5336 } else {
5337 // Put so socket structure into the queue
5338 DEBUG_TRACE(("Accepted socket %d", (int) so.sock));
5339 set_close_on_exec(so.sock);
5340 so.is_ssl = listener->is_ssl;
5341 so.ssl_redir = listener->ssl_redir;
5342 getsockname(so.sock, &so.lsa.sa, &len);
5343 // Set TCP keep-alive. This is needed because if HTTP-level keep-alive
5344 // is enabled, and client resets the connection, server won't get
5345 // TCP FIN or RST and will keep the connection open forever. With TCP
5346 // keep-alive, next keep-alive handshake will figure out that the client
5347 // is down and will close the server end.
5348 // Thanks to Igor Klopov who suggested the patch.
5349 setsockopt(so.sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &on, sizeof(on));
5351 produce_socket(ctx, &so);
5352 }
5353}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ addenv() [1/2]

static char static char * addenv ( struct cgi_env_block block,
const char fmt,
  ... 
)
static

Definition at line 3561 of file mongoose4.cxx.

3561 {
3562 int n, space;
3563 char *added;
3564 va_list ap;
3565
3566 // Calculate how much space is left in the buffer
3567 space = sizeof(block->buf) - block->len - 2;
3568 assert(space >= 0);
3569
3570 // Make a pointer to the free space int the buffer
3571 added = block->buf + block->len;
3572
3573 // Copy VARIABLE=VALUE\0 string into the free space
3574 va_start(ap, fmt);
3575 n = mg_vsnprintf(added, (size_t) space, fmt, ap);
3576 va_end(ap);
3577
3578 // Make sure we do not overflow buffer and the envp array
3579 if (n > 0 && n + 1 < space &&
3580 block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
3581 // Append a pointer to the added string into the envp array
3582 block->vars[block->nvars++] = added;
3583 // Bump up used length counter. Include \0 terminator
3584 block->len += n + 1;
3585 } else {
3586 cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt);
3587 }
3588
3589 return added;
3590}
Here is the call graph for this function:

◆ addenv() [2/2]

static char * addenv ( struct cgi_env_block block,
PRINTF_FORMAT_STRING(const char *fmt)  ,
  ... 
)
static
Here is the caller graph for this function:

◆ alloc_vprintf()

static int alloc_vprintf ( char **  buf,
size_t  size,
const char fmt,
va_list  ap 
)
static

Definition at line 1884 of file mongoose4.cxx.

1884 {
1886 int len;
1887
1888 // Windows is not standard-compliant, and vsnprintf() returns -1 if
1889 // buffer is too small. Also, older versions of msvcrt.dll do not have
1890 // _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
1891 // Therefore, we make two passes: on first pass, get required message length.
1892 // On second pass, actually print the message.
1893 va_copy(ap_copy, ap);
1894 len = vsnprintf(NULL, 0, fmt, ap_copy);
1895
1896 if (len > (int) size &&
1897 (size = len + 1) > 0 &&
1898 (*buf = (char *) malloc(size)) == NULL) {
1899 len = -1; // Allocation failed, mark failure
1900 } else {
1901 va_copy(ap_copy, ap);
1902 vsnprintf(*buf, size, fmt, ap_copy);
1903 }
1904
1905 return len;
1906}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ authorize()

static int authorize ( struct mg_connection conn,
FILE fp 
)
static

Definition at line 1237 of file mongoose4.cxx.

1237 {
1238 struct ah ah;
1239 char line[256], f_user[256], ha1[256], f_domain[256], buf[MG_BUF_LEN];
1240
1241 if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) {
1242 return 0;
1243 }
1244
1245 // Loop over passwords file
1246 while (fgets(line, sizeof(line), fp) != NULL) {
1247 if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
1248 continue;
1249 }
1250
1251 if (!strcmp(ah.user, f_user) &&
1255 }
1256
1257 return 0;
1258}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ bin2str()

static void bin2str ( char to,
const unsigned char p,
size_t  len 
)
static

Definition at line 1079 of file mongoose4.cxx.

1079 {
1080 static const char *hex = "0123456789abcdef";
1081
1082 for (; len--; p++) {
1083 *to++ = hex[p[0] >> 4];
1084 *to++ = hex[p[0] & 0x0f];
1085 }
1086 *to = '\0';
1087}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ byteReverse()

static void byteReverse ( unsigned char buf,
unsigned  longs 
)
static

Definition at line 892 of file mongoose4.cxx.

892 {
893 uint32_t t;
894
895 // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
896 if (is_big_endian()) {
897 do {
898 t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
899 ((unsigned) buf[1] << 8 | buf[0]);
900 * (uint32_t *) buf = t;
901 buf += 4;
902 } while (--longs);
903 }
904}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ call_user()

static int call_user ( int  type,
struct mg_connection conn,
void p 
)
static

Definition at line 2319 of file mongoose4.cxx.

2319 {
2320 if (conn != NULL && conn->ctx != NULL) {
2321 conn->event.user_data = conn->ctx->user_data;
2322 conn->event.type = type;
2323 conn->event.event_param = p;
2324 conn->event.request_info = &conn->request_info;
2325 conn->event.conn = conn;
2326 }
2327 return conn == NULL || conn->ctx == NULL || conn->ctx->event_handler == NULL ?
2328 0 : conn->ctx->event_handler(&conn->event);
2329}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_acl()

static int check_acl ( struct mg_context ctx,
uint32_t  remote_ip 
)
static

Definition at line 4977 of file mongoose4.cxx.

4977 {
4978 int allowed, flag;
4979 uint32_t net, mask;
4980 struct vec vec;
4981 const char *list = ctx->config[ACCESS_CONTROL_LIST];
4982
4983 // If any ACL is set, deny by default
4984 allowed = list == NULL ? '+' : '-';
4985
4986 while ((list = next_option(list, &vec, NULL)) != NULL) {
4987 flag = vec.ptr[0];
4988 if ((flag != '+' && flag != '-') ||
4989 parse_net(&vec.ptr[1], &net, &mask) == 0) {
4990 cry(fc(ctx), "%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
4991 return -1;
4992 }
4993
4994 if (net == (remote_ip & mask)) {
4995 allowed = flag;
4996 }
4997 }
4998
4999 return allowed == '+';
5000}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_authorization()

static int check_authorization ( struct mg_connection conn,
const char path 
)
static

Definition at line 1261 of file mongoose4.cxx.

1261 {
1262 char fname[PATH_MAX];
1263 struct vec uri_vec, filename_vec;
1264 const char *list;
1265 FILE *fp = NULL;
1266 int authorized = 1;
1267
1268 list = conn->ctx->config[PROTECT_URI];
1269 while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
1270 if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
1271 mg_snprintf(fname, sizeof(fname), "%.*s",
1272 (int) filename_vec.len, filename_vec.ptr);
1273 fp = mg_fopen(fname, "r");
1274 break;
1275 }
1276 }
1277
1278 if (fp == NULL) {
1279 fp = open_auth_file(conn, path);
1280 }
1281
1282 if (fp != NULL) {
1283 authorized = authorize(conn, fp);
1284 fclose(fp);
1285 }
1286
1287 return authorized;
1288}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_password()

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 
)
static

Definition at line 1110 of file mongoose4.cxx.

1112 {
1113 char ha2[32 + 1], expected_response[32 + 1];
1114
1115 // Some of the parameters may be NULL
1116 if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL ||
1117 qop == NULL || response == NULL) {
1118 return 0;
1119 }
1120
1121 // NOTE(lsm): due to a bug in MSIE, we do not compare the URI
1122 // TODO(lsm): check for authentication timeout
1123 if (// strcmp(dig->uri, c->ouri) != 0 ||
1124 strlen(response) != 32
1125 // || now - strtoul(dig->nonce, NULL, 10) > 3600
1126 ) {
1127 return 0;
1128 }
1129
1130 mg_md5(ha2, method, ":", uri, NULL);
1131 mg_md5(expected_response, ha1, ":", nonce, ":", nc,
1132 ":", cnonce, ":", qop, ":", ha2, NULL);
1133
1135}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ close_all_listening_sockets()

static void close_all_listening_sockets ( struct mg_context ctx)
static

Definition at line 4821 of file mongoose4.cxx.

4821 {
4822 int i;
4823 for (i = 0; i < ctx->num_listening_sockets; i++) {
4825 }
4826 free(ctx->listening_sockets);
4827}
Here is the caller graph for this function:

◆ close_connection()

static void close_connection ( struct mg_connection conn)
static

Definition at line 5080 of file mongoose4.cxx.

5080 {
5081 conn->must_close = 1;
5082
5083#ifndef NO_SSL
5084 if (conn->ssl != NULL) {
5085 // Run SSL_shutdown twice to ensure completly close SSL connection
5086 SSL_shutdown(conn->ssl);
5087 SSL_free(conn->ssl);
5088 conn->ssl = NULL;
5089 }
5090#endif
5091 if (conn->client.sock != INVALID_SOCKET) {
5093 conn->client.sock = INVALID_SOCKET;
5094 }
5095}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ close_socket_gracefully()

static void close_socket_gracefully ( struct mg_connection conn)
static

Definition at line 5047 of file mongoose4.cxx.

5047 {
5048#if defined(_WIN32)
5049 char buf[MG_BUF_LEN];
5050 int n;
5051#endif
5052 struct linger linger;
5053
5054 // Set linger option to avoid socket hanging out after close. This prevent
5055 // ephemeral port exhaust problem under high QPS.
5056 linger.l_onoff = 1;
5057 linger.l_linger = 1;
5059 (char *) &linger, sizeof(linger));
5060
5061 // Send FIN to the client
5062 shutdown(conn->client.sock, SHUT_WR);
5064
5065#if defined(_WIN32)
5066 // Read and discard pending incoming data. If we do not do that and close the
5067 // socket, the data in the send buffer may be discarded. This
5068 // behaviour is seen on Windows, when client keeps sending data
5069 // when server decides to close the connection; then when client
5070 // does recv() it gets no data back.
5071 do {
5072 n = pull(NULL, conn, buf, sizeof(buf));
5073 } while (n > 0);
5074#endif
5075
5076 // Now we know that our FIN is ACK-ed, safe to close
5077 closesocket(conn->client.sock);
5078}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ compare_dir_entries()

static int WINCDECL compare_dir_entries ( const void p1,
const void p2 
)
static

Definition at line 2993 of file mongoose4.cxx.

2993 {
2994 const struct de *a = (const struct de *) p1, *b = (const struct de *) p2;
2995 const char *query_string = a->conn->request_info.query_string;
2996 int cmp_result = 0;
2997
2998 if (query_string == NULL) {
2999 query_string = "na";
3000 }
3001
3002 if (a->file.is_directory && !b->file.is_directory) {
3003 return -1; // Always put directories on top
3004 } else if (!a->file.is_directory && b->file.is_directory) {
3005 return 1; // Always put directories on top
3006 } else if (*query_string == 'n') {
3007 cmp_result = strcmp(a->file_name, b->file_name);
3008 } else if (*query_string == 's') {
3009 cmp_result = a->file.size == b->file.size ? 0 :
3010 a->file.size > b->file.size ? 1 : -1;
3011 } else if (*query_string == 'd') {
3012 cmp_result = a->file.modification_time == b->file.modification_time ? 0 :
3013 a->file.modification_time > b->file.modification_time ? 1 : -1;
3014 }
3015
3016 return query_string[1] == 'd' ? -cmp_result : cmp_result;
3017}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ conn2()

static SOCKET conn2 ( const char host,
int  port,
int  use_ssl,
char ebuf,
size_t  ebuf_len 
)
static

Definition at line 2218 of file mongoose4.cxx.

2219 {
2220 struct sockaddr_in sin;
2221 struct hostent *he = NULL;
2222 SOCKET sock = INVALID_SOCKET;
2223
2224 (void) use_ssl; // Prevent warning for -DNO_SSL case
2225
2226 if (host == NULL) {
2227 snprintf(ebuf, ebuf_len, "%s", "NULL host");
2228#ifndef NO_SSL
2229 } else if (use_ssl && (void *)SSLv23_client_method == NULL) {
2230 snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized");
2231 // TODO(lsm): use something threadsafe instead of gethostbyname()
2232#endif
2233 } else if ((he = gethostbyname(host)) == NULL) {
2234 snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO));
2235 } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
2236 snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO));
2237 } else {
2238 set_close_on_exec(sock);
2239 sin.sin_family = AF_INET;
2240 sin.sin_port = htons((uint16_t) port);
2241 sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
2242 if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
2243 snprintf(ebuf, ebuf_len, "connect(%s:%d): %s",
2244 host, port, strerror(ERRNO));
2245 closesocket(sock);
2246 sock = INVALID_SOCKET;
2247 }
2248 }
2249 return sock;
2250}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ construct_etag()

static void construct_etag ( char buf,
size_t  buf_len,
const struct file filep 
)
static

Definition at line 3236 of file mongoose4.cxx.

3237 {
3238 snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
3239 (unsigned long) filep->modification_time, filep->size);
3240}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ consume_socket()

static int consume_socket ( struct mg_context ctx,
struct socket sp 
)
static

Definition at line 5206 of file mongoose4.cxx.

5206 {
5207 (void) pthread_mutex_lock(&ctx->mutex);
5208 DEBUG_TRACE(("going idle"));
5209
5210 // If the queue is empty, wait. We're idle at this point.
5211 while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
5212 pthread_cond_wait(&ctx->sq_full, &ctx->mutex);
5213 }
5214
5215 // If we're stopping, sq_head may be equal to sq_tail.
5216 if (ctx->sq_head > ctx->sq_tail) {
5217 // Copy socket from the queue and increment tail
5218 *sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];
5219 ctx->sq_tail++;
5220 DEBUG_TRACE(("grabbed socket %d, going busy", sp->sock));
5221
5222 // Wrap pointers if needed
5223 while (ctx->sq_tail > (int) ARRAY_SIZE(ctx->queue)) {
5224 ctx->sq_tail -= ARRAY_SIZE(ctx->queue);
5225 ctx->sq_head -= ARRAY_SIZE(ctx->queue);
5226 }
5227 }
5228
5231
5232 return !ctx->stop_flag;
5233}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ convert_uri_to_file_name()

static int convert_uri_to_file_name ( struct mg_connection conn,
char buf,
size_t  buf_len,
struct file filep 
)
static

Definition at line 2718 of file mongoose4.cxx.

2719 {
2720 struct vec a, b;
2721 const char *rewrite, *uri = conn->request_info.uri,
2722 *root = conn->ctx->config[DOCUMENT_ROOT];
2723 char *p;
2724 int match_len;
2725 char gz_path[PATH_MAX];
2726 char const* accept_encoding;
2727
2728 // No filesystem access
2729 if (root == NULL) {
2730 return 0;
2731 }
2732
2733 // Using buf_len - 1 because memmove() for PATH_INFO may shift part
2734 // of the path one byte on the right.
2735 // If document_root is NULL, leave the file empty.
2736 mg_snprintf(buf, buf_len - 1, "%s%s", root, uri);
2737
2738 rewrite = conn->ctx->config[REWRITE];
2739 while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
2740 if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
2741 mg_snprintf(buf, buf_len - 1, "%.*s%s", (int) b.len, b.ptr,
2742 uri + match_len);
2743 break;
2744 }
2745 }
2746
2747 if (mg_stat(buf, filep)) {
2748 return 1;
2749 }
2750
2751 // if we can't find the actual file, look for the file
2752 // with the same name but a .gz extension. If we find it,
2753 // use that and set the gzipped flag in the file struct
2754 // to indicate that the response need to have the content-
2755 // encoding: gzip header
2756 // we can only do this if the browser declares support
2757 if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
2758 if (strstr(accept_encoding,"gzip") != NULL) {
2759 snprintf(gz_path, sizeof(gz_path), "%s.gz", buf);
2760 if (mg_stat(gz_path, filep)) {
2761 filep->gzipped = 1;
2762 return 1;
2763 }
2764 }
2765 }
2766
2767 // Support PATH_INFO for CGI scripts.
2768 for (p = buf + strlen(root == NULL ? "" : root); *p != '\0'; p++) {
2769 if (*p == '/') {
2770 *p = '\0';
2772 strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 &&
2773 mg_stat(buf, filep)) {
2774 // Shift PATH_INFO block one character right, e.g.
2775 // "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
2776 // conn->path_info is pointing to the local variable "path" declared
2777 // in handle_request(), so PATH_INFO is not valid after
2778 // handle_request returns.
2779 conn->path_info = p + 1;
2780 memmove(p + 2, p + 1, strlen(p + 1) + 1); // +1 is for trailing \0
2781 p[1] = '/';
2782 return 1;
2783 } else {
2784 *p = '/';
2785 }
2786 }
2787 }
2788
2789 return 0;
2790}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ cry() [1/2]

static void cry ( struct mg_connection conn,
const char fmt,
  ... 
)
static

Definition at line 2358 of file mongoose4.cxx.

2358 {
2360 va_list ap;
2361 FILE *fp;
2362 time_t timestamp;
2363
2364 va_start(ap, fmt);
2365 (void) vsnprintf(buf, sizeof(buf), fmt, ap);
2366 va_end(ap);
2367
2368 // Do not lock when getting the callback value, here and below.
2369 // I suppose this is fine, since function cannot disappear in the
2370 // same way string option can.
2371 if (call_user(MG_EVENT_LOG, conn, buf) == 0) {
2372 fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
2373 fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
2374
2375 if (fp != NULL) {
2376 flockfile(fp);
2377 timestamp = time(NULL);
2378
2379 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
2380 fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp,
2381 src_addr);
2382
2383 if (conn->request_info.request_method != NULL) {
2384 fprintf(fp, "%s %s: ", conn->request_info.request_method,
2385 conn->request_info.uri);
2386 }
2387
2388 fprintf(fp, "%s", buf);
2389 fputc('\n', fp);
2390 funlockfile(fp);
2391 fclose(fp);
2392 }
2393 }
2394}
Here is the call graph for this function:

◆ cry() [2/2]

static void static void cry ( struct mg_connection conn,
PRINTF_FORMAT_STRING(const char *fmt)  ,
  ... 
)
static
Here is the caller graph for this function:

◆ dir_scan_callback()

static void dir_scan_callback ( struct de de,
void data 
)
static

Definition at line 3123 of file mongoose4.cxx.

3123 {
3124 struct dir_scan_data *dsd = (struct dir_scan_data *) data;
3125
3126 if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) {
3127 dsd->arr_size *= 2;
3128 dsd->entries = (struct de *) realloc2(dsd->entries, dsd->arr_size *
3129 sizeof(dsd->entries[0]));
3130 }
3131 if (dsd->entries == NULL) {
3132 // TODO(lsm): propagate an error to the caller
3133 dsd->num_entries = 0;
3134 } else {
3135 dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
3136 dsd->entries[dsd->num_entries].file = de->file;
3137 dsd->entries[dsd->num_entries].conn = de->conn;
3138 dsd->num_entries++;
3139 }
3140}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ do_ssi_exec()

static void do_ssi_exec ( struct mg_connection conn,
char tag 
)
static

Definition at line 4017 of file mongoose4.cxx.

4017 {
4018 char cmd[MG_BUF_LEN];
4019 FILE *fp;
4020
4021 if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
4022 cry(conn, "Bad SSI #exec: [%s]", tag);
4023 } else if ((fp = popen(cmd, "r")) == NULL) {
4024 cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
4025 } else {
4026 send_file_data(conn, fp, 0, INT64_MAX);
4027 pclose(fp);
4028 }
4029}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ do_ssi_include()

static void do_ssi_include ( struct mg_connection conn,
const char ssi,
char tag,
int  include_level 
)
static

Definition at line 3972 of file mongoose4.cxx.

3973 {
3974 char file_name[MG_BUF_LEN], path[PATH_MAX], *p;
3975 FILE *fp;
3976
3977 // sscanf() is safe here, since send_ssi_file() also uses buffer
3978 // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
3979 if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
3980 // File name is relative to the webserver root
3981 (void) mg_snprintf(path, sizeof(path), "%s%c%s",
3982 conn->ctx->config[DOCUMENT_ROOT], '/', file_name);
3983 } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) {
3984 // File name is relative to the webserver working directory
3985 // or it is absolute system path
3986 (void) mg_snprintf(path, sizeof(path), "%s", file_name);
3987 } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 ||
3988 sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
3989 // File name is relative to the currect document
3990 (void) mg_snprintf(path, sizeof(path), "%s", ssi);
3991 if ((p = strrchr(path, '/')) != NULL) {
3992 p[1] = '\0';
3993 }
3994 (void) mg_snprintf(path + strlen(path),
3995 sizeof(path) - strlen(path), "%s", file_name);
3996 } else {
3997 cry(conn, "Bad SSI #include: [%s]", tag);
3998 return;
3999 }
4000
4001 if ((fp = mg_fopen(path, "rb")) == NULL) {
4002 cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
4003 tag, path, strerror(ERRNO));
4004 } else {
4007 strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) {
4008 send_ssi_file(conn, path, fp, include_level + 1);
4009 } else {
4010 send_file_data(conn, fp, 0, INT64_MAX);
4011 }
4012 fclose(fp);
4013 }
4014}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ fc()

static struct mg_connection * fc ( struct mg_context ctx)
static

Definition at line 525 of file mongoose4.cxx.

525 {
526 static struct mg_connection fake_connection;
528 // See https://github.com/cesanta/mongoose/issues/236
530 return &fake_connection;
531}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ fclose_on_exec()

static void fclose_on_exec ( FILE fp)
static

Definition at line 3242 of file mongoose4.cxx.

3242 {
3243 if (fp != NULL) {
3244#ifndef _WIN32
3246#endif
3247 }
3248}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ forward_body_data()

static int forward_body_data ( struct mg_connection conn,
FILE fp,
SOCKET  sock,
SSL ssl 
)
static

Definition at line 3481 of file mongoose4.cxx.

3482 {
3483 const char *expect, *body;
3484 char buf[MG_BUF_LEN];
3485 int nread, buffered_len, success = 0;
3486 int64_t left;
3487
3488 expect = mg_get_header(conn, "Expect");
3489 assert(fp != NULL);
3490
3491 if (conn->content_len == INT64_MAX) {
3492 send_http_error(conn, 411, "Length Required", "%s", "");
3493 } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
3494 send_http_error(conn, 417, "Expectation Failed", "%s", "");
3495 } else {
3496 if (expect != NULL) {
3497 (void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
3498 }
3499
3500 buffered_len = conn->data_len - conn->request_len;
3501 body = conn->buf + conn->request_len;
3502 assert(buffered_len >= 0);
3503
3504 if (buffered_len > 0) {
3505 if ((int64_t) buffered_len > conn->content_len) {
3506 buffered_len = (int) conn->content_len;
3507 }
3508 push(fp, sock, ssl, body, (int64_t) buffered_len);
3509 memmove((char *) body, body + buffered_len, buffered_len);
3510 conn->data_len -= buffered_len;
3511 }
3512
3513 nread = 0;
3514 while (conn->num_bytes_read < conn->content_len + conn->request_len) {
3515 left = left_to_read(conn);
3516 if (left > (int64_t) sizeof(buf)) {
3517 left = sizeof(buf);
3518 }
3519 nread = pull(NULL, conn, buf, (int) left);
3520 if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) {
3521 break;
3522 }
3523 }
3524
3525 if (left_to_read(conn) == 0) {
3526 success = nread >= 0;
3527 }
3528
3529 // Each error code path in this function must send an error
3530 if (!success) {
3531 send_http_error(conn, 577, http_500_error, "%s", "");
3532 }
3533 }
3534
3535 return success;
3536}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ free_context()

static void free_context ( struct mg_context ctx)
static

Definition at line 5428 of file mongoose4.cxx.

5428 {
5429 int i;
5430
5431 // Deallocate config parameters
5432 for (i = 0; i < NUM_OPTIONS; i++) {
5433 if (ctx->config[i] != NULL)
5434 free(ctx->config[i]);
5435 }
5436
5437#ifndef NO_SSL
5438 // Deallocate SSL context
5439 if (ctx->ssl_ctx != NULL) {
5440 SSL_CTX_free(ctx->ssl_ctx);
5441 }
5442 if (ssl_mutexes != NULL) {
5443 free(ssl_mutexes);
5444 ssl_mutexes = NULL;
5445 }
5446#endif // !NO_SSL
5447
5448 // Deallocate context itself
5449 free(ctx);
5450}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_first_ssl_listener_index()

static int get_first_ssl_listener_index ( const struct mg_context ctx)
static

Definition at line 4688 of file mongoose4.cxx.

4688 {
4689 int i, index = -1;
4690 for (i = 0; index == -1 && i < ctx->num_listening_sockets; i++) {
4691 index = ctx->listening_sockets[i].is_ssl ? i : -1;
4692 }
4693 return index;
4694}
Here is the caller graph for this function:

◆ get_header()

static const char * get_header ( const struct mg_request_info ri,
const char name 
)
static

Definition at line 682 of file mongoose4.cxx.

683 {
684 int i;
685
686 for (i = 0; i < ri->num_headers; i++)
687 if (!mg_strcasecmp(name, ri->http_headers[i].name))
688 return ri->http_headers[i].value;
689
690 return NULL;
691}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_mime_type()

static void get_mime_type ( struct mg_context ctx,
const char path,
struct vec vec 
)
static

Definition at line 2914 of file mongoose4.cxx.

2915 {
2916 struct vec ext_vec, mime_vec;
2917 const char *list, *ext;
2918 size_t path_len;
2919
2920 path_len = strlen(path);
2921
2922 // Scan user-defined mime types first, in case user wants to
2923 // override default mime types.
2925 while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
2926 // ext now points to the path suffix
2927 ext = path + path_len - ext_vec.len;
2928 if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
2929 *vec = mime_vec;
2930 return;
2931 }
2932 }
2933
2935 vec->len = strlen(vec->ptr);
2936}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_month_index()

static int get_month_index ( const char s)
static

Definition at line 783 of file mongoose4.cxx.

783 {
784 int i;
785
786 for (i = 0; i < (int) ARRAY_SIZE(month_names); i++)
787 if (!strcmp(s, month_names[i]))
788 return i;
789
790 return -1;
791}
static const char * month_names[]
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_option_index()

static int get_option_index ( const char name)
static

Definition at line 859 of file mongoose4.cxx.

859 {
860 int i;
861
862 for (i = 0; config_options[i * 2] != NULL; i++) {
863 if (strcmp(config_options[i * 2], name) == 0) {
864 return i;
865 }
866 }
867 return -1;
868}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_remote_ip()

static uint32_t get_remote_ip ( const struct mg_connection conn)
static

Definition at line 4539 of file mongoose4.cxx.

4539 {
4540 return ntohl(* (uint32_t *) &conn->client.rsa.sin.sin_addr);
4541}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_request_len()

static int get_request_len ( const char buf,
int  buf_len 
)
static

Definition at line 2796 of file mongoose4.cxx.

2796 {
2797 int i;
2798
2799 for (i = 0; i < buf_len; i++) {
2800 // Control characters are not allowed but >=128 is.
2801 // Abort scan as soon as one malformed character is found;
2802 // don't let subsequent \r\n\r\n win us over anyhow
2803 if (!isprint(* (const unsigned char *) &buf[i]) && buf[i] != '\r' &&
2804 buf[i] != '\n' && * (const unsigned char *) &buf[i] < 128) {
2805 return -1;
2806 } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
2807 return i + 2;
2808 } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
2809 buf[i + 2] == '\n') {
2810 return i + 3;
2811 }
2812 }
2813
2814 return 0;
2815}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ getreq()

static int getreq ( struct mg_connection conn,
char ebuf,
size_t  ebuf_len 
)
static

Definition at line 5113 of file mongoose4.cxx.

5113 {
5114 const char *cl;
5115
5116 ebuf[0] = '\0';
5118 conn->request_len = read_request(NULL, conn, conn->buf, conn->buf_size,
5119 &conn->data_len);
5120 assert(conn->request_len < 0 || conn->data_len >= conn->request_len);
5121
5122 if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
5123 snprintf(ebuf, ebuf_len, "%s", "Request Too Large");
5124 } else if (conn->request_len <= 0) {
5125 snprintf(ebuf, ebuf_len, "%s", "Client closed connection");
5126 } else if (parse_http_message(conn->buf, conn->buf_size,
5127 &conn->request_info) <= 0) {
5128 snprintf(ebuf, ebuf_len, "Bad request: [%.*s]", conn->data_len, conn->buf);
5129 } else {
5130 // Request is valid. Set content_len attribute by parsing Content-Length
5131 // If Content-Length is absent, set content_len to 0 if request is GET,
5132 // and set it to INT64_MAX otherwise. Setting to INT64_MAX instructs
5133 // mg_read() to read from the socket until socket is closed.
5134 // The reason for treating GET and POST/PUT differently is that libraries
5135 // like jquery do not set Content-Length in GET requests, and we don't
5136 // want mg_read() to hang waiting until socket is timed out.
5137 // See https://github.com/cesanta/mongoose/pull/121 for more.
5138 conn->content_len = INT64_MAX;
5139 if (!mg_strcasecmp(conn->request_info.request_method, "GET")) {
5140 conn->content_len = 0;
5141 }
5142 if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) {
5143 conn->content_len = strtoll(cl, NULL, 10);
5144 }
5145 conn->birth_time = time(NULL);
5146 }
5147 return ebuf[0] == '\0';
5148}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ gmt_time_string()

static void gmt_time_string ( char buf,
size_t  buf_len,
time_t t 
)
static

Definition at line 3232 of file mongoose4.cxx.

3232 {
3233 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
3234}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_cgi_request()

static void handle_cgi_request ( struct mg_connection conn,
const char prog 
)
static

Definition at line 3714 of file mongoose4.cxx.

3714 {
3715 int headers_len, data_len, i, fdin[2], fdout[2];
3716 const char *status, *status_text;
3717 char buf[16384], *pbuf, dir[PATH_MAX], *p;
3718 struct mg_request_info ri;
3719 struct cgi_env_block blk;
3720 FILE *in = NULL, *out = NULL;
3721 pid_t pid = (pid_t) -1;
3722
3724
3725 // CGI must be executed in its own directory. 'dir' must point to the
3726 // directory containing executable program, 'p' must point to the
3727 // executable program name relative to 'dir'.
3728 (void) mg_snprintf(dir, sizeof(dir), "%s", prog);
3729 if ((p = strrchr(dir, '/')) != NULL) {
3730 *p++ = '\0';
3731 } else {
3732 dir[0] = '.', dir[1] = '\0';
3733 p = (char *) prog;
3734 }
3735
3736 if (pipe(fdin) != 0 || pipe(fdout) != 0) {
3738 "Cannot create CGI pipe: %s", strerror(ERRNO));
3739 goto done;
3740 }
3741
3742 pid = spawn_process(conn, p, blk.buf, blk.vars, fdin[0], fdout[1], dir);
3743 if (pid == (pid_t) -1) {
3745 "Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO));
3746 goto done;
3747 }
3748
3749 // Make sure child closes all pipe descriptors. It must dup them to 0,1
3754
3755 // Parent closes only one side of the pipes.
3756 // If we don't mark them as closed, close() attempt before
3757 // return from this function throws an exception on Windows.
3758 // Windows does not like when closed descriptor is closed again.
3759 (void) close(fdin[0]);
3760 (void) close(fdout[1]);
3761 fdin[0] = fdout[1] = -1;
3762
3763
3764 if ((in = fdopen(fdin[1], "wb")) == NULL ||
3765 (out = fdopen(fdout[0], "rb")) == NULL) {
3767 "fopen: %s", strerror(ERRNO));
3768 goto done;
3769 }
3770
3771 setbuf(in, NULL);
3772 setbuf(out, NULL);
3773
3774 // Send POST data to the CGI process if needed
3775 if (!strcmp(conn->request_info.request_method, "POST") &&
3777 goto done;
3778 }
3779
3780 // Close so child gets an EOF.
3781 fclose(in);
3782 in = NULL;
3783 fdin[1] = -1;
3784
3785 // Now read CGI reply into a buffer. We need to set correct
3786 // status code, thus we need to see all HTTP headers first.
3787 // Do not send anything back to client, until we buffer in all
3788 // HTTP headers.
3789 data_len = 0;
3790 headers_len = read_request(out, conn, buf, sizeof(buf), &data_len);
3791 if (headers_len <= 0) {
3793 "CGI program sent malformed or too big (>%u bytes) "
3794 "HTTP headers: [%.*s]",
3795 (unsigned) sizeof(buf), data_len, buf);
3796 goto done;
3797 }
3798 pbuf = buf;
3799 buf[headers_len - 1] = '\0';
3801
3802 // Make up and send the status line
3803 status_text = "OK";
3804 if ((status = get_header(&ri, "Status")) != NULL) {
3807 while (isdigit(* (unsigned char *) status_text) || *status_text == ' ') {
3808 status_text++;
3809 }
3810 } else if (get_header(&ri, "Location") != NULL) {
3811 conn->status_code = 302;
3812 } else {
3813 conn->status_code = 200;
3814 }
3815 if (get_header(&ri, "Connection") != NULL &&
3816 !mg_strcasecmp(get_header(&ri, "Connection"), "keep-alive")) {
3817 conn->must_close = 1;
3818 }
3819 (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code,
3820 status_text);
3821
3822 // Send headers
3823 for (i = 0; i < ri.num_headers; i++) {
3824 mg_printf(conn, "%s: %s\r\n",
3825 ri.http_headers[i].name, ri.http_headers[i].value);
3826 }
3827 mg_write(conn, "\r\n", 2);
3828
3829 // Send chunk of data that may have been read after the headers
3831 (size_t)(data_len - headers_len));
3832
3833 // Read the rest of CGI output and send to the client
3835
3836done:
3837 if (pid != (pid_t) -1) {
3838 kill(pid, SIGKILL);
3839 }
3840 if (fdin[0] != -1) {
3841 close(fdin[0]);
3842 }
3843 if (fdout[1] != -1) {
3844 close(fdout[1]);
3845 }
3846
3847 if (in != NULL) {
3848 fclose(in);
3849 } else if (fdin[1] != -1) {
3850 close(fdin[1]);
3851 }
3852
3853 if (out != NULL) {
3854 fclose(out);
3855 } else if (fdout[0] != -1) {
3856 close(fdout[0]);
3857 }
3858}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_delete_request()

static void handle_delete_request ( struct mg_connection conn,
const char path 
)
static

Definition at line 4711 of file mongoose4.cxx.

4712 {
4714
4715 if (!mg_stat(path, &file)) {
4716 send_http_error(conn, 404, "Not Found", "%s", "File not found");
4717 } else if (!file.modification_time) {
4718 send_http_error(conn, 500, http_500_error, "remove(%s): %s", path,
4719 strerror(ERRNO));
4720 } else if (file.is_directory) {
4721 remove_directory(conn, path);
4722 send_http_error(conn, 204, "No Content", "%s", "");
4723 } else if (mg_remove(path) == 0) {
4724 send_http_error(conn, 204, "No Content", "%s", "");
4725 } else {
4726 send_http_error(conn, 423, "Locked", "remove(%s): %s", path,
4727 strerror(ERRNO));
4728 }
4729}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_directory_request()

static void handle_directory_request ( struct mg_connection conn,
const char dir 
)
static

Definition at line 3142 of file mongoose4.cxx.

3143 {
3144 int i, sort_direction;
3145 struct dir_scan_data data = { NULL, 0, 128 };
3146
3147 if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
3148 send_http_error(conn, 500, "Cannot open directory",
3149 "Error: opendir(%s): %s", dir, strerror(ERRNO));
3150 return;
3151 }
3152
3154 conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
3155
3156 conn->must_close = 1;
3157 mg_printf(conn, "%s",
3158 "HTTP/1.1 200 OK\r\n"
3159 "Transfer-Encoding: Chunked\r\n"
3160 "Content-Type: text/html; charset=utf-8\r\n\r\n");
3161
3162 conn->num_bytes_sent += mg_chunked_printf(conn,
3163 "<html><head><title>Index of %s</title>"
3164 "<style>th {text-align: left;}</style></head>"
3165 "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
3166 "<tr><th><a href=\"?n%c\">Name</a></th>"
3167 "<th><a href=\"?d%c\">Modified</a></th>"
3168 "<th><a href=\"?s%c\">Size</a></th></tr>"
3169 "<tr><td colspan=\"3\"><hr></td></tr>",
3170 conn->request_info.uri, conn->request_info.uri,
3172
3173 // Print first entry - link to a parent directory
3174 conn->num_bytes_sent += mg_chunked_printf(conn,
3175 "<tr><td><a href=\"%s%s\">%s</a></td>"
3176 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
3177 conn->request_info.uri, "..", "Parent directory", "-", "-");
3178
3179 // Sort and print directory entries
3180 qsort(data.entries, (size_t) data.num_entries, sizeof(data.entries[0]),
3182 for (i = 0; i < data.num_entries; i++) {
3183 print_dir_entry(&data.entries[i]);
3184 free(data.entries[i].file_name);
3185 }
3186 free(data.entries);
3187
3188 conn->num_bytes_sent += mg_chunked_printf(conn, "%s",
3189 "</table></body></html>");
3190 conn->num_bytes_sent += mg_write(conn, "0\r\n\r\n", 5);
3191 conn->status_code = 200;
3192}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_file_request()

static void handle_file_request ( struct mg_connection conn,
const char path,
struct file filep 
)
static

Definition at line 3250 of file mongoose4.cxx.

3251 {
3252 char date[64], lm[64], etag[64], range[64];
3253 const char *msg = "OK", *hdr;
3254 time_t curtime = time(NULL);
3255 int64_t cl, r1, r2;
3256 struct vec mime_vec;
3257 int n;
3258 char gz_path[PATH_MAX];
3259 char const* encoding = "";
3260 FILE *fp;
3261
3262 get_mime_type(conn->ctx, path, &mime_vec);
3263 cl = filep->size;
3264 conn->status_code = 200;
3265 range[0] = '\0';
3266
3267 // if this file is in fact a pre-gzipped file, rewrite its filename
3268 // it's important to rewrite the filename after resolving
3269 // the mime type from it, to preserve the actual file's type
3270 if (filep->gzipped) {
3271 snprintf(gz_path, sizeof(gz_path), "%s.gz", path);
3272 path = gz_path;
3273 encoding = "Content-Encoding: gzip\r\n";
3274 }
3275
3276 if ((fp = mg_fopen(path, "rb")) == NULL) {
3278 "fopen(%s): %s", path, strerror(ERRNO));
3279 return;
3280 }
3281
3283
3284 // If Range: header specified, act accordingly
3285 r1 = r2 = 0;
3286 hdr = mg_get_header(conn, "Range");
3287 if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
3288 r1 >= 0 && r2 >= 0) {
3289 // actually, range requests don't play well with a pre-gzipped
3290 // file (since the range is specified in the uncmpressed space)
3291 if (filep->gzipped) {
3292 send_http_error(conn, 501, "Not Implemented",
3293 "range requests in gzipped files are not supported");
3294 return;
3295 }
3296 conn->status_code = 206;
3297 cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1: cl - r1;
3298 mg_snprintf(range, sizeof(range),
3299 "Content-Range: bytes "
3300 "%" INT64_FMT "-%"
3301 INT64_FMT "/%" INT64_FMT "\r\n",
3302 r1, r1 + cl - 1, filep->size);
3303 msg = "Partial Content";
3304 }
3305
3306 // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
3307 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
3308 gmt_time_string(date, sizeof(date), &curtime);
3309 gmt_time_string(lm, sizeof(lm), &filep->modification_time);
3310 construct_etag(etag, sizeof(etag), filep);
3311
3312 (void) mg_printf(conn,
3313 "HTTP/1.1 %d %s\r\n"
3314 "Date: %s\r\n"
3315 "Last-Modified: %s\r\n"
3316 "Etag: %s\r\n"
3317 "Content-Type: %.*s\r\n"
3318 "Content-Length: %" INT64_FMT "\r\n"
3319 "Connection: %s\r\n"
3320 "Accept-Ranges: bytes\r\n"
3321 "%s%s%s\r\n",
3322 conn->status_code, msg, date, lm, etag, (int) mime_vec.len,
3323 mime_vec.ptr, cl, suggest_connection_header(conn), range, encoding,
3325
3326 if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
3327 send_file_data(conn, fp, r1, cl);
3328 }
3329 fclose(fp);
3330}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_options_request()

static void handle_options_request ( struct mg_connection conn)
static

Definition at line 4116 of file mongoose4.cxx.

4116 {
4117 static const char reply[] = "HTTP/1.1 200 OK\r\n"
4118 "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\n"
4119 "DAV: 1\r\n\r\n";
4120
4121 conn->status_code = 200;
4122 mg_write(conn, reply, sizeof(reply) - 1);
4123}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_propfind()

static void handle_propfind ( struct mg_connection conn,
const char path,
struct file filep 
)
static

Definition at line 4158 of file mongoose4.cxx.

4159 {
4160 const char *depth = mg_get_header(conn, "Depth");
4161
4162 conn->must_close = 1;
4163 conn->status_code = 207;
4164 mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
4165 "Connection: close\r\n"
4166 "Content-Type: text/xml; charset=utf-8\r\n\r\n");
4167
4168 conn->num_bytes_sent += mg_printf(conn,
4169 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
4170 "<d:multistatus xmlns:d='DAV:'>\n");
4171
4172 // Print properties for the requested resource itself
4173 print_props(conn, conn->request_info.uri, filep);
4174
4175 // If it is a directory, print directory entries too if Depth is not 0
4176 if (filep->is_directory &&
4178 (depth == NULL || strcmp(depth, "0") != 0)) {
4179 scan_directory(conn, path, conn, &print_dav_dir_entry);
4180 }
4181
4182 conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>");
4183}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_request()

static void handle_request ( struct mg_connection conn)
static

Definition at line 4735 of file mongoose4.cxx.

4735 {
4736 struct mg_request_info *ri = &conn->request_info;
4737 char path[PATH_MAX];
4738 int uri_len, ssl_index;
4740
4741 if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
4742 * ((char *) conn->request_info.query_string++) = '\0';
4743 }
4744 uri_len = (int) strlen(ri->uri);
4745 mg_url_decode(ri->uri, uri_len, (char *) ri->uri, uri_len + 1, 0);
4747 conn->throttle = set_throttle(conn->ctx->config[THROTTLE],
4748 get_remote_ip(conn), ri->uri);
4749 path[0] = '\0';
4750 convert_uri_to_file_name(conn, path, sizeof(path), &file);
4751
4752 // Perform redirect and auth checks before calling begin_request() handler.
4753 // Otherwise, begin_request() would need to perform auth checks and redirects.
4754 if (!conn->client.is_ssl && conn->client.ssl_redir &&
4755 (ssl_index = get_first_ssl_listener_index(conn->ctx)) > -1) {
4757 } else if (!strcmp(ri->request_method, "OPTIONS") && (call_user(MG_REQUEST_BEGIN, conn, (void *) ri->uri) == 1)) {
4758 // CORS "pre-flight" "OPTIONS" requests are sent without authentication, duh!
4759 } else if (!is_put_or_delete_request(conn) &&
4760 !check_authorization(conn, path)) {
4762 } else if (call_user(MG_REQUEST_BEGIN, conn, (void *) ri->uri) == 1) {
4763 // Do nothing, callback has served the request
4764 } else if (!strcmp(ri->request_method, "OPTIONS")) {
4766 } else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
4767 send_http_error(conn, 404, "Not Found", "Not Found");
4768 } else if (is_put_or_delete_request(conn) &&
4769 (is_authorized_for_put(conn) != 1)) {
4771 } else if (!strcmp(ri->request_method, "PUT")) {
4772 put_file(conn, path);
4773 } else if (!strcmp(ri->request_method, "MKCOL")) {
4774 mkcol(conn, path);
4775 } else if (!strcmp(ri->request_method, "DELETE")) {
4776 handle_delete_request(conn, path);
4777 } else if (file.modification_time == (time_t) 0 ||
4778 must_hide_file(conn, path)) {
4779 send_http_error(conn, 404, "Not Found", "%s", "File not found");
4780 } else if (file.is_directory && ri->uri[uri_len - 1] != '/') {
4781 mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
4782 "Location: %s/\r\n\r\n", ri->uri);
4783 } else if (!strcmp(ri->request_method, "PROPFIND")) {
4784 handle_propfind(conn, path, &file);
4785 } else if (file.is_directory &&
4786 !substitute_index_file(conn, path, sizeof(path), &file)) {
4787 if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) {
4788 handle_directory_request(conn, path);
4789 } else {
4790 send_http_error(conn, 403, "Directory Listing Denied",
4791 "Directory listing denied");
4792 }
4793#ifdef USE_LUA
4794 } else if (match_prefix("**.lp$", 6, path) > 0) {
4795 handle_lsp_request(conn, path, &file, NULL);
4796#endif
4797#if !defined(NO_CGI)
4798 } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
4800 path) > 0) {
4801 if (strcmp(ri->request_method, "POST") &&
4802 strcmp(ri->request_method, "HEAD") &&
4803 strcmp(ri->request_method, "GET")) {
4804 send_http_error(conn, 501, "Not Implemented",
4805 "Method %s is not implemented", ri->request_method);
4806 } else {
4807 handle_cgi_request(conn, path);
4808 }
4809#endif // !NO_CGI
4810 } else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
4812 path) > 0) {
4813 handle_ssi_file_request(conn, path);
4814 } else if (is_not_modified(conn, &file)) {
4815 send_http_error(conn, 304, "Not Modified", "%s", "");
4816 } else {
4817 handle_file_request(conn, path, &file);
4818 }
4819}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_ssi_file_request()

static void handle_ssi_file_request ( struct mg_connection conn,
const char path 
)
static

Definition at line 4095 of file mongoose4.cxx.

4096 {
4097 struct vec mime_vec;
4098 FILE *fp;
4099
4100 if ((fp = mg_fopen(path, "rb")) == NULL) {
4101 send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path,
4102 strerror(ERRNO));
4103 } else {
4104 conn->must_close = 1;
4106 get_mime_type(conn->ctx, path, &mime_vec);
4107 mg_printf(conn, "HTTP/1.1 200 OK\r\n"
4108 "Content-Type: %.*s\r\n"
4109 "Connection: close\r\n\r\n",
4110 (int) mime_vec.len, mime_vec.ptr);
4111 send_ssi_file(conn, path, fp, 0);
4112 fclose(fp);
4113 }
4114}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_authorized_for_put()

static int is_authorized_for_put ( struct mg_connection conn)
static

Definition at line 1301 of file mongoose4.cxx.

1301 {
1302 const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE];
1303 FILE *fp;
1304 int ret = 0;
1305
1306 if (passfile != NULL && (fp = mg_fopen(passfile, "r")) != NULL) {
1307 ret = authorize(conn, fp);
1308 fclose(fp);
1309 }
1310
1311 return ret;
1312}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_big_endian()

static int is_big_endian ( void  )
static

Definition at line 880 of file mongoose4.cxx.

880 {
881 static const int n = 1;
882 return ((char *) &n)[0] == 0;
883}
Here is the caller graph for this function:

◆ is_not_modified()

static int is_not_modified ( const struct mg_connection conn,
const struct file filep 
)
static

Definition at line 3471 of file mongoose4.cxx.

3472 {
3473 char etag[64];
3474 const char *ims = mg_get_header(conn, "If-Modified-Since");
3475 const char *inm = mg_get_header(conn, "If-None-Match");
3476 construct_etag(etag, sizeof(etag), filep);
3477 return (inm != NULL && !mg_strcasecmp(etag, inm)) ||
3478 (ims != NULL && filep->modification_time <= parse_date_string(ims));
3479}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_put_or_delete_request()

static int is_put_or_delete_request ( const struct mg_connection conn)
static

Definition at line 4681 of file mongoose4.cxx.

4681 {
4682 const char *s = conn->request_info.request_method;
4683 return s != NULL && (!strcmp(s, "PUT") ||
4684 !strcmp(s, "DELETE") ||
4685 !strcmp(s, "MKCOL"));
4686}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_valid_http_method()

static int is_valid_http_method ( const char method)
static

Definition at line 3356 of file mongoose4.cxx.

3356 {
3357 return !strcmp(method, "GET") || !strcmp(method, "POST") ||
3358 !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
3359 !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
3360 !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND")
3361 || !strcmp(method, "MKCOL")
3362 ;
3363}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_valid_port()

static int is_valid_port ( unsigned int  port)
static

Definition at line 4829 of file mongoose4.cxx.

4829 {
4830 return port > 0 && port < 0xffff;
4831}
Here is the caller graph for this function:

◆ is_valid_uri()

static int is_valid_uri ( const char uri)
static

Definition at line 5107 of file mongoose4.cxx.

5107 {
5108 // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
5109 // URI can be an asterisk (*) or should start with slash.
5110 return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
5111}
Here is the caller graph for this function:

◆ isbyte()

static int isbyte ( int  n)
static

Definition at line 4492 of file mongoose4.cxx.

4492 {
4493 return n >= 0 && n <= 255;
4494}
Here is the caller graph for this function:

◆ left_to_read()

static int64_t left_to_read ( const struct mg_connection conn)
static

Definition at line 2315 of file mongoose4.cxx.

2315 {
2316 return conn->content_len + conn->request_len - conn->num_bytes_read;
2317}
Here is the caller graph for this function:

◆ log_access()

static void log_access ( const struct mg_connection conn)
static

Definition at line 4943 of file mongoose4.cxx.

4943 {
4944 const struct mg_request_info *ri;
4945 FILE *fp;
4946 char date[64], src_addr[IP_ADDR_STR_LEN];
4947
4948 fp = conn->ctx->config[ACCESS_LOG_FILE] == NULL ? NULL :
4949 fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+");
4950
4951 if (fp == NULL)
4952 return;
4953
4954 strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
4955 localtime(&conn->birth_time));
4956
4957 ri = &conn->request_info;
4958 flockfile(fp);
4959
4960 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
4961 fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
4962 src_addr, ri->remote_user == NULL ? "-" : ri->remote_user, date,
4964 ri->uri ? ri->uri : "-", ri->http_version,
4965 conn->status_code, conn->num_bytes_sent);
4966 log_header(conn, "Referer", fp);
4967 log_header(conn, "User-Agent", fp);
4968 fputc('\n', fp);
4969 fflush(fp);
4970
4971 funlockfile(fp);
4972 fclose(fp);
4973}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ log_header()

static void log_header ( const struct mg_connection conn,
const char header,
FILE fp 
)
static

Definition at line 4932 of file mongoose4.cxx.

4933 {
4934 const char *header_value;
4935
4936 if ((header_value = mg_get_header(conn, header)) == NULL) {
4937 (void) fprintf(fp, "%s", " -");
4938 } else {
4939 (void) fprintf(fp, " \"%s\"", header_value);
4940 }
4941}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ lowercase()

static int lowercase ( const char s)
static

Definition at line 540 of file mongoose4.cxx.

540 {
541 return tolower(* (const unsigned char *) s);
542}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ master_thread()

static void * master_thread ( void thread_func_param)
static

Definition at line 5355 of file mongoose4.cxx.

5355 {
5356 struct mg_context *ctx = (struct mg_context *) thread_func_param;
5357 struct pollfd *pfd;
5358 int i;
5359
5360 // Increase priority of the master thread
5361#if defined(_WIN32)
5363#endif
5364
5365#if defined(ISSUE_317)
5366 struct sched_param sched_param;
5369#endif
5370
5372
5373 pfd = (struct pollfd *) calloc(ctx->num_listening_sockets, sizeof(pfd[0]));
5374 while (pfd != NULL && ctx->stop_flag == 0) {
5375 for (i = 0; i < ctx->num_listening_sockets; i++) {
5376 pfd[i].fd = ctx->listening_sockets[i].sock;
5377 pfd[i].events = POLLIN;
5378 }
5379
5380 if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
5381 for (i = 0; i < ctx->num_listening_sockets; i++) {
5382 // NOTE(lsm): on QNX, poll() returns POLLRDNORM after the
5383 // successfull poll, and POLLIN is defined as (POLLRDNORM | POLLRDBAND)
5384 // Therefore, we're checking pfd[i].revents & POLLIN, not
5385 // pfd[i].revents == POLLIN.
5386 if (ctx->stop_flag == 0 && (pfd[i].revents & POLLIN)) {
5388 }
5389 }
5390 }
5391 }
5392 free(pfd);
5393 DEBUG_TRACE(("stopping workers"));
5394
5395 // Stop signal received: somebody called mg_stop. Quit.
5397
5398 // Wakeup workers that are waiting for connections to handle.
5400
5401 // Wait until all threads finish
5402 (void) pthread_mutex_lock(&ctx->mutex);
5403 while (ctx->num_threads > 0) {
5404 (void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
5405 }
5407
5408 // All threads exited, no sync is needed. Destroy mutex and condvars
5413
5414#if !defined(NO_SSL)
5415 uninitialize_ssl(ctx);
5416#endif
5417 DEBUG_TRACE(("exiting"));
5418
5420
5421 // Signal mg_stop() that we're done.
5422 // WARNING: This must be the very last thing this
5423 // thread does, as ctx becomes invalid after this line.
5424 ctx->stop_flag = 2;
5425 return NULL;
5426}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ match_prefix()

static int match_prefix ( const char pattern,
int  pattern_len,
const char str 
)
static

Definition at line 737 of file mongoose4.cxx.

737 {
738 const char *or_str;
739 int i, j, len, res;
740
741 if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) {
742 res = match_prefix(pattern, or_str - pattern, str);
743 return res > 0 ? res :
744 match_prefix(or_str + 1, (pattern + pattern_len) - (or_str + 1), str);
745 }
746
747 i = j = 0;
748 res = -1;
749 for (; i < pattern_len; i++, j++) {
750 if (pattern[i] == '?' && str[j] != '\0') {
751 continue;
752 } else if (pattern[i] == '$') {
753 return str[j] == '\0' ? j : -1;
754 } else if (pattern[i] == '*') {
755 i++;
756 if (pattern[i] == '*') {
757 i++;
758 len = (int) strlen(str + j);
759 } else {
760 len = (int) strcspn(str + j, "/");
761 }
762 if (i == pattern_len) {
763 return j + len;
764 }
765 do {
766 res = match_prefix(pattern + i, pattern_len - i, str + j + len);
767 } while (res == -1 && len-- > 0);
768 return res == -1 ? -1 : j + res + len;
769 } else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
770 return -1;
771 }
772 }
773 return j;
774}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MD5Final()

static void MD5Final ( unsigned char  digest[16],
MD5_CTX ctx 
)
static

Definition at line 1044 of file mongoose4.cxx.

1044 {
1045 unsigned count;
1046 unsigned char *p;
1047 uint32_t *a;
1048
1049 count = (ctx->bits[0] >> 3) & 0x3F;
1050
1051 p = ctx->in + count;
1052 *p++ = 0x80;
1053 count = 64 - 1 - count;
1054 if (count < 8) {
1055 memset(p, 0, count);
1056 byteReverse(ctx->in, 16);
1057 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1058 memset(ctx->in, 0, 56);
1059 } else {
1060 memset(p, 0, count - 8);
1061 }
1062 byteReverse(ctx->in, 14);
1063
1064 a = (uint32_t *)ctx->in;
1065 a[14] = ctx->bits[0];
1066 a[15] = ctx->bits[1];
1067
1068 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1069 byteReverse((unsigned char *) ctx->buf, 4);
1070 memcpy(digest, ctx->buf, 16);
1071 memset((char *) ctx, 0, sizeof(*ctx));
1072}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MD5Init()

static void MD5Init ( MD5_CTX ctx)
static

Definition at line 916 of file mongoose4.cxx.

916 {
917 ctx->buf[0] = 0x67452301;
918 ctx->buf[1] = 0xefcdab89;
919 ctx->buf[2] = 0x98badcfe;
920 ctx->buf[3] = 0x10325476;
921
922 ctx->bits[0] = 0;
923 ctx->bits[1] = 0;
924}
Here is the caller graph for this function:

◆ MD5Transform()

static void MD5Transform ( uint32_t  buf[4],
uint32_t const  in[16] 
)
static

Definition at line 926 of file mongoose4.cxx.

926 {
927 register uint32_t a, b, c, d;
928
929 a = buf[0];
930 b = buf[1];
931 c = buf[2];
932 d = buf[3];
933
934 MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
935 MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
936 MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
937 MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
938 MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
939 MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
940 MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
941 MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
942 MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
943 MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
944 MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
945 MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
946 MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
947 MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
948 MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
949 MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
950
951 MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
952 MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
953 MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
954 MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
955 MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
956 MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
957 MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
958 MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
959 MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
960 MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
961 MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
962 MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
963 MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
964 MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
965 MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
966 MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
967
968 MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
969 MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
970 MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
971 MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
972 MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
973 MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
974 MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
975 MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
976 MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
977 MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
978 MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
979 MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
980 MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
981 MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
982 MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
983 MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
984
985 MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
986 MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
987 MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
988 MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
989 MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
990 MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
991 MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
992 MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
993 MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
994 MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
995 MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
996 MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
997 MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
998 MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
999 MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
1000 MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
1001
1002 buf[0] += a;
1003 buf[1] += b;
1004 buf[2] += c;
1005 buf[3] += d;
1006}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MD5Update()

static void MD5Update ( MD5_CTX ctx,
unsigned char const buf,
unsigned  len 
)
static

Definition at line 1008 of file mongoose4.cxx.

1008 {
1009 uint32_t t;
1010
1011 t = ctx->bits[0];
1012 if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
1013 ctx->bits[1]++;
1014 ctx->bits[1] += len >> 29;
1015
1016 t = (t >> 3) & 0x3f;
1017
1018 if (t) {
1019 unsigned char *p = (unsigned char *) ctx->in + t;
1020
1021 t = 64 - t;
1022 if (len < t) {
1023 memcpy(p, buf, len);
1024 return;
1025 }
1026 memcpy(p, buf, t);
1027 byteReverse(ctx->in, 16);
1028 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1029 buf += t;
1030 len -= t;
1031 }
1032
1033 while (len >= 64) {
1034 memcpy(ctx->in, buf, 64);
1035 byteReverse(ctx->in, 16);
1036 MD5Transform(ctx->buf, (uint32_t *) ctx->in);
1037 buf += 64;
1038 len -= 64;
1039 }
1040
1041 memcpy(ctx->in, buf, len);
1042}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mg_chunked_printf()

static int mg_chunked_printf ( struct mg_connection conn,
const char fmt,
  ... 
)
static

Definition at line 1928 of file mongoose4.cxx.

1928 {
1929 char mem[MG_BUF_LEN], *buf = mem;
1930 int len;
1931
1932 va_list ap;
1933 va_start(ap, fmt);
1934 if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
1935 len = mg_printf(conn, "%X\r\n%s\r\n", len, buf);
1936 }
1937
1938 if (buf != mem && buf != NULL) {
1939 free(buf);
1940 }
1941
1942 return len;
1943}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mg_connect()

struct mg_connection * mg_connect ( const char host,
int  port,
int  use_ssl,
char ebuf,
size_t  ebuf_len 
)

Definition at line 2252 of file mongoose4.cxx.

2253 {
2254 static struct mg_context fake_ctx;
2255 struct mg_connection *conn = NULL;
2256 SOCKET sock;
2257
2258 if ((sock = conn2(host, port, use_ssl, ebuf, ebuf_len)) == INVALID_SOCKET) {
2259 } else if ((conn = (struct mg_connection *)
2260 calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
2261 snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO));
2263#ifndef NO_SSL
2264 } else if (use_ssl && (conn->client_ssl_ctx =
2266 snprintf(ebuf, ebuf_len, "SSL_CTX_new error");
2268 free(conn);
2269 conn = NULL;
2270#endif // NO_SSL
2271 } else {
2272 socklen_t len = sizeof(struct sockaddr);
2273 conn->buf_size = MAX_REQUEST_SIZE;
2274 conn->buf = (char *) (conn + 1);
2275 conn->ctx = &fake_ctx;
2276 conn->client.sock = sock;
2277 getsockname(sock, &conn->client.rsa.sa, &len);
2278 conn->client.is_ssl = use_ssl;
2279#ifndef NO_SSL
2280 if (use_ssl) {
2281 // SSL_CTX_set_verify call is needed to switch off server certificate
2282 // checking, which is off by default in OpenSSL and on in yaSSL.
2284 sslize(conn, conn->client_ssl_ctx, SSL_connect);
2285 }
2286#endif
2287 }
2288
2289 return conn;
2290}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mg_download()

struct mg_connection * mg_download ( const char host,
int  port,
int  use_ssl,
char ebuf,
size_t  ebuf_len,
const char fmt,
  ... 
)

Definition at line 2292 of file mongoose4.cxx.

2294 {
2295 struct mg_connection *conn;
2296 va_list ap;
2297
2298 va_start(ap, fmt);
2299 ebuf[0] = '\0';
2300 if ((conn = mg_connect(host, port, use_ssl, ebuf, ebuf_len)) == NULL) {
2301 } else if (mg_vprintf(conn, fmt, ap) <= 0) {
2302 snprintf(ebuf, ebuf_len, "%s", "Error sending request");
2303 } else {
2304 getreq(conn, ebuf, ebuf_len);
2305 }
2306 if (ebuf[0] != '\0' && conn != NULL) {
2307 mg_close_connection(conn);
2308 conn = NULL;
2309 }
2310
2311 return conn;
2312}
Here is the call graph for this function:

◆ mg_fopen()

static FILE * mg_fopen ( const char path,
const char mode 
)
static

Definition at line 2331 of file mongoose4.cxx.

2331 {
2332#ifdef _WIN32
2333 wchar_t wbuf[PATH_MAX], wmode[20];
2334 to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
2336 return _wfopen(wbuf, wmode);
2337#else
2338 return fopen(path, mode);
2339#endif
2340}
Here is the caller graph for this function:

◆ mg_snprintf() [1/2]

static int static int mg_snprintf ( char buf,
size_t  buflen,
const char fmt,
  ... 
)
static

Definition at line 617 of file mongoose4.cxx.

617 {
618 va_list ap;
619 int n;
620
621 va_start(ap, fmt);
622 n = mg_vsnprintf(buf, buflen, fmt, ap);
623 va_end(ap);
624
625 return n;
626}
Here is the call graph for this function:

◆ mg_snprintf() [2/2]

static int mg_snprintf ( char buf,
size_t  buflen,
PRINTF_FORMAT_STRING(const char *fmt)  ,
  ... 
)
static
Here is the caller graph for this function:

◆ mg_stat()

static int mg_stat ( const char path,
struct file filep 
)
static

Definition at line 1778 of file mongoose4.cxx.

1778 {
1779 struct stat st;
1780
1781 filep->modification_time = (time_t) 0;
1782 if (stat(path, &st) == 0) {
1783 filep->size = st.st_size;
1784 filep->modification_time = st.st_mtime;
1785 filep->is_directory = S_ISDIR(st.st_mode);
1786
1787 // See https://github.com/cesanta/mongoose/issues/109
1788 // Some filesystems report modification time as 0. Artificially
1789 // bump it up to mark mg_stat() success.
1790 if (filep->modification_time == (time_t) 0) {
1791 filep->modification_time = (time_t) 1;
1792 }
1793 }
1794
1795 return filep->modification_time != (time_t) 0;
1796}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mg_strcasecmp()

static int mg_strcasecmp ( const char s1,
const char s2 
)
static

Definition at line 555 of file mongoose4.cxx.

555 {
556 int diff;
557
558 do {
559 diff = lowercase(s1++) - lowercase(s2++);
560 } while (diff == 0 && s1[-1] != '\0');
561
562 return diff;
563}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mg_strcasestr()

static const char * mg_strcasestr ( const char big_str,
const char small_str 
)
static

Definition at line 579 of file mongoose4.cxx.

579 {
581
582 for (i = 0; i <= big_len - small_len; i++) {
584 return big_str + i;
585 }
586 }
587
588 return NULL;
589}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mg_strdup()

static char * mg_strdup ( const char str)
static

Definition at line 575 of file mongoose4.cxx.

575 {
576 return mg_strndup(str, strlen(str));
577}
static char * mg_strndup(const char *ptr, size_t len)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mg_strlcpy()

static void mg_strlcpy ( register char dst,
register const char src,
size_t  n 
)
static

Definition at line 533 of file mongoose4.cxx.

533 {
534 for (; *src != '\0' && n > 1; n--) {
535 *dst++ = *src++;
536 }
537 *dst = '\0';
538}
Here is the caller graph for this function:

◆ mg_strncasecmp()

static int mg_strncasecmp ( const char s1,
const char s2,
size_t  len 
)
static

Definition at line 544 of file mongoose4.cxx.

544 {
545 int diff = 0;
546
547 if (len > 0)
548 do {
549 diff = lowercase(s1++) - lowercase(s2++);
550 } while (diff == 0 && s1[-1] != '\0' && --len > 0);
551
552 return diff;
553}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mg_strndup()

static char * mg_strndup ( const char ptr,
size_t  len 
)
static

Definition at line 565 of file mongoose4.cxx.

565 {
566 char *p;
567
568 if ((p = (char *) malloc(len + 1)) != NULL) {
569 mg_strlcpy(p, ptr, len + 1);
570 }
571
572 return p;
573}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mg_url_encode()

void mg_url_encode ( const char src,
char dst,
size_t  dst_len 
)

Definition at line 2938 of file mongoose4.cxx.

2938 {
2939 static const char *dont_escape = "._-$,;~()";
2940 static const char *hex = "0123456789abcdef";
2941 const char *end = dst + dst_len - 1;
2942
2943 for (; *src != '\0' && dst < end; src++, dst++) {
2944 if (isalnum(*(const unsigned char *) src) ||
2945 strchr(dont_escape, * (const unsigned char *) src) != NULL) {
2946 *dst = *src;
2947 } else if (dst + 2 < end) {
2948 dst[0] = '%';
2949 dst[1] = hex[(* (const unsigned char *) src) >> 4];
2950 dst[2] = hex[(* (const unsigned char *) src) & 0xf];
2951 dst += 2;
2952 }
2953 }
2954
2955 *dst = '\0';
2956}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mg_vsnprintf()

static int mg_vsnprintf ( char buf,
size_t  buflen,
const char fmt,
va_list  ap 
)
static

Definition at line 595 of file mongoose4.cxx.

595 {
596 int n;
597
598 if (buflen == 0) {
599 return 0;
600 }
601
602 n = vsnprintf(buf, buflen, fmt, ap);
603
604 if (n < 0) {
605 n = 0;
606 } else if (n >= (int) buflen) {
607 n = (int) buflen - 1;
608 }
609 buf[n] = '\0';
610
611 return n;
612}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mkcol()

static void mkcol ( struct mg_connection conn,
const char path 
)
static

Definition at line 3895 of file mongoose4.cxx.

3895 {
3896 int rc, body_len;
3897 struct de de;
3898
3899 memset(&de.file, 0, sizeof(de.file));
3900 mg_stat(path, &de.file);
3901
3903 send_http_error(conn, 405, "Method Not Allowed",
3904 "mkcol(%s): %s", path, strerror(ERRNO));
3905 return;
3906 }
3907
3908 body_len = conn->data_len - conn->request_len;
3909 if(body_len > 0) {
3910 send_http_error(conn, 415, "Unsupported media type",
3911 "mkcol(%s): %s", path, strerror(ERRNO));
3912 return;
3913 }
3914
3915 rc = mg_mkdir(path, 0755);
3916
3917 if (rc == 0) {
3918 conn->status_code = 201;
3919 mg_printf(conn, "HTTP/1.1 %d Created\r\n\r\n", conn->status_code);
3920 } else if (rc == -1) {
3921 if(errno == EEXIST)
3922 send_http_error(conn, 405, "Method Not Allowed",
3923 "mkcol(%s): %s", path, strerror(ERRNO));
3924 else if(errno == EACCES)
3925 send_http_error(conn, 403, "Forbidden",
3926 "mkcol(%s): %s", path, strerror(ERRNO));
3927 else if(errno == ENOENT)
3928 send_http_error(conn, 409, "Conflict",
3929 "mkcol(%s): %s", path, strerror(ERRNO));
3930 else
3932 "fopen(%s): %s", path, strerror(ERRNO));
3933 }
3934}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ must_hide_file()

static int must_hide_file ( struct mg_connection conn,
const char path 
)
static

Definition at line 3019 of file mongoose4.cxx.

3019 {
3020 const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
3021 const char *pattern = conn->ctx->config[HIDE_FILES];
3022 return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
3023 (pattern != NULL && match_prefix(pattern, strlen(pattern), path) > 0);
3024}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ next_option()

static const char * next_option ( const char list,
struct vec val,
struct vec eq_val 
)
static

Definition at line 703 of file mongoose4.cxx.

704 {
705 if (list == NULL || *list == '\0') {
706 // End of the list
707 list = NULL;
708 } else {
709 val->ptr = list;
710 if ((list = strchr(val->ptr, ',')) != NULL) {
711 // Comma found. Store length and shift the list ptr
712 val->len = list - val->ptr;
713 list++;
714 } else {
715 // This value is the last one
716 list = val->ptr + strlen(val->ptr);
717 val->len = list - val->ptr;
718 }
719
720 if (eq_val != NULL) {
721 // Value has form "x=y", adjust pointers and lengths
722 // so that val points to "x", and eq_val points to "y".
723 eq_val->len = 0;
724 eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len);
725 if (eq_val->ptr != NULL) {
726 eq_val->ptr++; // Skip over '=' character
727 eq_val->len = val->ptr + val->len - eq_val->ptr;
728 val->len = (eq_val->ptr - val->ptr) - 1;
729 }
730 }
731 }
732
733 return list;
734}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ num_leap_years()

static int num_leap_years ( int  year)
static

Definition at line 793 of file mongoose4.cxx.

793 {
794 return year / 4 - year / 100 + year / 400;
795}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ open_auth_file()

static FILE * open_auth_file ( struct mg_connection conn,
const char path 
)
static

Definition at line 1139 of file mongoose4.cxx.

1139 {
1140 char name[PATH_MAX];
1141 const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE];
1143 FILE *fp = NULL;
1144
1145 if (gpass != NULL) {
1146 // Use global passwords file
1147 fp = mg_fopen(gpass, "r");
1148 // Important: using local struct file to test path for is_directory flag.
1149 // If filep is used, mg_stat() makes it appear as if auth file was opened.
1150 } else if (mg_stat(path, &file) && file.is_directory) {
1151 mg_snprintf(name, sizeof(name), "%s%c%s",
1152 path, '/', PASSWORDS_FILE_NAME);
1153 fp = mg_fopen(name, "r");
1154 } else {
1155 // Try to find .htpasswd in requested directory.
1156 for (p = path, e = p + strlen(p) - 1; e > p; e--)
1157 if (e[0] == '/')
1158 break;
1159 mg_snprintf(name, sizeof(name), "%.*s%c%s",
1160 (int) (e - p), p, '/', PASSWORDS_FILE_NAME);
1161 fp = mg_fopen(name, "r");
1162 }
1163
1164 return fp;
1165}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_auth_header()

static int parse_auth_header ( struct mg_connection conn,
char buf,
size_t  buf_size,
struct ah ah 
)
static

Definition at line 1173 of file mongoose4.cxx.

1174 {
1175 char *name, *value, *s;
1176 const char *auth_header;
1177
1178 (void) memset(ah, 0, sizeof(*ah));
1179 if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
1180 mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
1181 return 0;
1182 }
1183
1184 // Make modifiable copy of the auth header
1185 (void) mg_strlcpy(buf, auth_header + 7, buf_size);
1186 s = buf;
1187
1188 // Parse authorization header
1189 for (;;) {
1190 // Gobble initial spaces
1191 while (isspace(* (unsigned char *) s)) {
1192 s++;
1193 }
1194 name = skip_quoted(&s, "=", " ", 0);
1195 // Value is either quote-delimited, or ends at first comma or space.
1196 if (s[0] == '\"') {
1197 s++;
1198 value = skip_quoted(&s, "\"", " ", '\\');
1199 if (s[0] == ',') {
1200 s++;
1201 }
1202 } else {
1203 value = skip_quoted(&s, ", ", " ", 0); // IE uses commas, FF uses spaces
1204 }
1205 if (*name == '\0') {
1206 break;
1207 }
1208
1209 if (!strcmp(name, "username")) {
1210 ah->user = value;
1211 } else if (!strcmp(name, "cnonce")) {
1212 ah->cnonce = value;
1213 } else if (!strcmp(name, "response")) {
1214 ah->response = value;
1215 } else if (!strcmp(name, "uri")) {
1216 ah->uri = value;
1217 } else if (!strcmp(name, "qop")) {
1218 ah->qop = value;
1219 } else if (!strcmp(name, "nc")) {
1220 ah->nc = value;
1221 } else if (!strcmp(name, "nonce")) {
1222 ah->nonce = value;
1223 }
1224 }
1225
1226 // CGI needs it as REMOTE_USER
1227 if (ah->user != NULL) {
1229 } else {
1230 return 0;
1231 }
1232
1233 return 1;
1234}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_date_string()

static time_t parse_date_string ( const char datetime)
static

Definition at line 798 of file mongoose4.cxx.

798 {
799 static const unsigned short days_before_month[] = {
800 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
801 };
802 char month_str[32];
804 time_t result = (time_t) 0;
805
806 if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
807 &day, month_str, &year, &hour, &minute, &second) == 6) ||
808 (sscanf(datetime, "%d %3s %d %d:%d:%d",
809 &day, month_str, &year, &hour, &minute, &second) == 6) ||
810 (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
811 &day, month_str, &year, &hour, &minute, &second) == 6) ||
812 (sscanf(datetime, "%d-%3s-%d %d:%d:%d",
813 &day, month_str, &year, &hour, &minute, &second) == 6)) &&
814 year > 1970 &&
815 (month = get_month_index(month_str)) != -1) {
817 year -= 1970;
818 days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
819 result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
820 }
821
822 return result;
823}
static int num_leap_years(int year)
static int get_month_index(const char *s)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_http_headers()

static void parse_http_headers ( char **  buf,
struct mg_request_info ri 
)
static

Definition at line 3344 of file mongoose4.cxx.

3344 {
3345 int i;
3346
3347 for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
3348 ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
3349 ri->http_headers[i].value = skip(buf, "\r\n");
3350 if (ri->http_headers[i].name[0] == '\0')
3351 break;
3352 ri->num_headers = i + 1;
3353 }
3354}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_http_message()

static int parse_http_message ( char buf,
int  len,
struct mg_request_info ri 
)
static

Definition at line 3368 of file mongoose4.cxx.

3368 {
3369 int is_request, request_length = get_request_len(buf, len);
3370 if (request_length > 0) {
3371 // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
3372 ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL;
3373 ri->num_headers = 0;
3374
3375 buf[request_length - 1] = '\0';
3376
3377 // RFC says that all initial whitespaces should be ingored
3378 while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
3379 buf++;
3380 }
3381 ri->request_method = skip(&buf, " ");
3382 ri->uri = skip(&buf, " ");
3383 ri->http_version = skip(&buf, "\r\n");
3384
3385 // HTTP message could be either HTTP request or HTTP response, e.g.
3386 // "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..."
3387 is_request = is_valid_http_method(ri->request_method);
3388 if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) ||
3389 (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) {
3390 request_length = -1;
3391 } else {
3392 if (is_request) {
3393 ri->http_version += 5;
3394 }
3395 parse_http_headers(&buf, ri);
3396 }
3397 }
3398 return request_length;
3399}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_net()

static int parse_net ( const char spec,
uint32_t net,
uint32_t mask 
)
static

Definition at line 4496 of file mongoose4.cxx.

4496 {
4497 int n, a, b, c, d, slash = 32, len = 0;
4498
4499 if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
4500 sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
4501 isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) &&
4502 slash >= 0 && slash < 33) {
4503 len = n;
4504 *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d;
4505 *mask = slash ? 0xffffffffU << (32 - slash) : 0;
4506 }
4507
4508 return len;
4509}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_port_string()

static int parse_port_string ( const struct vec vec,
struct socket so 
)
static

Definition at line 4836 of file mongoose4.cxx.

4836 {
4837 unsigned int a, b, c, d, ch, port;
4838 int len;
4839#if defined(USE_IPV6)
4840 char buf[100];
4841#endif
4842
4843 // MacOS needs that. If we do not zero it, subsequent bind() will fail.
4844 // Also, all-zeroes in the socket address means binding to all addresses
4845 // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
4846 memset(so, 0, sizeof(*so));
4847 so->lsa.sin.sin_family = AF_INET;
4848
4849 if (sscanf(vec->ptr, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
4850 // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
4851 so->lsa.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
4852 so->lsa.sin.sin_port = htons((uint16_t) port);
4853#if defined(USE_IPV6)
4854
4855 } else if (sscanf(vec->ptr, "[%49[^]]]:%d%n", buf, &port, &len) == 2 &&
4856 inet_pton(AF_INET6, buf, &so->lsa.sin6.sin6_addr)) {
4857 // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
4858 so->lsa.sin6.sin6_family = AF_INET6;
4859 so->lsa.sin6.sin6_port = htons((uint16_t) port);
4860#endif
4861 } else if (sscanf(vec->ptr, "%u%n", &port, &len) == 1) {
4862 // If only port is specified, bind to IPv4, INADDR_ANY
4863 so->lsa.sin.sin_port = htons((uint16_t) port);
4864 } else {
4865 port = len = 0; // Parsing failure. Make port invalid.
4866 }
4867
4868 ch = vec->ptr[len]; // Next character after the port number
4869 so->is_ssl = ch == 's';
4870 so->ssl_redir = ch == 'r';
4871
4872 // Make sure the port is valid and vector ends with 's', 'r' or ','
4873 return is_valid_port(port) &&
4874 (ch == '\0' || ch == 's' || ch == 'r' || ch == ',');
4875}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_range_header()

static int parse_range_header ( const char header,
int64_t a,
int64_t b 
)
static

Definition at line 3228 of file mongoose4.cxx.

3228 {
3229 return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
3230}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ prepare_cgi_environment()

static void prepare_cgi_environment ( struct mg_connection conn,
const char prog,
struct cgi_env_block blk 
)
static

Definition at line 3592 of file mongoose4.cxx.

3594 {
3595 const struct mg_request_info *ri = &conn->request_info;
3596 const char *s, *slash;
3597 struct vec var_vec;
3598 char *p, src_addr[IP_ADDR_STR_LEN];
3599 int i;
3600
3601 blk->len = blk->nvars = 0;
3602 blk->conn = conn;
3603 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
3604
3605 addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]);
3606 addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
3607 addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
3608 addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", mg_version());
3609
3610 // Prepare the environment block
3611 addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
3612 addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
3613 addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
3614
3615 // TODO(lsm): fix this for IPv6 case
3616 addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
3617
3618 addenv(blk, "REQUEST_METHOD=%s", ri->request_method);
3619 addenv(blk, "REMOTE_ADDR=%s", src_addr);
3620 addenv(blk, "REMOTE_PORT=%d", ri->remote_port);
3621 addenv(blk, "REQUEST_URI=%s%s%s", ri->uri,
3622 ri->query_string == NULL ? "" : "?",
3623 ri->query_string == NULL ? "" : ri->query_string);
3624
3625 // SCRIPT_NAME
3626 if (conn->path_info != NULL) {
3627 addenv(blk, "SCRIPT_NAME=%.*s",
3628 (int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri);
3629 addenv(blk, "PATH_INFO=%s", conn->path_info);
3630 } else {
3631 s = strrchr(prog, '/');
3632 slash = strrchr(ri->uri, '/');
3633 addenv(blk, "SCRIPT_NAME=%.*s%s",
3634 slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri,
3635 s == NULL ? prog : s);
3636 }
3637
3638 addenv(blk, "SCRIPT_FILENAME=%s", prog);
3639 addenv(blk, "PATH_TRANSLATED=%s", prog);
3640 addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
3641
3642 if ((s = mg_get_header(conn, "Content-Type")) != NULL)
3643 addenv(blk, "CONTENT_TYPE=%s", s);
3644
3645 if (ri->query_string != NULL) {
3646 addenv(blk, "QUERY_STRING=%s", ri->query_string);
3647 }
3648
3649 if ((s = mg_get_header(conn, "Content-Length")) != NULL)
3650 addenv(blk, "CONTENT_LENGTH=%s", s);
3651
3652 if ((s = getenv("PATH")) != NULL)
3653 addenv(blk, "PATH=%s", s);
3654
3655#if defined(_WIN32)
3656 if ((s = getenv("COMSPEC")) != NULL) {
3657 addenv(blk, "COMSPEC=%s", s);
3658 }
3659 if ((s = getenv("SYSTEMROOT")) != NULL) {
3660 addenv(blk, "SYSTEMROOT=%s", s);
3661 }
3662 if ((s = getenv("SystemDrive")) != NULL) {
3663 addenv(blk, "SystemDrive=%s", s);
3664 }
3665 if ((s = getenv("ProgramFiles")) != NULL) {
3666 addenv(blk, "ProgramFiles=%s", s);
3667 }
3668 if ((s = getenv("ProgramFiles(x86)")) != NULL) {
3669 addenv(blk, "ProgramFiles(x86)=%s", s);
3670 }
3671 if ((s = getenv("CommonProgramFiles(x86)")) != NULL) {
3672 addenv(blk, "CommonProgramFiles(x86)=%s", s);
3673 }
3674#else
3675 if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
3676 addenv(blk, "LD_LIBRARY_PATH=%s", s);
3677#endif // _WIN32
3678
3679 if ((s = getenv("PERLLIB")) != NULL)
3680 addenv(blk, "PERLLIB=%s", s);
3681
3682 if (ri->remote_user != NULL) {
3683 addenv(blk, "REMOTE_USER=%s", ri->remote_user);
3684 addenv(blk, "%s", "AUTH_TYPE=Digest");
3685 }
3686
3687 // Add all headers as HTTP_* variables
3688 for (i = 0; i < ri->num_headers; i++) {
3689 p = addenv(blk, "HTTP_%s=%s",
3690 ri->http_headers[i].name, ri->http_headers[i].value);
3691
3692 // Convert variable name into uppercase, and change - to _
3693 for (; *p != '=' && *p != '\0'; p++) {
3694 if (*p == '-')
3695 *p = '_';
3696 *p = (char) toupper(* (unsigned char *) p);
3697 }
3698 }
3699
3700 // Add user-specified variables
3701 s = conn->ctx->config[CGI_ENVIRONMENT];
3702 while ((s = next_option(s, &var_vec, NULL)) != NULL) {
3703 addenv(blk, "%.*s", (int) var_vec.len, var_vec.ptr);
3704 }
3705
3706 blk->vars[blk->nvars++] = NULL;
3707 blk->buf[blk->len++] = '\0';
3708
3709 assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
3710 assert(blk->len > 0);
3711 assert(blk->len < (int) sizeof(blk->buf));
3712}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ print_dav_dir_entry()

static void print_dav_dir_entry ( struct de de,
void data 
)
static

Definition at line 4148 of file mongoose4.cxx.

4148 {
4149 char href[PATH_MAX];
4150 char href_encoded[PATH_MAX];
4151 struct mg_connection *conn = (struct mg_connection *) data;
4152 mg_snprintf(href, sizeof(href), "%s%s",
4153 conn->request_info.uri, de->file_name);
4155 print_props(conn, href_encoded, &de->file);
4156}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ print_dir_entry()

static void print_dir_entry ( const struct de de)
static

Definition at line 2958 of file mongoose4.cxx.

2958 {
2959 char size[64], mod[64], href[PATH_MAX * 3];
2960 const char *slash = de->file.is_directory ? "/" : "";
2961
2962 if (de->file.is_directory) {
2963 mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]");
2964 } else {
2965 // We use (signed) cast below because MSVC 6 compiler cannot
2966 // convert unsigned __int64 to double. Sigh.
2967 if (de->file.size < 1024) {
2968 mg_snprintf(size, sizeof(size), "%d", (int) de->file.size);
2969 } else if (de->file.size < 0x100000) {
2970 mg_snprintf(size, sizeof(size),
2971 "%.1fk", (double) de->file.size / 1024.0);
2972 } else if (de->file.size < 0x40000000) {
2973 mg_snprintf(size, sizeof(size),
2974 "%.1fM", (double) de->file.size / 1048576);
2975 } else {
2976 mg_snprintf(size, sizeof(size),
2977 "%.1fG", (double) de->file.size / 1073741824);
2978 }
2979 }
2980 strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
2982 mg_url_encode(de->file_name, href, sizeof(href));
2984 "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
2985 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
2987}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ print_props()

static void print_props ( struct mg_connection conn,
const char uri,
struct file filep 
)
static

Definition at line 4126 of file mongoose4.cxx.

4127 {
4128 char mtime[64];
4129 gmt_time_string(mtime, sizeof(mtime), &filep->modification_time);
4130 conn->num_bytes_sent += mg_printf(conn,
4131 "<d:response>"
4132 "<d:href>%s</d:href>"
4133 "<d:propstat>"
4134 "<d:prop>"
4135 "<d:resourcetype>%s</d:resourcetype>"
4136 "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
4137 "<d:getlastmodified>%s</d:getlastmodified>"
4138 "</d:prop>"
4139 "<d:status>HTTP/1.1 200 OK</d:status>"
4140 "</d:propstat>"
4141 "</d:response>\n",
4142 uri,
4143 filep->is_directory ? "<d:collection/>" : "",
4144 filep->size,
4145 mtime);
4146}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ process_new_connection()

static void process_new_connection ( struct mg_connection conn)
static

Definition at line 5150 of file mongoose4.cxx.

5150 {
5151 struct mg_request_info *ri = &conn->request_info;
5152 int keep_alive_enabled, keep_alive, discard_len;
5153 char ebuf[100];
5154
5156 keep_alive = 0;
5157
5158 // Important: on new connection, reset the receiving buffer. Credit goes
5159 // to crule42.
5160 conn->data_len = 0;
5161 do {
5162 if (!getreq(conn, ebuf, sizeof(ebuf))) {
5163 send_http_error(conn, 500, "Server Error", "%s", ebuf);
5164 conn->must_close = 1;
5165 } else if (!is_valid_uri(conn->request_info.uri)) {
5166 snprintf(ebuf, sizeof(ebuf), "Invalid URI: [%s]", ri->uri);
5167 send_http_error(conn, 400, "Bad Request", "%s", ebuf);
5168 } else if (strcmp(ri->http_version, "1.0") &&
5169 strcmp(ri->http_version, "1.1")) {
5170 snprintf(ebuf, sizeof(ebuf), "Bad HTTP version: [%s]", ri->http_version);
5171 send_http_error(conn, 505, "Bad HTTP version", "%s", ebuf);
5172 }
5173
5174 if (ebuf[0] == '\0') {
5175 handle_request(conn);
5176 call_user(MG_REQUEST_END, conn, (void *) (long) conn->status_code);
5177 log_access(conn);
5178 }
5179 if (ri->remote_user != NULL) {
5180 free((void *) ri->remote_user);
5181 // Important! When having connections with and without auth
5182 // would cause double free and then crash
5183 ri->remote_user = NULL;
5184 }
5185
5186 // NOTE(lsm): order is important here. should_keep_alive() call
5187 // is using parsed request, which will be invalid after memmove's below.
5188 // Therefore, memorize should_keep_alive() result now for later use
5189 // in loop exit condition.
5190 keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled &&
5191 conn->content_len >= 0 && should_keep_alive(conn);
5192
5193 // Discard all buffered data for this request
5194 discard_len = conn->content_len >= 0 && conn->request_len > 0 &&
5195 conn->request_len + conn->content_len < (int64_t) conn->data_len ?
5196 (int) (conn->request_len + conn->content_len) : conn->data_len;
5197 assert(discard_len >= 0);
5198 memmove(conn->buf, conn->buf + discard_len, conn->data_len - discard_len);
5199 conn->data_len -= discard_len;
5200 assert(conn->data_len >= 0);
5201 assert(conn->data_len <= conn->buf_size);
5202 } while (keep_alive);
5203}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ produce_socket()

static void produce_socket ( struct mg_context ctx,
const struct socket sp 
)
static

Definition at line 5291 of file mongoose4.cxx.

5291 {
5292 (void) pthread_mutex_lock(&ctx->mutex);
5293
5294 // If the queue is full, wait
5295 while (ctx->stop_flag == 0 &&
5296 ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
5297 (void) pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);
5298 }
5299
5300 if (ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue)) {
5301 // Copy socket to the queue and increment head
5302 ctx->queue[ctx->sq_head % ARRAY_SIZE(ctx->queue)] = *sp;
5303 ctx->sq_head++;
5304 DEBUG_TRACE(("queued socket %d", sp->sock));
5305 }
5306
5309}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ pull()

static int pull ( FILE fp,
struct mg_connection conn,
char buf,
int  len 
)
static

Definition at line 2487 of file mongoose4.cxx.

2487 {
2488 int nread;
2489
2490 if (len <= 0) return 0;
2491 if (fp != NULL) {
2492 // Use read() instead of fread(), because if we're reading from the CGI
2493 // pipe, fread() may block until IO buffer is filled up. We cannot afford
2494 // to block and must pass all read bytes immediately to the client.
2495 nread = read(fileno(fp), buf, (size_t) len);
2496#ifndef NO_SSL
2497 } else if (conn->ssl != NULL) {
2498 nread = SSL_read(conn->ssl, buf, len);
2499#endif
2500 } else {
2501 nread = recv(conn->client.sock, buf, (size_t) len, 0);
2502 }
2503 if (nread > 0) {
2504 conn->num_bytes_read += nread;
2505 }
2506
2507 return conn->ctx->stop_flag ? -1 : nread;
2508}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ pull_all()

static int pull_all ( FILE fp,
struct mg_connection conn,
char buf,
int  len 
)
static

Definition at line 2510 of file mongoose4.cxx.

2510 {
2511 int n, nread = 0;
2512
2513 while (len > 0 && conn->ctx->stop_flag == 0) {
2514 n = pull(fp, conn, buf + nread, len);
2515 if (n < 0) {
2516 nread = n; // Propagate the error
2517 break;
2518 } else if (n == 0) {
2519 break; // No more data to read
2520 } else {
2521 nread += n;
2522 len -= n;
2523 }
2524 }
2525
2526 return nread;
2527}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ push()

static int64_t push ( FILE fp,
SOCKET  sock,
SSL ssl,
const char buf,
int64_t  len 
)
static

Definition at line 2451 of file mongoose4.cxx.

2452 {
2453 int64_t sent;
2454 int n, k;
2455
2456 (void) ssl; // Get rid of warning
2457 sent = 0;
2458 while (sent < len) {
2459
2460 // How many bytes we send in this iteration
2461 k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
2462
2463#if !defined(NO_SSL)
2464 if (ssl != NULL) {
2465 n = SSL_write(ssl, buf + sent, k);
2466 } else
2467#endif
2468 if (fp != NULL) {
2469 n = (int) fwrite(buf + sent, 1, (size_t) k, fp);
2470 if (ferror(fp))
2471 n = -1;
2472 } else {
2473 n = send(sock, buf + sent, (size_t) k, MSG_NOSIGNAL);
2474 }
2475
2476 if (n <= 0)
2477 break;
2478
2479 sent += n;
2480 }
2481
2482 return sent;
2483}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ put_dir()

static int put_dir ( const char path)
static

Definition at line 3864 of file mongoose4.cxx.

3864 {
3865 char buf[PATH_MAX];
3866 const char *s, *p;
3868 int len, res = 1;
3869
3870 for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
3871 len = p - path;
3872 if (len >= (int) sizeof(buf)) {
3873 res = -1;
3874 break;
3875 }
3876 memcpy(buf, path, len);
3877 buf[len] = '\0';
3878
3879 // Try to create intermediate directory
3880 DEBUG_TRACE(("mkdir(%s)", buf));
3881 if (!mg_stat(buf, &file) && mg_mkdir(buf, 0755) != 0) {
3882 res = -1;
3883 break;
3884 }
3885
3886 // Is path itself a directory?
3887 if (p[1] == '\0') {
3888 res = 0;
3889 }
3890 }
3891
3892 return res;
3893}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ put_file()

static void put_file ( struct mg_connection conn,
const char path 
)
static

Definition at line 3936 of file mongoose4.cxx.

3936 {
3938 FILE *fp;
3939 const char *range;
3940 int64_t r1, r2;
3941 int rc;
3942
3943 conn->status_code = mg_stat(path, &file) ? 200 : 201;
3944
3945 if ((rc = put_dir(path)) == 0) {
3946 mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn->status_code);
3947 } else if (rc == -1) {
3949 "put_dir(%s): %s", path, strerror(ERRNO));
3950 } else if ((fp = mg_fopen(path, "wb+")) == NULL) {
3951 fclose(fp);
3953 "fopen(%s): %s", path, strerror(ERRNO));
3954 } else {
3956 range = mg_get_header(conn, "Content-Range");
3957 r1 = r2 = 0;
3958 if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
3959 conn->status_code = 206;
3960 fseeko(fp, r1, SEEK_SET);
3961 }
3962 if (!forward_body_data(conn, fp, INVALID_SOCKET, NULL)) {
3963 conn->status_code = 500;
3964 }
3965 mg_printf(conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n",
3966 conn->status_code);
3967 fclose(fp);
3968 }
3969}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ read_request()

static int read_request ( FILE fp,
struct mg_connection conn,
char buf,
int  bufsiz,
int nread 
)
static

Definition at line 3406 of file mongoose4.cxx.

3407 {
3408 int request_len, n = 0;
3409
3410 request_len = get_request_len(buf, *nread);
3411 while (conn->ctx->stop_flag == 0 &&
3412 *nread < bufsiz &&
3413 request_len == 0 &&
3414 (n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
3415 *nread += n;
3416 assert(*nread <= bufsiz);
3417 request_len = get_request_len(buf, *nread);
3418 }
3419
3420 return request_len <= 0 && n <= 0 ? -1 : request_len;
3421}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ realloc2()

static void * realloc2 ( void ptr,
size_t  size 
)
static

Definition at line 3115 of file mongoose4.cxx.

3115 {
3116 void *new_ptr = realloc(ptr, size);
3117 if (new_ptr == NULL) {
3118 free(ptr);
3119 }
3120 return new_ptr;
3121}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ redirect_to_https_port()

static void redirect_to_https_port ( struct mg_connection conn,
int  ssl_index 
)
static

Definition at line 4696 of file mongoose4.cxx.

4696 {
4697 char host[1025];
4698 const char *host_header;
4699
4700 if ((host_header = mg_get_header(conn, "Host")) == NULL ||
4701 sscanf(host_header, "%1024[^:]", host) == 0) {
4702 // Cannot get host from the Host: header. Fallback to our IP address.
4703 sockaddr_to_string(host, sizeof(host), &conn->client.lsa);
4704 }
4705
4706 mg_printf(conn, "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s\r\n\r\n",
4707 host, (int) ntohs(conn->ctx->listening_sockets[ssl_index].
4708 lsa.sin.sin_port), conn->request_info.uri);
4709}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ remove_directory()

static int remove_directory ( struct mg_connection conn,
const char dir 
)
static

Definition at line 3064 of file mongoose4.cxx.

3064 {
3065 char path[PATH_MAX];
3066 struct dirent *dp;
3067 DIR *dirp;
3068 struct de de;
3069
3070 if ((dirp = opendir(dir)) == NULL) {
3071 return 0;
3072 } else {
3073 de.conn = conn;
3074
3075 while ((dp = readdir(dirp)) != NULL) {
3076 // Do not show current dir, but show hidden files
3077 if (!strcmp(dp->d_name, ".") ||
3078 !strcmp(dp->d_name, "..")) {
3079 continue;
3080 }
3081
3082 mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
3083
3084 // If we don't memset stat structure to zero, mtime will have
3085 // garbage and strftime() will segfault later on in
3086 // print_dir_entry(). memset is required only if mg_stat()
3087 // fails. For more details, see
3088 // http://code.google.com/p/mongoose/issues/detail?id=79
3089 memset(&de.file, 0, sizeof(de.file));
3090 mg_stat(path, &de.file);
3092 if(de.file.is_directory) {
3093 remove_directory(conn, path);
3094 } else {
3095 mg_remove(path);
3096 }
3097 }
3098
3099 }
3100 (void) closedir(dirp);
3101
3102 rmdir(dir);
3103 }
3104
3105 return 1;
3106}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ remove_double_dots_and_double_slashes()

static void remove_double_dots_and_double_slashes ( char s)
static

Definition at line 2819 of file mongoose4.cxx.

2819 {
2820 char *p = s;
2821
2822 while (*s != '\0') {
2823 *p++ = *s++;
2824 if (s[-1] == '/' || s[-1] == '\\') {
2825 // Skip all following slashes, backslashes and double-dots
2826 while (s[0] != '\0') {
2827 if (s[0] == '/' || s[0] == '\\') {
2828 s++;
2829 } else if (s[0] == '.' && s[1] == '.') {
2830 s += 2;
2831 } else {
2832 break;
2833 }
2834 }
2835 }
2836 }
2837 *p = '\0';
2838}
Here is the caller graph for this function:

◆ reset_per_request_attributes()

static void reset_per_request_attributes ( struct mg_connection conn)
static

Definition at line 5040 of file mongoose4.cxx.

5040 {
5041 conn->path_info = NULL;
5042 conn->num_bytes_sent = conn->num_bytes_read = 0;
5043 conn->status_code = -1;
5044 conn->must_close = conn->request_len = conn->throttle = 0;
5045}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ scan_directory()

static int scan_directory ( struct mg_connection conn,
const char dir,
void data,
void(*)(struct de *, void *)  cb 
)
static

Definition at line 3026 of file mongoose4.cxx.

3027 {
3028 char path[PATH_MAX];
3029 struct dirent *dp;
3030 DIR *dirp;
3031 struct de de;
3032
3033 if ((dirp = opendir(dir)) == NULL) {
3034 return 0;
3035 } else {
3036 de.conn = conn;
3037
3038 while ((dp = readdir(dirp)) != NULL) {
3039 // Do not show current dir and hidden files
3040 if (!strcmp(dp->d_name, ".") ||
3041 !strcmp(dp->d_name, "..") ||
3042 must_hide_file(conn, dp->d_name)) {
3043 continue;
3044 }
3045
3046 mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
3047
3048 // If we don't memset stat structure to zero, mtime will have
3049 // garbage and strftime() will segfault later on in
3050 // print_dir_entry(). memset is required only if mg_stat()
3051 // fails. For more details, see
3052 // http://code.google.com/p/mongoose/issues/detail?id=79
3053 memset(&de.file, 0, sizeof(de.file));
3054 mg_stat(path, &de.file);
3055
3056 de.file_name = dp->d_name;
3057 cb(&de, data);
3058 }
3059 (void) closedir(dirp);
3060 }
3061 return 1;
3062}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ send_authorization_request()

static void send_authorization_request ( struct mg_connection conn)
static

Definition at line 1290 of file mongoose4.cxx.

1290 {
1291 conn->status_code = 401;
1292 mg_printf(conn,
1293 "HTTP/1.1 401 Unauthorized\r\n"
1294 "Content-Length: 0\r\n"
1295 "WWW-Authenticate: Digest qop=\"auth\", "
1296 "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
1298 (unsigned long) time(NULL));
1299}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ send_file_data()

static void send_file_data ( struct mg_connection conn,
FILE fp,
int64_t  offset,
int64_t  len 
)
static

Definition at line 3195 of file mongoose4.cxx.

3196 {
3197 char buf[MG_BUF_LEN];
3199
3200 // If offset is beyond file boundaries, don't send anything
3201 if (offset > 0 && fseeko(fp, offset, SEEK_SET) != 0) {
3202 return;
3203 }
3204
3205 while (len > 0) {
3206 // Calculate how much to read from the file in the buffer
3207 to_read = sizeof(buf);
3208 if ((int64_t) to_read > len) {
3209 to_read = (int) len;
3210 }
3211
3212 // Read from file, exit the loop on error
3213 if ((num_read = fread(buf, 1, (size_t) to_read, fp)) <= 0) {
3214 break;
3215 }
3216
3217 // Send read bytes to the client, exit the loop on error
3218 if ((num_written = mg_write(conn, buf, (size_t) num_read)) != num_read) {
3219 break;
3220 }
3221
3222 // Both read and were successful, adjust counters
3223 conn->num_bytes_sent += num_written;
3224 len -= num_written;
3225 }
3226}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ send_http_error() [1/2]

static void send_http_error ( struct mg_connection ,
int  ,
const char ,
PRINTF_FORMAT_STRING(const char *fmt)  ,
  ... 
)
static
Here is the caller graph for this function:

◆ send_http_error() [2/2]

static void send_http_error ( struct mg_connection conn,
int  status,
const char reason,
const char fmt,
  ... 
)
static

Definition at line 2420 of file mongoose4.cxx.

2421 {
2422 char buf[MG_BUF_LEN];
2423 va_list ap;
2424 int len = 0;
2425
2426 conn->status_code = status;
2427 buf[0] = '\0';
2428
2429 // Errors 1xx, 204 and 304 MUST NOT send a body
2430 if (status > 199 && status != 204 && status != 304) {
2431 len = mg_snprintf(buf, sizeof(buf), "Error %d: %s", status, reason);
2432 buf[len++] = '\n';
2433
2434 va_start(ap, fmt);
2435 len += mg_vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
2436 va_end(ap);
2437 }
2438 DEBUG_TRACE(("[%s]", buf));
2439
2440 if (call_user(MG_HTTP_ERROR, conn, (void *) (long) status) == 0) {
2441 mg_printf(conn, "HTTP/1.1 %d %s\r\n"
2442 "Content-Length: %d\r\n"
2443 "Connection: %s\r\n\r\n", status, reason, len,
2445 conn->num_bytes_sent += mg_printf(conn, "%s", buf);
2446 }
2447}
Here is the call graph for this function:

◆ send_ssi_file()

static void send_ssi_file ( struct mg_connection conn,
const char path,
FILE fp,
int  include_level 
)
static

Definition at line 4032 of file mongoose4.cxx.

4033 {
4034 char buf[MG_BUF_LEN];
4035 int ch, offset, len, in_ssi_tag;
4036
4037 if (include_level > 10) {
4038 cry(conn, "SSI #include level is too deep (%s)", path);
4039 return;
4040 }
4041
4042 in_ssi_tag = len = offset = 0;
4043 while ((ch = fgetc(fp)) != EOF) {
4044 if (in_ssi_tag && ch == '>') {
4045 in_ssi_tag = 0;
4046 buf[len++] = (char) ch;
4047 buf[len] = '\0';
4048 assert(len <= (int) sizeof(buf));
4049 if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
4050 // Not an SSI tag, pass it
4051 (void) mg_write(conn, buf, (size_t) len);
4052 } else {
4053 if (!memcmp(buf + 5, "include", 7)) {
4054 do_ssi_include(conn, path, buf + 12, include_level);
4055#if !defined(NO_POPEN)
4056 } else if (!memcmp(buf + 5, "exec", 4)) {
4057 do_ssi_exec(conn, buf + 9);
4058#endif // !NO_POPEN
4059 } else {
4060 cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
4061 }
4062 }
4063 len = 0;
4064 } else if (in_ssi_tag) {
4065 if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
4066 // Not an SSI tag
4067 in_ssi_tag = 0;
4068 } else if (len == (int) sizeof(buf) - 2) {
4069 cry(conn, "%s: SSI tag is too large", path);
4070 len = 0;
4071 }
4072 buf[len++] = ch & 0xff;
4073 } else if (ch == '<') {
4074 in_ssi_tag = 1;
4075 if (len > 0) {
4076 mg_write(conn, buf, (size_t) len);
4077 }
4078 len = 0;
4079 buf[len++] = ch & 0xff;
4080 } else {
4081 buf[len++] = ch & 0xff;
4082 if (len == (int) sizeof(buf)) {
4083 mg_write(conn, buf, (size_t) len);
4084 len = 0;
4085 }
4086 }
4087 }
4088
4089 // Send the rest of buffered data
4090 if (len > 0) {
4091 mg_write(conn, buf, (size_t) len);
4092 }
4093}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_acl_option()

static int set_acl_option ( struct mg_context ctx)
static

Definition at line 5036 of file mongoose4.cxx.

5036 {
5037 return check_acl(ctx, (uint32_t) 0x7f000001UL) != -1;
5038}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_close_on_exec()

static void set_close_on_exec ( int  fd)
static

Definition at line 1798 of file mongoose4.cxx.

1798 {
1799 fcntl(fd, F_SETFD, FD_CLOEXEC);
1800}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_gpass_option()

static int set_gpass_option ( struct mg_context ctx)
static

Definition at line 5026 of file mongoose4.cxx.

5026 {
5028 const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
5029 if (path != NULL && !mg_stat(path, &file)) {
5030 cry(fc(ctx), "Cannot open %s: %s", path, strerror(ERRNO));
5031 return 0;
5032 }
5033 return 1;
5034}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_non_blocking_mode()

static int set_non_blocking_mode ( SOCKET  sock)
static

Definition at line 1870 of file mongoose4.cxx.

1870 {
1871 int flags;
1872
1873 flags = fcntl(sock, F_GETFL, 0);
1874 (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
1875
1876 return 0;
1877}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_ports_option()

static int set_ports_option ( struct mg_context ctx)
static

Definition at line 4877 of file mongoose4.cxx.

4877 {
4878 const char *list = ctx->config[LISTENING_PORTS];
4879 int on = 1, success = 1;
4880#if defined(USE_IPV6)
4881 int off = 0;
4882#endif
4883 struct vec vec;
4884 struct socket so, *ptr;
4885
4886 while (success && (list = next_option(list, &vec, NULL)) != NULL) {
4887 if (!parse_port_string(&vec, &so)) {
4888 cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
4889 __func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|r]");
4890 success = 0;
4891 } else if (so.is_ssl && ctx->ssl_ctx == NULL) {
4892 cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
4893 success = 0;
4894 } else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
4896 // On Windows, SO_REUSEADDR is recommended only for
4897 // broadcast UDP sockets
4899 (void *) &on, sizeof(on)) != 0 ||
4900#if defined(USE_IPV6)
4901 (so.lsa.sa.sa_family == AF_INET6 &&
4902 setsockopt(so.sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &off,
4903 sizeof(off)) != 0) ||
4904#endif
4905 bind(so.sock, &so.lsa.sa, so.lsa.sa.sa_family == AF_INET ?
4906 sizeof(so.lsa.sin) : sizeof(so.lsa)) != 0 ||
4907 listen(so.sock, SOMAXCONN) != 0) {
4908 cry(fc(ctx), "%s: cannot bind to %.*s: %d (%s)", __func__,
4909 (int) vec.len, vec.ptr, ERRNO, strerror(errno));
4910 closesocket(so.sock);
4911 success = 0;
4912 } else if ((ptr = (struct socket *) realloc(ctx->listening_sockets,
4913 (ctx->num_listening_sockets + 1) *
4914 sizeof(ctx->listening_sockets[0]))) == NULL) {
4915 closesocket(so.sock);
4916 success = 0;
4917 } else {
4918 set_close_on_exec(so.sock);
4919 ctx->listening_sockets = ptr;
4921 ctx->num_listening_sockets++;
4922 }
4923 }
4924
4925 if (!success) {
4927 }
4928
4929 return success;
4930}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_sock_timeout()

static int set_sock_timeout ( SOCKET  sock,
int  milliseconds 
)
static

Definition at line 5311 of file mongoose4.cxx.

5311 {
5312#ifdef _WIN32
5313 DWORD t = milliseconds;
5314#else
5315 struct timeval t;
5316 t.tv_sec = milliseconds / 1000;
5317 t.tv_usec = (milliseconds * 1000) % 1000000;
5318#endif
5319 return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *) &t, sizeof(t)) ||
5320 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *) &t, sizeof(t));
5321}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_ssl_option()

static int set_ssl_option ( struct mg_context ctx)
static

Definition at line 2060 of file mongoose4.cxx.

2060 {
2061 int i, size;
2062 const char *pem;
2063 int ecdh_enabled = 0;
2064 int ecdh_available = 0;
2065
2066 // If PEM file is not specified and the init_ssl callback
2067 // is not specified, skip SSL initialization.
2068 if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) {
2069 // MG_INIT_SSL
2070 // ctx->callbacks.init_ssl == NULL) {
2071 return 1;
2072 }
2073
2074#if !defined(NO_SSL_DL)
2075 if (!load_dll(ctx, SSL_LIB, ssl_sw) ||
2076 !load_dll(ctx, CRYPTO_LIB, crypto_sw)) {
2077 return 0;
2078 }
2079#endif // NO_SSL_DL
2080
2081 // Initialize SSL library
2084
2085 if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
2086 cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error());
2087 return 0;
2088 }
2089
2092
2093#if 0
2094 {
2095 //
2096 // enable Diffie-Hellman key exchange - the DHE series of ciphers
2097 //
2098 // this code is from here:
2099 // https://www.openssl.org/docs/ssl/SSL_CTX_set_tmp_dh_callback.html
2100 // to generate the pem file: openssl dhparam -out dh_param_2048.pem 2048
2101
2102 /* Set up ephemeral DH parameters. */
2103 void *dh_2048 = NULL;
2104 FILE *paramfile;
2105 paramfile = fopen("dh_param_2048.pem", "r");
2106 if (paramfile) {
2109 } else {
2110 /* Error. */
2111 }
2112 if (dh_2048 == NULL) {
2113 /* Error. */
2114 }
2115 if (SSL_CTX_set_tmp_dh(ctx->ssl_ctx, dh_2048) != 1) {
2116 /* Error. */
2117 }
2118 }
2119#endif
2120
2121#ifdef OPENSSL_EC_NAMED_CURVE
2122 {
2123 //
2124 // enable Elliptic Curve Diffie-Hellman key exchange - the ECDHE series of ciphers
2125 //
2126
2128 if (! ecdh) {
2129 cry(fc(ctx), "%s: EC_KEY_new_by_curve_name (NID_X9_62_prime256v1) failed: %s", __func__, ssl_error());
2130 } else {
2131 if (1 != SSL_CTX_set_tmp_ecdh (ctx->ssl_ctx, ecdh)) {
2132 cry(fc(ctx), "%s: SSL_CTX_set_tmp_ecdh (ctx->ssl_ctx, ecdh) failed: %s", __func__, ssl_error());
2133 }
2134 EC_KEY_free (ecdh);
2135 ecdh_enabled = 1;
2136 }
2137 }
2138#else
2139#warning Very old openssl, no EC_KEY, no ECDH for modern criptography!
2140#endif
2141
2144
2145 // get cipher list
2146
2147 {
2148 int priority;
2149 SSL* ssl = SSL_new(ctx->ssl_ctx);
2150 for (priority=0; 1; priority++) {
2153 break;
2154 //printf("priority %d cipher list: %s\n", priority, available_cipher_list);
2155 if (strstr(available_cipher_list, "ECDHE") != NULL)
2156 ecdh_available = 1;
2157 }
2158 SSL_free(ssl);
2159 }
2160
2161 if (!ecdh_available || !ecdh_enabled) {
2162 cry(fc(ctx), "%s: openssl \"modern cryptography\" ECDH ciphers not available", __func__);
2163 }
2164
2165 // disable weak ciphers
2166 const char* cipher_list = "ALL:!RC4:!DES-CBC-SHA:!DES:!DES-CBC3-SHA:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW";
2167
2168 // enable only "modern cryptography" ciphers if ECDHE ciphers available
2170 cipher_list = "ALL:!AECDH:!RC4:!DES-CBC-SHA:!DES:!AES128-SHA+RSA:!AES256-SHA+RSA:!DES-CBC3-SHA:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW";
2171
2173
2174 // If user callback returned non-NULL, that means that user callback has
2175 // set up certificate itself. In this case, skip sertificate setting.
2176 // MG_INIT_SSL
2177 if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 ||
2178 SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) {
2179 cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
2180 return 0;
2181 }
2182
2183 if (pem != NULL) {
2185 }
2186
2187 // Initialize locking callbacks, needed for thread safety.
2188 // http://www.openssl.org/support/faq.html#PROG1
2189 size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
2190 if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
2191 cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
2192 return 0;
2193 }
2194
2195 for (i = 0; i < CRYPTO_num_locks(); i++) {
2197 }
2198
2201
2202 return 1;
2203}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_throttle()

static int set_throttle ( const char spec,
uint32_t  remote_ip,
const char uri 
)
static

Definition at line 4511 of file mongoose4.cxx.

4511 {
4512 int throttle = 0;
4513 struct vec vec, val;
4514 uint32_t net, mask;
4515 char mult;
4516 double v;
4517
4518 while ((spec = next_option(spec, &vec, &val)) != NULL) {
4519 mult = ',';
4520 if (sscanf(val.ptr, "%lf%c", &v, &mult) < 1 || v < 0 ||
4521 (lowercase(&mult) != 'k' && lowercase(&mult) != 'm' && mult != ',')) {
4522 continue;
4523 }
4524 v *= lowercase(&mult) == 'k' ? 1024 : lowercase(&mult) == 'm' ? 1048576 : 1;
4525 if (vec.len == 1 && vec.ptr[0] == '*') {
4526 throttle = (int) v;
4527 } else if (parse_net(vec.ptr, &net, &mask) > 0) {
4528 if ((remote_ip & mask) == net) {
4529 throttle = (int) v;
4530 }
4531 } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
4532 throttle = (int) v;
4533 }
4534 }
4535
4536 return throttle;
4537}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_uid_option()

static int set_uid_option ( struct mg_context ctx)
static

Definition at line 5003 of file mongoose4.cxx.

5003 {
5004 struct passwd *pw;
5005 const char *uid = ctx->config[RUN_AS_USER];
5006 int success = 0;
5007
5008 if (uid == NULL) {
5009 success = 1;
5010 } else {
5011 if ((pw = getpwnam(uid)) == NULL) {
5012 cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
5013 } else if (setgid(pw->pw_gid) == -1) {
5014 cry(fc(ctx), "%s: setgid(%s): %s", __func__, uid, strerror(errno));
5015 } else if (setuid(pw->pw_uid) == -1) {
5016 cry(fc(ctx), "%s: setuid(%s): %s", __func__, uid, strerror(errno));
5017 } else {
5018 success = 1;
5019 }
5020 }
5021
5022 return success;
5023}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ should_keep_alive()

static int should_keep_alive ( const struct mg_connection conn)
static

Definition at line 2403 of file mongoose4.cxx.

2403 {
2404 const char *http_version = conn->request_info.http_version;
2405 const char *header = mg_get_header(conn, "Connection");
2406 if (conn->must_close ||
2407 conn->status_code == 401 ||
2408 mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 ||
2409 (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) ||
2410 (header == NULL && http_version && strcmp(http_version, "1.1"))) {
2411 return 0;
2412 }
2413 return 1;
2414}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ skip()

static char * skip ( char **  buf,
const char delimiters 
)
static

Definition at line 676 of file mongoose4.cxx.

676 {
677 return skip_quoted(buf, delimiters, delimiters, 0);
678}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ skip_quoted()

static char * skip_quoted ( char **  buf,
const char delimiters,
const char whitespace,
char  quotechar 
)
static

Definition at line 632 of file mongoose4.cxx.

633 {
634 char *p, *begin_word, *end_word, *end_whitespace;
635
636 begin_word = *buf;
638
639 // Check for quotechar
640 if (end_word > begin_word) {
641 p = end_word - 1;
642 while (*p == quotechar) {
643 // If there is anything beyond end_word, copy it
644 if (*end_word == '\0') {
645 *p = '\0';
646 break;
647 } else {
648 size_t end_off = strcspn(end_word + 1, delimiters);
649 memmove (p, end_word, end_off + 1);
650 p += end_off; // p must correspond to end_word - 1
651 end_word += end_off + 1;
652 }
653 }
654 for (p++; p < end_word; p++) {
655 *p = '\0';
656 }
657 }
658
659 if (*end_word == '\0') {
660 *buf = end_word;
661 } else {
663
664 for (p = end_word; p < end_whitespace; p++) {
665 *p = '\0';
666 }
667
668 *buf = end_whitespace;
669 }
670
671 return begin_word;
672}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sockaddr_to_string()

static void sockaddr_to_string ( char buf,
size_t  len,
const union usa usa 
)
static

Definition at line 2342 of file mongoose4.cxx.

2343 {
2344 buf[0] = '\0';
2345#if defined(USE_IPV6)
2346 inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
2347 (void *) &usa->sin.sin_addr :
2348 (void *) &usa->sin6.sin6_addr, buf, len);
2349#elif defined(_WIN32)
2350 // Only Windoze Vista (and newer) have inet_ntop()
2351 strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
2352#else
2353 inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
2354#endif
2355}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ spawn_process()

static pid_t spawn_process ( struct mg_connection conn,
const char prog,
char envblk,
char envp[],
int  fdin,
int  fdout,
const char dir 
)
static

Definition at line 1822 of file mongoose4.cxx.

1824 {
1825 pid_t pid;
1826 const char *interp;
1827
1828 (void) envblk;
1829
1830 if ((pid = fork()) == -1) {
1831 // Parent
1832 send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
1833 } else if (pid == 0) {
1834 // Child
1835 if (chdir(dir) != 0) {
1836 cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
1837 } else if (dup2(fdin, 0) == -1) {
1838 cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO));
1839 } else if (dup2(fdout, 1) == -1) {
1840 cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO));
1841 } else {
1842 // Not redirecting stderr to stdout, to avoid output being littered
1843 // with the error messages.
1844 (void) close(fdin);
1845 (void) close(fdout);
1846
1847 // After exec, all signal handlers are restored to their default values,
1848 // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
1849 // implementation, SIGCHLD's handler will leave unchanged after exec
1850 // if it was set to be ignored. Restore it to default action.
1852
1853 interp = conn->ctx->config[CGI_INTERPRETER];
1854 if (interp == NULL) {
1855 (void) execle(prog, prog, NULL, envp);
1856 cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
1857 } else {
1859 cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
1860 strerror(ERRNO));
1861 }
1862 }
1863 exit(EXIT_FAILURE);
1864 }
1865
1866 return pid;
1867}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ssl_error()

static const char * ssl_error ( void  )
static

Definition at line 2004 of file mongoose4.cxx.

2004 {
2005 unsigned long err;
2006 err = ERR_get_error();
2007 return err == 0 ? "" : ERR_error_string(err, NULL);
2008}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ssl_id_callback()

static unsigned long ssl_id_callback ( void  )
static

Definition at line 2022 of file mongoose4.cxx.

2022 {
2023 return (unsigned long) pthread_self();
2024}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ssl_locking_callback()

static void ssl_locking_callback ( int  mode,
int  mutex_num,
const char file,
int  line 
)
static

Definition at line 2010 of file mongoose4.cxx.

2011 {
2012 (void) line;
2013 (void) file;
2014
2015 if (mode & 1) { // 1 is CRYPTO_LOCK
2017 } else {
2019 }
2020}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sslize()

static int sslize ( struct mg_connection conn,
SSL_CTX s,
int(*)(SSL *)  func 
)
static

Definition at line 1997 of file mongoose4.cxx.

1997 {
1998 return (conn->ssl = SSL_new(s)) != NULL &&
1999 SSL_set_fd(conn->ssl, conn->client.sock) == 1 &&
2000 func(conn->ssl) == 1;
2001}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ substitute_index_file()

static int substitute_index_file ( struct mg_connection conn,
char path,
size_t  path_len,
struct file filep 
)
static

Definition at line 3426 of file mongoose4.cxx.

3427 {
3428 const char *list = conn->ctx->config[INDEX_FILES];
3430 struct vec filename_vec;
3431 size_t n = strlen(path);
3432 int found = 0;
3433
3434 // The 'path' given to us points to the directory. Remove all trailing
3435 // directory separator characters from the end of the path, and
3436 // then append single directory separator character.
3437 while (n > 0 && path[n - 1] == '/') {
3438 n--;
3439 }
3440 path[n] = '/';
3441
3442 // Traverse index files list. For each entry, append it to the given
3443 // path and see if the file exists. If it exists, break the loop
3444 while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
3445
3446 // Ignore too long entries that may overflow path buffer
3447 if (filename_vec.len > path_len - (n + 2))
3448 continue;
3449
3450 // Prepare full path to the index file
3451 mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
3452
3453 // Does it exist?
3454 if (mg_stat(path, &file)) {
3455 // Yes it does, break the loop
3456 *filep = file;
3457 found = 1;
3458 break;
3459 }
3460 }
3461
3462 // If no index file exists, restore directory path
3463 if (!found) {
3464 path[n] = '\0';
3465 }
3466
3467 return found;
3468}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ suggest_connection_header()

static const char * suggest_connection_header ( const struct mg_connection conn)
static

Definition at line 2416 of file mongoose4.cxx.

2416 {
2417 return should_keep_alive(conn) ? "keep-alive" : "close";
2418}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ uninitialize_ssl()

static void uninitialize_ssl ( struct mg_context ctx)
static

Definition at line 2205 of file mongoose4.cxx.

2205 {
2206 int i;
2207 if (ctx->ssl_ctx != NULL) {
2209 for (i = 0; i < CRYPTO_num_locks(); i++) {
2211 }
2214 }
2215}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ worker_thread()

static void * worker_thread ( void thread_func_param)
static

Definition at line 5235 of file mongoose4.cxx.

5235 {
5236 struct mg_context *ctx = (struct mg_context *) thread_func_param;
5237 struct mg_connection *conn;
5238
5239 conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
5240 if (conn == NULL) {
5241 cry(fc(ctx), "%s", "Cannot create new connection struct, OOM");
5242 } else {
5243 conn->buf_size = MAX_REQUEST_SIZE;
5244 conn->buf = (char *) (conn + 1);
5245 conn->ctx = ctx;
5246 conn->event.user_data = ctx->user_data;
5247
5249
5250 // Call consume_socket() even when ctx->stop_flag > 0, to let it signal
5251 // sq_empty condvar to wake up the master waiting in produce_socket()
5252 while (consume_socket(ctx, &conn->client)) {
5253 conn->birth_time = time(NULL);
5254
5255 // Fill in IP, port info early so even if SSL setup below fails,
5256 // error handler would have the corresponding info.
5257 // Thanks to Johannes Winkelmann for the patch.
5258 // TODO(lsm): Fix IPv6 case
5259 conn->request_info.remote_port = ntohs(conn->client.rsa.sin.sin_port);
5261 &conn->client.rsa.sin.sin_addr.s_addr, 4);
5263 conn->request_info.is_ssl = conn->client.is_ssl;
5264
5265 if (!conn->client.is_ssl
5267 || sslize(conn, conn->ctx->ssl_ctx, SSL_accept)
5268#endif
5269 ) {
5271 }
5272
5273 close_connection(conn);
5274 }
5276 free(conn);
5277 }
5278
5279 // Signal master that we're done with connection and exiting
5281 ctx->num_threads--;
5283 assert(ctx->num_threads >= 0);
5285
5286 DEBUG_TRACE(("exiting"));
5287 return NULL;
5288}
Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ [struct]

const struct { ... } builtin_mime_types[]

◆ config_options

const char* config_options[]
static
Initial value:
= {
"cgi_pattern", "**.cgi$|**.pl$|**.php$",
"cgi_environment", NULL,
"put_delete_auth_file", NULL,
"cgi_interpreter", NULL,
"protect_uri", NULL,
"authentication_domain", "mydomain.com",
"ssi_pattern", "**.shtml$|**.shtm$",
"throttle", NULL,
"access_log_file", NULL,
"enable_directory_listing", "yes",
"error_log_file", NULL,
"global_auth_file", NULL,
"index_files",
"index.html,index.htm,index.cgi,index.shtml,index.php,index.lp",
"enable_keep_alive", "no",
"access_control_list", NULL,
"extra_mime_types", NULL,
"listening_ports", "8080",
"document_root", NULL,
"ssl_certificate", NULL,
"num_threads", "50",
"run_as_user", NULL,
"url_rewrite_patterns", NULL,
"hide_files_patterns", NULL,
"request_timeout_ms", "30000",
}

Definition at line 826 of file mongoose4.cxx.

826 {
827 "cgi_pattern", "**.cgi$|**.pl$|**.php$",
828 "cgi_environment", NULL,
829 "put_delete_auth_file", NULL,
830 "cgi_interpreter", NULL,
831 "protect_uri", NULL,
832 "authentication_domain", "mydomain.com",
833 "ssi_pattern", "**.shtml$|**.shtm$",
834 "throttle", NULL,
835 "access_log_file", NULL,
836 "enable_directory_listing", "yes",
837 "error_log_file", NULL,
838 "global_auth_file", NULL,
839 "index_files",
840 "index.html,index.htm,index.cgi,index.shtml,index.php,index.lp",
841 "enable_keep_alive", "no",
842 "access_control_list", NULL,
843 "extra_mime_types", NULL,
844 "listening_ports", "8080",
845 "document_root", NULL,
846 "ssl_certificate", NULL,
847 "num_threads", "50",
848 "run_as_user", NULL,
849 "url_rewrite_patterns", NULL,
850 "hide_files_patterns", NULL,
851 "request_timeout_ms", "30000",
852 NULL
853};

◆ ext_len

size_t ext_len

Definition at line 2842 of file mongoose4.cxx.

◆ extension

const char* extension

Definition at line 2841 of file mongoose4.cxx.

◆ http_500_error

const char* http_500_error = "Internal Server Error"
static

Definition at line 319 of file mongoose4.cxx.

◆ mime_type

const char* mime_type

Definition at line 2843 of file mongoose4.cxx.

◆ month_names

const char* month_names[]
static
Initial value:
= {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
}

Definition at line 777 of file mongoose4.cxx.

777 {
778 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
779 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
780};

◆ ssl_mutexes

pthread_mutex_t* ssl_mutexes
static

Definition at line 1995 of file mongoose4.cxx.