Github User Fetcher 1.0.0
C Application with Server and GUI
Loading...
Searching...
No Matches
civetweb.c File Reference
#include "civetweb.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdint.h>
#include <inttypes.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
#include <grp.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
#include <pthread.h>
#include <pwd.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <dlfcn.h>
#include "md5.inl"
#include "openssl_dl.inl"
#include "sort.inl"
#include "match.inl"
#include "response.inl"
#include "handle_form.inl"

Go to the source code of this file.

Data Structures

struct  mg_workerTLS
 
union  usa
 
struct  vec
 
struct  mg_file_stat
 
struct  mg_file_access
 
struct  mg_file
 
struct  socket
 
struct  mg_handler_info
 
struct  mg_domain_context
 
struct  twebdav_lock
 
struct  mg_context
 
struct  mg_connection
 
struct  de
 
struct  ah
 
struct  read_auth_file_struct
 
struct  dir_scan_data
 
struct  mg_http_method_info
 
struct  cgi_environment
 
struct  process_control_data
 
struct  websocket_client_thread_data
 

Macros

#define _GNU_SOURCE   /* for setgroups(), pthread_setname_np() */
 
#define _LARGEFILE_SOURCE   /* For fseeko(), ftello() */
 
#define _FILE_OFFSET_BITS   64 /* Use 64-bit file offsets by default */
 
#define __STDC_FORMAT_MACROS   /* <inttypes.h> wants this for C++ */
 
#define __STDC_LIMIT_MACROS   /* C++ wants that for INT64_MAX */
 
#define _DARWIN_UNLIMITED_SELECT
 
#define mg_static_assert(cond, txt)    extern char static_assert_replacement[(cond) ? 1 : -1]
 
#define NO_ALTERNATIVE_QUEUE
 
#define WIN32_LEAN_AND_MEAN
 
#define DEBUG_TRACE(fmt, ...)
 
#define DEBUG_ASSERT(cond)
 
#define IGNORE_UNUSED_RESULT(a)   ((void)((a) && 1))
 
#define FUNCTION_MAY_BE_UNUSED
 
#define ERROR_TRY_AGAIN(err)    (((err) == EAGAIN) || ((err) == EWOULDBLOCK) || ((err) == EINTR))
 
#define MAX_WORKER_THREADS   (1024 * 64) /* in threads (count) */
 
#define SOCKET_TIMEOUT_QUANTUM   (2000) /* in ms */
 
#define MG_FILE_COMPRESSION_SIZE_LIMIT   (1024) /* in bytes */
 
#define PASSWORDS_FILE_NAME   ".htpasswd"
 
#define CGI_ENVIRONMENT_SIZE   (4096) /* in bytes */
 
#define MAX_CGI_ENVIR_VARS   (256) /* in variables (count) */
 
#define MG_BUF_LEN   (1024 * 8)
 
#define ARRAY_SIZE(array)   (sizeof(array) / sizeof(array[0]))
 
#define INT64_MAX   (9223372036854775807)
 
#define SHUTDOWN_RD   (0)
 
#define SHUTDOWN_WR   (1)
 
#define SHUTDOWN_BOTH   (2)
 
#define UTF8_PATH_MAX   (PATH_MAX)
 
#define vsnprintf_impl   vsnprintf
 
#define SSL_LIB   "libssl.so"
 
#define CRYPTO_LIB   "libcrypto.so"
 
#define O_BINARY   (0)
 
#define closesocket(a)   (close(a))
 
#define mg_mkdir(conn, path, mode)   (mkdir(path, mode))
 
#define mg_remove(conn, x)   (remove(x))
 
#define mg_sleep(x)   (usleep((x)*1000))
 
#define mg_opendir(conn, x)   (opendir(x))
 
#define mg_closedir(x)   (closedir(x))
 
#define mg_readdir(x)   (readdir(x))
 
#define ERRNO   (errno)
 
#define INVALID_SOCKET   (-1)
 
#define INT64_FMT   PRId64
 
#define UINT64_FMT   PRIu64
 
#define WINCDECL
 
#define mg_pollfd   pollfd
 
#define va_copy(x, y)   ((x) = (y))
 
#define mg_malloc_ctx(a, c)   mg_malloc(a)
 
#define mg_calloc_ctx(a, b, c)   mg_calloc(a, b)
 
#define mg_realloc_ctx(a, b, c)   mg_realloc(a, b)
 
#define mg_free_ctx(a, c)   mg_free(a)
 
#define malloc   DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
 
#define calloc   DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
 
#define realloc   DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
 
#define free   DO_NOT_USE_THIS_FUNCTION__USE_mg_free
 
#define snprintf   DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
 
#define MD5_STATIC   static
 
#define IP_ADDR_STR_LEN   (50) /* IPv6 hex string is 46 chars */
 
#define MSG_NOSIGNAL   (0)
 
#define USA_IN_PORT_UNSAFE(s)   ((s)->sin.sin_port)
 
#define STRUCT_FILE_INITIALIZER
 
#define STOP_FLAG_IS_ZERO(f)   ((*(f)) == 0)
 
#define STOP_FLAG_IS_TWO(f)   ((*(f)) == 2)
 
#define STOP_FLAG_ASSIGN(f, v)   ((*(f)) = (v))
 
#define NUM_WEBDAV_LOCKS   10
 
#define LOCK_DURATION_S   60
 
#define mg_cry_internal(conn, fmt, ...)    mg_cry_internal_wrap(conn, NULL, __func__, __LINE__, fmt, __VA_ARGS__)
 
#define mg_cry_ctx_internal(ctx, fmt, ...)    mg_cry_internal_wrap(NULL, ctx, __func__, __LINE__, fmt, __VA_ARGS__)
 
#define MG_FOPEN_MODE_NONE   (0)
 
#define MG_FOPEN_MODE_READ   (1)
 
#define MG_FOPEN_MODE_WRITE   (2)
 
#define MG_FOPEN_MODE_APPEND   (4)
 
#define mg_get_option   DO_NOT_USE_THIS_FUNCTION_INTERNALLY__access_directly
 
#define mg_cry   DO_NOT_USE_THIS_FUNCTION__USE_mg_cry_internal
 
#define HTTP1_only
 
#define HEXTOI(x)   (isdigit(x) ? (x - '0') : (x - 'W'))
 
#define INITIAL_DEPTH   9
 

Typedefs

typedef const void * SOCK_OPT_TYPE
 
typedef int SOCKET
 
typedef int volatile stop_flag_t
 

Enumerations

enum  {
  LISTENING_PORTS , NUM_THREADS , RUN_AS_USER , CONFIG_TCP_NODELAY ,
  MAX_REQUEST_SIZE , LINGER_TIMEOUT , CONNECTION_QUEUE_SIZE , LISTEN_BACKLOG_SIZE ,
  THROTTLE , ENABLE_KEEP_ALIVE , REQUEST_TIMEOUT , KEEP_ALIVE_TIMEOUT ,
  DECODE_URL , DECODE_QUERY_STRING , DOCUMENT_ROOT , ACCESS_LOG_FILE ,
  ERROR_LOG_FILE , CGI_EXTENSIONS , CGI_ENVIRONMENT , CGI_INTERPRETER ,
  CGI_INTERPRETER_ARGS , CGI_BUFFERING , CGI2_EXTENSIONS , CGI2_ENVIRONMENT ,
  CGI2_INTERPRETER , CGI2_INTERPRETER_ARGS , CGI2_BUFFERING , PUT_DELETE_PASSWORDS_FILE ,
  PROTECT_URI , AUTHENTICATION_DOMAIN , ENABLE_AUTH_DOMAIN_CHECK , SSI_EXTENSIONS ,
  ENABLE_DIRECTORY_LISTING , ENABLE_WEBDAV , GLOBAL_PASSWORDS_FILE , INDEX_FILES ,
  ACCESS_CONTROL_LIST , EXTRA_MIME_TYPES , SSL_CERTIFICATE , SSL_CERTIFICATE_CHAIN ,
  URL_REWRITE_PATTERN , HIDE_FILES , SSL_DO_VERIFY_PEER , SSL_CACHE_TIMEOUT ,
  SSL_CA_PATH , SSL_CA_FILE , SSL_VERIFY_DEPTH , SSL_DEFAULT_VERIFY_PATHS ,
  SSL_CIPHER_LIST , SSL_PROTOCOL_VERSION , SSL_SHORT_TRUST , ACCESS_CONTROL_ALLOW_ORIGIN ,
  ACCESS_CONTROL_ALLOW_METHODS , ACCESS_CONTROL_ALLOW_HEADERS , ERROR_PAGES , STATIC_FILE_MAX_AGE ,
  STATIC_FILE_CACHE_CONTROL , STRICT_HTTPS_MAX_AGE , ADDITIONAL_HEADER , ALLOW_INDEX_SCRIPT_SUB_RES ,
  NUM_OPTIONS
}
 
enum  { REQUEST_HANDLER , WEBSOCKET_HANDLER , AUTH_HANDLER }
 
enum  { CONTEXT_INVALID , CONTEXT_SERVER , CONTEXT_HTTP_CLIENT , CONTEXT_WS_CLIENT }
 
enum  { CONNECTION_TYPE_INVALID = 0 , CONNECTION_TYPE_REQUEST = 1 , CONNECTION_TYPE_RESPONSE = 2 }
 
enum  { PROTOCOL_TYPE_HTTP1 = 0 , PROTOCOL_TYPE_WEBSOCKET = 1 , PROTOCOL_TYPE_HTTP2 = 2 }
 

Functions

 mg_static_assert (sizeof(int)==4||sizeof(int)==8, "int data type size check")
 
 mg_static_assert (sizeof(void *)==4||sizeof(void *)==8, "pointer data type size check")
 
 mg_static_assert (sizeof(void *) >=sizeof(int), "data type size check")
 
 mg_static_assert (MAX_WORKER_THREADS >=1, "worker threads must be a positive number")
 
 mg_static_assert (sizeof(size_t)==4||sizeof(size_t)==8, "size_t data type size check")
 
static FUNCTION_MAY_BE_UNUSED void mg_global_lock (void)
 
static FUNCTION_MAY_BE_UNUSED void mg_global_unlock (void)
 
static FUNCTION_MAY_BE_UNUSED ptrdiff_t mg_atomic_inc (volatile ptrdiff_t *addr)
 
static FUNCTION_MAY_BE_UNUSED ptrdiff_t mg_atomic_dec (volatile ptrdiff_t *addr)
 
static __inline void * mg_malloc (size_t a)
 
static __inline void * mg_calloc (size_t a, size_t b)
 
static __inline void * mg_realloc (void *a, size_t b)
 
static __inline void mg_free (void *a)
 
static void mg_vsnprintf (const struct mg_connection *conn, int *truncated, char *buf, size_t buflen, const char *fmt, va_list ap)
 
static void mg_snprintf (const struct mg_connection *conn, int *truncated, char *buf, size_t buflen, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(5
 
static FUNCTION_MAY_BE_UNUSED unsigned long mg_current_thread_id (void)
 
static FUNCTION_MAY_BE_UNUSED uint64_t mg_get_current_time_ns (void)
 
 mg_static_assert ((sizeof(config_options)/sizeof(config_options[0]))==(NUM_OPTIONS+1), "config_options and enum not sync")
 
static void mg_cry_internal_wrap (const struct mg_connection *conn, struct mg_context *ctx, const char *func, unsigned line, const char *fmt,...) PRINTF_ARGS(5
 
static void static void mg_set_thread_name (const char *name)
 
CIVETWEB_API const struct mg_optionmg_get_valid_options (void)
 
static int is_file_opened (const struct mg_file_access *fileacc)
 
static int mg_stat (const struct mg_connection *conn, const char *path, struct mg_file_stat *filep)
 
static int mg_path_suspicious (const struct mg_connection *conn, const char *path)
 
static int mg_fopen (const struct mg_connection *conn, const char *path, int mode, struct mg_file *filep)
 
static int mg_fclose (struct mg_file_access *fileacc)
 
static void mg_strlcpy (char *dst, const char *src, size_t n)
 
static int lowercase (const char *s)
 
CIVETWEB_API int mg_strncasecmp (const char *s1, const char *s2, size_t len)
 
CIVETWEB_API int mg_strcasecmp (const char *s1, const char *s2)
 
static char * mg_strndup_ctx (const char *ptr, size_t len, struct mg_context *ctx)
 
static char * mg_strdup_ctx (const char *str, struct mg_context *ctx)
 
static char * mg_strdup (const char *str)
 
static const char * mg_strcasestr (const char *big_str, const char *small_str)
 
static void mg_snprintf (const struct mg_connection *conn, int *truncated, char *buf, size_t buflen, const char *fmt,...)
 
static int get_option_index (const char *name)
 
CIVETWEB_API const char * mg_get_option (const struct mg_context *ctx, const char *name)
 
CIVETWEB_API struct mg_contextmg_get_context (const struct mg_connection *conn)
 
CIVETWEB_API void * mg_get_user_data (const struct mg_context *ctx)
 
CIVETWEB_API void * mg_get_user_context_data (const struct mg_connection *conn)
 
CIVETWEB_API void * mg_get_thread_pointer (const struct mg_connection *conn)
 
CIVETWEB_API void mg_set_user_connection_data (const struct mg_connection *const_conn, void *data)
 
CIVETWEB_API void * mg_get_user_connection_data (const struct mg_connection *conn)
 
CIVETWEB_API int mg_get_server_ports (const struct mg_context *ctx, int size, struct mg_server_port *ports)
 
static void sockaddr_to_string (char *buf, size_t len, const union usa *usa)
 
static void gmt_time_string (char *buf, size_t buf_len, time_t *t)
 
static double mg_difftimespec (const struct timespec *ts_now, const struct timespec *ts_before)
 
static void mg_cry_internal_impl (const struct mg_connection *conn, const char *func, unsigned line, const char *fmt, va_list ap)
 
static struct mg_connectionfake_connection (struct mg_connection *fc, struct mg_context *ctx)
 
CIVETWEB_API void mg_cry (const struct mg_connection *conn, const char *fmt,...)
 
CIVETWEB_API const char * mg_version (void)
 
CIVETWEB_API const struct mg_request_infomg_get_request_info (const struct mg_connection *conn)
 
CIVETWEB_API const struct mg_response_infomg_get_response_info (const struct mg_connection *conn)
 
static const char * get_proto_name (const struct mg_connection *conn)
 
static int mg_construct_local_link (const struct mg_connection *conn, char *buf, size_t buflen, const char *define_proto, int define_port, const char *define_uri)
 
CIVETWEB_API int mg_get_request_link (const struct mg_connection *conn, char *buf, size_t buflen)
 
static char * skip_quoted (char **buf, const char *delimiters, const char *whitespace, char quotechar)
 
static const char * get_header (const struct mg_header *hdr, int num_hdr, const char *name)
 
static int get_req_headers (const struct mg_request_info *ri, const char *name, const char **output, int output_max_size)
 
CIVETWEB_API const char * mg_get_header (const struct mg_connection *conn, const char *name)
 
static const char * get_http_version (const struct mg_connection *conn)
 
static const char * next_option (const char *list, struct vec *val, struct vec *eq_val)
 
static int header_has_option (const char *header, const char *option)
 
static int should_keep_alive (const struct mg_connection *conn)
 
static int should_decode_url (const struct mg_connection *conn)
 
static int should_decode_query_string (const struct mg_connection *conn)
 
static const char * suggest_connection_header (const struct mg_connection *conn)
 
static void send_no_cache_header (struct mg_connection *conn)
 
static void send_static_cache_header (struct mg_connection *conn)
 
static void send_additional_header (struct mg_connection *conn)
 
static void send_cors_header (struct mg_connection *conn)
 
static void handle_file_based_request (struct mg_connection *conn, const char *path, struct mg_file *filep)
 
CIVETWEB_API const char * mg_get_response_code_text (const struct mg_connection *conn, int response_code)
 
static int mg_send_http_error_impl (struct mg_connection *conn, int status, const char *fmt, va_list args)
 
CIVETWEB_API int mg_send_http_error (struct mg_connection *conn, int status, const char *fmt,...)
 
CIVETWEB_API int mg_send_http_ok (struct mg_connection *conn, const char *mime_type, long long content_length)
 
CIVETWEB_API int mg_send_http_redirect (struct mg_connection *conn, const char *target_url, int redirect_code)
 
static void set_close_on_exec (int fd, const struct mg_connection *conn, struct mg_context *ctx)
 
CIVETWEB_API int mg_start_thread (mg_thread_func_t func, void *param)
 
static int mg_start_thread_with_id (mg_thread_func_t func, void *param, pthread_t *threadidptr)
 
static int mg_join_thread (pthread_t threadid)
 
static pid_t spawn_process (struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin[2], int fdout[2], int fderr[2], const char *dir, int cgi_config_idx)
 
static int set_non_blocking_mode (SOCKET sock)
 
static int set_blocking_mode (SOCKET sock)
 
static uint64_t get_random (void)
 
static int mg_poll (struct mg_pollfd *pfd, unsigned int n, int milliseconds, const stop_flag_t *stop_flag)
 
static int push_inner (struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int len, double timeout)
 
static int push_all (struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int len)
 
static int pull_inner (FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
 
static int pull_all (FILE *fp, struct mg_connection *conn, char *buf, int len)
 
static void discard_unread_request_data (struct mg_connection *conn)
 
static int mg_read_inner (struct mg_connection *conn, void *buf, size_t len)
 
static void handle_request (struct mg_connection *)
 
static void log_access (const struct mg_connection *)
 
static void handle_request_stat_log (struct mg_connection *conn)
 
CIVETWEB_API int mg_read (struct mg_connection *conn, void *buf, size_t len)
 
CIVETWEB_API int mg_write (struct mg_connection *conn, const void *buf, size_t len)
 
CIVETWEB_API int mg_send_chunk (struct mg_connection *conn, const char *chunk, unsigned int chunk_len)
 
static int alloc_vprintf2 (char **buf, const char *fmt, va_list ap)
 
static int alloc_vprintf (char **out_buf, char *prealloc_buf, size_t prealloc_size, const char *fmt, va_list ap)
 
static int alloc_printf (char **out_buf, const char *fmt,...)
 
static int mg_vprintf (struct mg_connection *conn, const char *fmt, va_list ap)
 
CIVETWEB_API int mg_printf (struct mg_connection *conn, const char *fmt,...)
 
CIVETWEB_API int mg_url_decode (const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
 
static void url_decode_in_place (char *buf)
 
CIVETWEB_API int mg_get_var (const char *data, size_t data_len, const char *name, char *dst, size_t dst_len)
 
CIVETWEB_API int mg_get_var2 (const char *data, size_t data_len, const char *name, char *dst, size_t dst_len, size_t occurrence)
 
CIVETWEB_API int mg_split_form_urlencoded (char *data, struct mg_header *form_fields, unsigned num_form_fields)
 
CIVETWEB_API int mg_get_cookie (const char *cookie_header, const char *var_name, char *dst, size_t dst_size)
 
CIVETWEB_API int mg_base64_encode (const unsigned char *src, size_t src_len, char *dst, size_t *dst_len)
 
static unsigned char b64reverse (char letter)
 
CIVETWEB_API int mg_base64_decode (const char *src, size_t src_len, unsigned char *dst, size_t *dst_len)
 
static int is_put_or_delete_method (const struct mg_connection *conn)
 
static int is_civetweb_webdav_method (const struct mg_connection *conn)
 
static int extention_matches_script (struct mg_connection *conn, const char *filename)
 
static int extention_matches_template_text (struct mg_connection *conn, const char *filename)
 
static int substitute_index_file (struct mg_connection *conn, char *path, size_t path_len, struct mg_file_stat *filestat)
 
static void interpret_uri (struct mg_connection *conn, char *filename, size_t filename_buf_len, struct mg_file_stat *filestat, int *is_found, int *is_script_resource, int *is_websocket_request, int *is_put_or_delete_request, int *is_webdav_request, int *is_template_text)
 
static int get_http_header_len (const char *buf, int buflen)
 
static int get_month_index (const char *s)
 
static time_t parse_date_string (const char *datetime)
 
static void remove_dot_segments (char *inout)
 
CIVETWEB_API const char * mg_get_builtin_mime_type (const char *path)
 
static void get_mime_type (struct mg_connection *conn, const char *path, struct vec *vec)
 
static void bin2str (char *to, const unsigned char *p, size_t len)
 
CIVETWEB_API char * mg_md5 (char buf[33],...)
 
static int check_password_digest (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 void open_auth_file (struct mg_connection *conn, const char *path, struct mg_file *filep)
 
static int parse_auth_header (struct mg_connection *conn, char *buf, size_t buf_size, struct ah *ah)
 
static const char * mg_fgets (char *buf, size_t size, struct mg_file *filep)
 
static int read_auth_file (struct mg_file *filep, struct read_auth_file_struct *workdata, int depth)
 
static int authorize (struct mg_connection *conn, struct mg_file *filep, const char *realm)
 
CIVETWEB_API int mg_check_digest_access_authentication (struct mg_connection *conn, const char *realm, const char *filename)
 
static int check_authorization (struct mg_connection *conn, const char *path)
 
static void send_authorization_request (struct mg_connection *conn, const char *realm)
 
CIVETWEB_API int mg_send_digest_access_authentication_request (struct mg_connection *conn, const char *realm)
 
static int is_authorized_for_put (struct mg_connection *conn)
 
CIVETWEB_API int mg_modify_passwords_file_ha1 (const char *fname, const char *domain, const char *user, const char *ha1)
 
CIVETWEB_API int mg_modify_passwords_file (const char *fname, const char *domain, const char *user, const char *pass)
 
static int is_valid_port (unsigned long port)
 
static int mg_inet_pton (int af, const char *src, void *dst, size_t dstlen, int resolve_src)
 
static int connect_socket (struct mg_context *ctx, const char *host, int port, int use_ssl, struct mg_error_data *error, SOCKET *sock, union usa *sa)
 
CIVETWEB_API int mg_url_encode (const char *src, char *dst, size_t dst_len)
 
static int print_dir_entry (struct mg_connection *conn, struct de *de)
 
static int compare_dir_entries (const void *p1, const void *p2, void *arg)
 
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, int(*cb)(struct de *, void *))
 
static int remove_directory (struct mg_connection *conn, const char *dir)
 
static int 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, struct mg_file *filep, int64_t offset, int64_t len, int no_buffering)
 
static int parse_range_header (const char *header, int64_t *a, int64_t *b)
 
static void construct_etag (char *buf, size_t buf_len, const struct mg_file_stat *filestat)
 
static void fclose_on_exec (struct mg_file_access *filep, struct mg_connection *conn)
 
static void handle_static_file_request (struct mg_connection *conn, const char *path, struct mg_file *filep, const char *mime_type, const char *additional_headers)
 
CIVETWEB_API int mg_send_file_body (struct mg_connection *conn, const char *path)
 
static int is_not_modified (const struct mg_connection *conn, const struct mg_file_stat *filestat)
 
static void handle_not_modified_static_file_request (struct mg_connection *conn, struct mg_file *filep)
 
CIVETWEB_API void mg_send_file (struct mg_connection *conn, const char *path)
 
CIVETWEB_API void mg_send_mime_file (struct mg_connection *conn, const char *path, const char *mime_type)
 
CIVETWEB_API void mg_send_mime_file2 (struct mg_connection *conn, const char *path, const char *mime_type, const char *additional_headers)
 
static int put_dir (struct mg_connection *conn, const char *path)
 
static void remove_bad_file (const struct mg_connection *conn, const char *path)
 
CIVETWEB_API long long mg_store_body (struct mg_connection *conn, const char *path)
 
static int skip_to_end_of_word_and_terminate (char **ppw, int eol)
 
static int parse_http_headers (char **buf, struct mg_header hdr[MG_MAX_HEADERS])
 
static const struct mg_http_method_infoget_http_method_info (const char *method)
 
static int is_valid_http_method (const char *method)
 
static int parse_http_request (char *buf, int len, struct mg_request_info *ri)
 
static int parse_http_response (char *buf, int len, struct mg_response_info *ri)
 
static int read_message (FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread)
 
static int forward_body_data (struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 
static void addenv (struct cgi_environment *env, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
 
static void static void addenv (struct cgi_environment *env, const char *fmt,...)
 
static int prepare_cgi_environment (struct mg_connection *conn, const char *prog, struct cgi_environment *env, int cgi_config_idx)
 
static int abort_cgi_process (void *data)
 
static void handle_cgi_request (struct mg_connection *conn, const char *prog, int cgi_config_idx)
 
static void dav_mkcol (struct mg_connection *conn, const char *path)
 
static int get_uri_type (const char *uri)
 
static const char * get_rel_url_at_current_server (const char *uri, const struct mg_connection *conn)
 
static void dav_move_file (struct mg_connection *conn, const char *path, int do_copy)
 
static void put_file (struct mg_connection *conn, const char *path)
 
static void delete_file (struct mg_connection *conn, const char *path)
 
static void send_ssi_file (struct mg_connection *, const char *, struct mg_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 int mg_fgetc (struct mg_file *filep)
 
static void handle_ssi_file_request (struct mg_connection *conn, const char *path, struct mg_file *filep)
 
static void send_options (struct mg_connection *conn)
 
static int print_props (struct mg_connection *conn, const char *uri, const char *name, struct mg_file_stat *filep)
 
static int print_dav_dir_entry (struct de *de, void *data)
 
static void handle_propfind (struct mg_connection *conn, const char *path, struct mg_file_stat *filep)
 
static void dav_lock_file (struct mg_connection *conn, const char *path)
 
static void dav_unlock_file (struct mg_connection *conn, const char *path)
 
static void dav_proppatch (struct mg_connection *conn, const char *path)
 
CIVETWEB_API void mg_lock_connection (struct mg_connection *conn)
 
CIVETWEB_API void mg_unlock_connection (struct mg_connection *conn)
 
CIVETWEB_API void mg_lock_context (struct mg_context *ctx)
 
CIVETWEB_API void mg_unlock_context (struct mg_context *ctx)
 
static int should_switch_to_protocol (const struct mg_connection *conn)
 
static int parse_match_net (const struct vec *vec, const union usa *sa, int no_strict)
 
static int set_throttle (const char *spec, const union usa *rsa, const char *uri)
 
static int get_first_ssl_listener_index (const struct mg_context *ctx)
 
static void get_host_from_request_info (struct vec *host, const struct mg_request_info *ri)
 
static int switch_domain_context (struct mg_connection *conn)
 
static void redirect_to_https_port (struct mg_connection *conn, int port)
 
static void mg_set_handler_type (struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx, const char *uri, int handler_type, int is_delete_request, mg_request_handler handler, struct mg_websocket_subprotocols *subprotocols, mg_websocket_connect_handler connect_handler, mg_websocket_ready_handler ready_handler, mg_websocket_data_handler data_handler, mg_websocket_close_handler close_handler, mg_authorization_handler auth_handler, void *cbdata)
 
CIVETWEB_API void mg_set_request_handler (struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata)
 
CIVETWEB_API void mg_set_websocket_handler (struct mg_context *ctx, const char *uri, mg_websocket_connect_handler connect_handler, mg_websocket_ready_handler ready_handler, mg_websocket_data_handler data_handler, mg_websocket_close_handler close_handler, void *cbdata)
 
CIVETWEB_API void mg_set_websocket_handler_with_subprotocols (struct mg_context *ctx, const char *uri, struct mg_websocket_subprotocols *subprotocols, mg_websocket_connect_handler connect_handler, mg_websocket_ready_handler ready_handler, mg_websocket_data_handler data_handler, mg_websocket_close_handler close_handler, void *cbdata)
 
CIVETWEB_API void mg_set_auth_handler (struct mg_context *ctx, const char *uri, mg_authorization_handler handler, void *cbdata)
 
static int get_request_handler (struct mg_connection *conn, int handler_type, mg_request_handler *handler, struct mg_websocket_subprotocols **subprotocols, mg_websocket_connect_handler *connect_handler, mg_websocket_ready_handler *ready_handler, mg_websocket_data_handler *data_handler, mg_websocket_close_handler *close_handler, mg_authorization_handler *auth_handler, void **cbdata, struct mg_handler_info **handler_info)
 
static int is_in_script_path (const struct mg_connection *conn, const char *path)
 
static void release_handler_ref (struct mg_connection *conn, struct mg_handler_info *handler_info)
 
static void close_all_listening_sockets (struct mg_context *ctx)
 
static int parse_port_string (const struct vec *vec, struct socket *so, int *ip_version)
 
static int is_ssl_port_used (const char *ports)
 
static int set_ports_option (struct mg_context *phys_ctx)
 
static const char * header_val (const struct mg_connection *conn, const char *header)
 
static int check_acl (struct mg_context *phys_ctx, const union usa *sa)
 
static int set_uid_option (struct mg_context *phys_ctx)
 
static void tls_dtor (void *key)
 
static int ssl_use_pem_file (struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx, const char *pem, const char *chain)
 
static const char * ssl_error (void)
 
static int refresh_trust (struct mg_connection *conn)
 
static int sslize (struct mg_connection *conn, int(*func)(SSL *), const struct mg_client_options *client_options)
 
static int hexdump2string (void *mem, int memlen, char *buf, int buflen)
 
static int ssl_get_client_cert_info (const struct mg_connection *conn, struct mg_client_cert *client_cert)
 
static void ssl_locking_callback (int mode, int mutex_num, const char *file, int line)
 
static void * load_tls_dll (char *ebuf, size_t ebuf_len, const char *dll_name, struct ssl_func *sw, int *feature_missing)
 
static int initialize_openssl (char *ebuf, size_t ebuf_len)
 
static long ssl_get_protocol (int version_id)
 
static void ssl_info_callback (const SSL *ssl, int what, int ret)
 
static int ssl_servername_callback (SSL *ssl, int *ad, void *arg)
 
static int init_ssl_ctx_impl (struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx, const char *pem, const char *chain)
 
static int init_ssl_ctx (struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
 
static void uninitialize_openssl (void)
 
static int set_gpass_option (struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
 
static int set_acl_option (struct mg_context *phys_ctx)
 
static void reset_per_request_attributes (struct mg_connection *conn)
 
static int set_tcp_nodelay (const struct socket *so, int nodelay_on)
 
static void close_socket_gracefully (struct mg_connection *conn)
 
static void close_connection (struct mg_connection *conn)
 
CIVETWEB_API void mg_close_connection (struct mg_connection *conn)
 
static struct mg_connectionmg_connect_client_impl (const struct mg_client_options *client_options, int use_ssl, struct mg_init_data *init, struct mg_error_data *error)
 
CIVETWEB_API struct mg_connectionmg_connect_client_secure (const struct mg_client_options *client_options, char *error_buffer, size_t error_buffer_size)
 
CIVETWEB_API struct mg_connectionmg_connect_client (const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size)
 
static int get_message (struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
 
static int get_request (struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
 
static int get_response (struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
 
CIVETWEB_API int mg_get_response (struct mg_connection *conn, char *ebuf, size_t ebuf_len, int timeout)
 
CIVETWEB_API struct mg_connectionmg_download (const char *host, int port, int use_ssl, char *ebuf, size_t ebuf_len, const char *fmt,...)
 
static struct mg_connectionmg_connect_websocket_client_impl (const struct mg_client_options *client_options, int use_ssl, char *error_buffer, size_t error_buffer_size, const char *path, const char *origin, const char *extensions, mg_websocket_data_handler data_func, mg_websocket_close_handler close_func, void *user_data)
 
CIVETWEB_API struct mg_connectionmg_connect_websocket_client (const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size, const char *path, const char *origin, mg_websocket_data_handler data_func, mg_websocket_close_handler close_func, void *user_data)
 
CIVETWEB_API struct mg_connectionmg_connect_websocket_client_secure (const struct mg_client_options *client_options, char *error_buffer, size_t error_buffer_size, const char *path, const char *origin, mg_websocket_data_handler data_func, mg_websocket_close_handler close_func, void *user_data)
 
CIVETWEB_API struct mg_connectionmg_connect_websocket_client_extensions (const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size, const char *path, const char *origin, const char *extensions, mg_websocket_data_handler data_func, mg_websocket_close_handler close_func, void *user_data)
 
CIVETWEB_API struct mg_connectionmg_connect_websocket_client_secure_extensions (const struct mg_client_options *client_options, char *error_buffer, size_t error_buffer_size, const char *path, const char *origin, const char *extensions, mg_websocket_data_handler data_func, mg_websocket_close_handler close_func, void *user_data)
 
static void init_connection (struct mg_connection *conn)
 
static void process_new_connection (struct mg_connection *conn)
 
static int consume_socket (struct mg_context *ctx, struct socket *sp, int thread_index)
 
static void produce_socket (struct mg_context *ctx, const struct socket *sp)
 
static void worker_thread_run (struct mg_connection *conn)
 
static void * worker_thread (void *thread_func_param)
 
static void accept_new_connection (const struct socket *listener, struct mg_context *ctx)
 
static void master_thread_run (struct mg_context *ctx)
 
static void * master_thread (void *thread_func_param)
 
static void free_context (struct mg_context *ctx)
 
CIVETWEB_API void mg_stop (struct mg_context *ctx)
 
static void get_system_name (char **sysName)
 
static void legacy_init (const char **options)
 
CIVETWEB_API struct mg_contextmg_start2 (struct mg_init_data *init, struct mg_error_data *error)
 
CIVETWEB_API struct mg_contextmg_start (const struct mg_callbacks *callbacks, void *user_data, const char **options)
 
CIVETWEB_API int mg_start_domain2 (struct mg_context *ctx, const char **options, struct mg_error_data *error)
 
CIVETWEB_API int mg_start_domain (struct mg_context *ctx, const char **options)
 
CIVETWEB_API unsigned mg_check_feature (unsigned feature)
 
static size_t mg_str_append (char **dst, char *end, const char *src)
 
CIVETWEB_API int mg_get_system_info (char *buffer, int buflen)
 
CIVETWEB_API int mg_get_context_info (const struct mg_context *ctx, char *buffer, int buflen)
 
CIVETWEB_API void mg_disable_connection_keep_alive (struct mg_connection *conn)
 
CIVETWEB_API unsigned mg_init_library (unsigned features)
 
CIVETWEB_API unsigned mg_exit_library (void)
 

Variables

char static_assert_replacement [1]
 
static pthread_mutexattr_t pthread_mutex_attr
 
static pthread_mutex_t global_lock_mutex
 
static int mg_init_library_called = 0
 
static pthread_key_t sTlsKey
 
static volatile ptrdiff_t thread_idx_max = 0
 
static const char month_names [][4]
 
static const struct mg_option config_options []
 
struct { 
 
   const char *   extension 
 
   size_t   ext_len 
 
   const char *   mime_type 
 
builtin_mime_types [] 
 
static const struct mg_http_method_info http_methods []
 
static char * all_methods = NULL
 
static pthread_mutex_t * ssl_mutexes
 
static void * ssllib_dll_handle
 
static void * cryptolib_dll_handle
 
static volatile ptrdiff_t cryptolib_users
 
struct { 
 
   const char *   proto 
 
   size_t   proto_len 
 
   unsigned   default_port 
 
abs_uri_protocols [] 
 

Macro Definition Documentation

◆ __STDC_FORMAT_MACROS

#define __STDC_FORMAT_MACROS   /* <inttypes.h> wants this for C++ */

Definition at line 75 of file civetweb.c.

◆ __STDC_LIMIT_MACROS

#define __STDC_LIMIT_MACROS   /* C++ wants that for INT64_MAX */

Definition at line 78 of file civetweb.c.

◆ _DARWIN_UNLIMITED_SELECT

#define _DARWIN_UNLIMITED_SELECT

Definition at line 81 of file civetweb.c.

◆ _FILE_OFFSET_BITS

#define _FILE_OFFSET_BITS   64 /* Use 64-bit file offsets by default */

Definition at line 72 of file civetweb.c.

◆ _GNU_SOURCE

#define _GNU_SOURCE   /* for setgroups(), pthread_setname_np() */

Definition at line 59 of file civetweb.c.

◆ _LARGEFILE_SOURCE

#define _LARGEFILE_SOURCE   /* For fseeko(), ftello() */

Definition at line 69 of file civetweb.c.

◆ ARRAY_SIZE

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

Definition at line 506 of file civetweb.c.

Referenced by dav_move_file(), get_month_index(), and mg_fopen().

◆ calloc

#define calloc   DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc

Definition at line 1540 of file civetweb.c.

Referenced by mg_calloc(), and Tokenizer_new().

◆ CGI_ENVIRONMENT_SIZE

#define CGI_ENVIRONMENT_SIZE   (4096) /* in bytes */

Definition at line 488 of file civetweb.c.

Referenced by addenv(), and prepare_cgi_environment().

◆ closesocket

◆ CRYPTO_LIB

#define CRYPTO_LIB   "libcrypto.so"

Definition at line 911 of file civetweb.c.

Referenced by initialize_openssl().

◆ DEBUG_ASSERT

◆ DEBUG_TRACE

◆ ERRNO

◆ ERROR_TRY_AGAIN

#define ERROR_TRY_AGAIN ( err)     (((err) == EAGAIN) || ((err) == EWOULDBLOCK) || ((err) == EINTR))

Definition at line 447 of file civetweb.c.

447#define ERROR_TRY_AGAIN(err) \
448 (((err) == EAGAIN) || ((err) == EWOULDBLOCK) || ((err) == EINTR))

Referenced by mg_poll(), pull_inner(), and push_inner().

◆ free

◆ FUNCTION_MAY_BE_UNUSED

#define FUNCTION_MAY_BE_UNUSED

Definition at line 316 of file civetweb.c.

◆ HEXTOI

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

◆ HTTP1_only

#define HTTP1_only

Definition at line 6609 of file civetweb.c.

Referenced by handle_request().

◆ IGNORE_UNUSED_RESULT

#define IGNORE_UNUSED_RESULT ( a)    ((void)((a) && 1))

◆ INITIAL_DEPTH

#define INITIAL_DEPTH   9

Definition at line 8715 of file civetweb.c.

Referenced by authorize().

◆ INT64_FMT

◆ INT64_MAX

#define INT64_MAX   (9223372036854775807)

◆ INVALID_SOCKET

◆ IP_ADDR_STR_LEN

#define IP_ADDR_STR_LEN   (50) /* IPv6 hex string is 46 chars */

◆ LOCK_DURATION_S

#define LOCK_DURATION_S   60

Definition at line 2333 of file civetweb.c.

Referenced by dav_lock_file(), and print_props().

◆ malloc

◆ MAX_CGI_ENVIR_VARS

#define MAX_CGI_ENVIR_VARS   (256) /* in variables (count) */

Definition at line 493 of file civetweb.c.

Referenced by prepare_cgi_environment().

◆ MAX_WORKER_THREADS

#define MAX_WORKER_THREADS   (1024 * 64) /* in threads (count) */

Definition at line 465 of file civetweb.c.

Referenced by mg_start2().

◆ MD5_STATIC

#define MD5_STATIC   static

Definition at line 1724 of file civetweb.c.

◆ MG_BUF_LEN

◆ mg_calloc_ctx

#define mg_calloc_ctx ( a,
b,
c )   mg_calloc(a, b)

◆ mg_closedir

#define mg_closedir ( x)    (closedir(x))

Definition at line 922 of file civetweb.c.

Referenced by remove_directory(), and scan_directory().

◆ mg_cry

#define mg_cry   DO_NOT_USE_THIS_FUNCTION__USE_mg_cry_internal

Definition at line 3510 of file civetweb.c.

◆ mg_cry_ctx_internal

#define mg_cry_ctx_internal ( ctx,
fmt,
... )    mg_cry_internal_wrap(NULL, ctx, __func__, __LINE__, fmt, __VA_ARGS__)

Definition at line 2586 of file civetweb.c.

2586#define mg_cry_ctx_internal(ctx, fmt, ...) \
2587 mg_cry_internal_wrap(NULL, ctx, __func__, __LINE__, fmt, __VA_ARGS__)

Referenced by accept_new_connection(), check_acl(), init_ssl_ctx(), init_ssl_ctx_impl(), mg_set_handler_type(), mg_start2(), mg_start_domain2(), refresh_trust(), set_gpass_option(), set_ports_option(), set_uid_option(), ssl_use_pem_file(), and worker_thread_run().

◆ mg_cry_internal

#define mg_cry_internal ( conn,
fmt,
... )    mg_cry_internal_wrap(conn, NULL, __func__, __LINE__, fmt, __VA_ARGS__)

◆ MG_FILE_COMPRESSION_SIZE_LIMIT

#define MG_FILE_COMPRESSION_SIZE_LIMIT   (1024) /* in bytes */

Definition at line 478 of file civetweb.c.

Referenced by handle_static_file_request().

◆ MG_FOPEN_MODE_APPEND

#define MG_FOPEN_MODE_APPEND   (4)

Definition at line 2849 of file civetweb.c.

Referenced by log_access(), mg_cry_internal_impl(), and mg_fopen().

◆ MG_FOPEN_MODE_NONE

#define MG_FOPEN_MODE_NONE   (0)

Definition at line 2840 of file civetweb.c.

◆ MG_FOPEN_MODE_READ

◆ MG_FOPEN_MODE_WRITE

#define MG_FOPEN_MODE_WRITE   (2)

Definition at line 2846 of file civetweb.c.

Referenced by mg_fopen(), mg_store_body(), and put_file().

◆ mg_free_ctx

#define mg_free_ctx ( a,
c )   mg_free(a)

Definition at line 1500 of file civetweb.c.

◆ mg_get_option

#define mg_get_option   DO_NOT_USE_THIS_FUNCTION_INTERNALLY__access_directly

Definition at line 3185 of file civetweb.c.

Referenced by main().

◆ mg_malloc_ctx

◆ mg_mkdir

#define mg_mkdir ( conn,
path,
mode )   (mkdir(path, mode))

Definition at line 918 of file civetweb.c.

Referenced by dav_mkcol(), and put_dir().

◆ mg_opendir

#define mg_opendir ( conn,
x )   (opendir(x))

Definition at line 921 of file civetweb.c.

Referenced by remove_directory(), and scan_directory().

◆ mg_pollfd

#define mg_pollfd   pollfd

◆ mg_readdir

#define mg_readdir ( x)    (readdir(x))

Definition at line 923 of file civetweb.c.

Referenced by remove_directory(), and scan_directory().

◆ mg_realloc_ctx

#define mg_realloc_ctx ( a,
b,
c )   mg_realloc(a, b)

Definition at line 1499 of file civetweb.c.

Referenced by addenv(), and set_ports_option().

◆ mg_remove

#define mg_remove ( conn,
x )   (remove(x))

Definition at line 919 of file civetweb.c.

Referenced by delete_file(), remove_bad_file(), and remove_directory().

◆ mg_sleep

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

Definition at line 920 of file civetweb.c.

Referenced by mg_set_handler_type(), mg_stop(), push_inner(), and sslize().

◆ mg_static_assert

#define mg_static_assert ( cond,
txt )    extern char static_assert_replacement[(cond) ? 1 : -1]

Definition at line 126 of file civetweb.c.

126#define mg_static_assert(cond, txt) \
127 extern char static_assert_replacement[(cond) ? 1 : -1]

◆ MSG_NOSIGNAL

#define MSG_NOSIGNAL   (0)

Definition at line 1735 of file civetweb.c.

Referenced by push_inner(), socket_write(), and socket_write().

◆ NO_ALTERNATIVE_QUEUE

#define NO_ALTERNATIVE_QUEUE

Definition at line 152 of file civetweb.c.

◆ NUM_WEBDAV_LOCKS

#define NUM_WEBDAV_LOCKS   10

Definition at line 2330 of file civetweb.c.

Referenced by dav_lock_file(), dav_unlock_file(), and print_props().

◆ O_BINARY

#define O_BINARY   (0)

Definition at line 915 of file civetweb.c.

◆ PASSWORDS_FILE_NAME

#define PASSWORDS_FILE_NAME   ".htpasswd"

Definition at line 482 of file civetweb.c.

Referenced by must_hide_file(), and open_auth_file().

◆ realloc

◆ SHUTDOWN_BOTH

#define SHUTDOWN_BOTH   (2)

Definition at line 518 of file civetweb.c.

◆ SHUTDOWN_RD

#define SHUTDOWN_RD   (0)

Definition at line 516 of file civetweb.c.

◆ SHUTDOWN_WR

#define SHUTDOWN_WR   (1)

Definition at line 517 of file civetweb.c.

Referenced by close_socket_gracefully().

◆ snprintf

#define snprintf   DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf

◆ SOCKET_TIMEOUT_QUANTUM

#define SOCKET_TIMEOUT_QUANTUM   (2000) /* in ms */

Definition at line 473 of file civetweb.c.

Referenced by master_thread_run(), mg_poll(), and push_inner().

◆ SSL_LIB

#define SSL_LIB   "libssl.so"

Definition at line 908 of file civetweb.c.

Referenced by initialize_openssl().

◆ STOP_FLAG_ASSIGN

#define STOP_FLAG_ASSIGN ( f,
v )   ((*(f)) = (v))

Definition at line 2324 of file civetweb.c.

Referenced by connect_socket(), master_thread_run(), mg_close_connection(), and mg_stop().

◆ STOP_FLAG_IS_TWO

#define STOP_FLAG_IS_TWO ( f)    ((*(f)) == 2)

Definition at line 2323 of file civetweb.c.

Referenced by mg_stop().

◆ STOP_FLAG_IS_ZERO

◆ STRUCT_FILE_INITIALIZER

#define STRUCT_FILE_INITIALIZER
Value:
{ \
{(uint64_t)0, (time_t)0, 0, 0, 0}, \
{ \
(FILE *)NULL \
} \
}
#define NULL
Definition gmacros.h:924

Definition at line 1891 of file civetweb.c.

1891#define STRUCT_FILE_INITIALIZER \
1892 { \
1893 {(uint64_t)0, (time_t)0, 0, 0, 0}, \
1894 { \
1895 (FILE *)NULL \
1896 } \
1897 }

Referenced by check_authorization(), do_ssi_exec(), do_ssi_include(), handle_cgi_request(), handle_request(), is_authorized_for_put(), mg_check_digest_access_authentication(), mg_send_file_body(), mg_send_http_error_impl(), mg_send_mime_file2(), put_dir(), put_file(), and set_gpass_option().

◆ UINT64_FMT

#define UINT64_FMT   PRIu64

Definition at line 927 of file civetweb.c.

Referenced by dav_lock_file(), mg_send_http_ok(), and send_authorization_request().

◆ USA_IN_PORT_UNSAFE

#define USA_IN_PORT_UNSAFE ( s)    ((s)->sin.sin_port)

◆ UTF8_PATH_MAX

◆ va_copy

#define va_copy ( x,
y )   ((x) = (y))

Definition at line 1003 of file civetweb.c.

Referenced by alloc_vprintf(), alloc_vprintf2(), and mg_send_http_error_impl().

◆ vsnprintf_impl

#define vsnprintf_impl   vsnprintf

Definition at line 897 of file civetweb.c.

Referenced by alloc_vprintf(), alloc_vprintf2(), mg_cry_internal_impl(), and mg_vsnprintf().

◆ WIN32_LEAN_AND_MEAN

#define WIN32_LEAN_AND_MEAN

Definition at line 175 of file civetweb.c.

◆ WINCDECL

#define WINCDECL

Definition at line 929 of file civetweb.c.

Typedef Documentation

◆ SOCK_OPT_TYPE

typedef const void* SOCK_OPT_TYPE

Definition at line 863 of file civetweb.c.

◆ SOCKET

typedef int SOCKET

Definition at line 928 of file civetweb.c.

◆ stop_flag_t

typedef int volatile stop_flag_t

Definition at line 2321 of file civetweb.c.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
LISTENING_PORTS 
NUM_THREADS 
RUN_AS_USER 
CONFIG_TCP_NODELAY 
MAX_REQUEST_SIZE 
LINGER_TIMEOUT 
CONNECTION_QUEUE_SIZE 
LISTEN_BACKLOG_SIZE 
THROTTLE 
ENABLE_KEEP_ALIVE 
REQUEST_TIMEOUT 
KEEP_ALIVE_TIMEOUT 
DECODE_URL 
DECODE_QUERY_STRING 
DOCUMENT_ROOT 
ACCESS_LOG_FILE 
ERROR_LOG_FILE 
CGI_EXTENSIONS 
CGI_ENVIRONMENT 
CGI_INTERPRETER 
CGI_INTERPRETER_ARGS 
CGI_BUFFERING 
CGI2_EXTENSIONS 
CGI2_ENVIRONMENT 
CGI2_INTERPRETER 
CGI2_INTERPRETER_ARGS 
CGI2_BUFFERING 
PUT_DELETE_PASSWORDS_FILE 
PROTECT_URI 
AUTHENTICATION_DOMAIN 
ENABLE_AUTH_DOMAIN_CHECK 
SSI_EXTENSIONS 
ENABLE_DIRECTORY_LISTING 
ENABLE_WEBDAV 
GLOBAL_PASSWORDS_FILE 
INDEX_FILES 
ACCESS_CONTROL_LIST 
EXTRA_MIME_TYPES 
SSL_CERTIFICATE 
SSL_CERTIFICATE_CHAIN 
URL_REWRITE_PATTERN 
HIDE_FILES 
SSL_DO_VERIFY_PEER 
SSL_CACHE_TIMEOUT 
SSL_CA_PATH 
SSL_CA_FILE 
SSL_VERIFY_DEPTH 
SSL_DEFAULT_VERIFY_PATHS 
SSL_CIPHER_LIST 
SSL_PROTOCOL_VERSION 
SSL_SHORT_TRUST 
ACCESS_CONTROL_ALLOW_ORIGIN 
ACCESS_CONTROL_ALLOW_METHODS 
ACCESS_CONTROL_ALLOW_HEADERS 
ERROR_PAGES 
STATIC_FILE_MAX_AGE 
STATIC_FILE_CACHE_CONTROL 
STRICT_HTTPS_MAX_AGE 
ADDITIONAL_HEADER 
ALLOW_INDEX_SCRIPT_SUB_RES 
NUM_OPTIONS 

Definition at line 1918 of file civetweb.c.

1918 {
1919 /* Once for each server */
1923 CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
1924 * socket option typedef TCP_NODELAY. */
1929#if defined(__linux__)
1930 ALLOW_SENDFILE_CALL,
1931#endif
1932#if defined(_WIN32)
1933 CASE_SENSITIVE_FILES,
1934#endif
1935 THROTTLE,
1939#if defined(USE_WEBSOCKET)
1940 WEBSOCKET_TIMEOUT,
1941 ENABLE_WEBSOCKET_PING_PONG,
1942#endif
1943 DECODE_URL,
1945#if defined(USE_LUA)
1946 LUA_BACKGROUND_SCRIPT,
1947 LUA_BACKGROUND_SCRIPT_PARAMS,
1948#endif
1949#if defined(USE_HTTP2)
1950 ENABLE_HTTP2,
1951#endif
1952
1953 /* Once for each domain */
1955
1958
1963#if defined(USE_TIMERS)
1964 CGI_TIMEOUT,
1965#endif
1967
1972#if defined(USE_TIMERS)
1973 CGI2_TIMEOUT,
1974#endif
1976
1977#if defined(USE_4_CGI)
1978 CGI3_EXTENSIONS,
1979 CGI3_ENVIRONMENT,
1980 CGI3_INTERPRETER,
1981 CGI3_INTERPRETER_ARGS,
1982#if defined(USE_TIMERS)
1983 CGI3_TIMEOUT,
1984#endif
1985 CGI3_BUFFERING,
1986
1987 CGI4_EXTENSIONS,
1988 CGI4_ENVIRONMENT,
1989 CGI4_INTERPRETER,
1990 CGI4_INTERPRETER_ARGS,
1991#if defined(USE_TIMERS)
1992 CGI4_TIMEOUT,
1993#endif
1994 CGI4_BUFFERING,
1995#endif
1996
1997 PUT_DELETE_PASSWORDS_FILE, /* must follow CGI_* */
2011 HIDE_FILES,
2021
2022#if defined(USE_LUA)
2023 LUA_PRELOAD_FILE,
2024 LUA_SCRIPT_EXTENSIONS,
2025 LUA_SERVER_PAGE_EXTENSIONS,
2026#if defined(MG_EXPERIMENTAL_INTERFACES)
2027 LUA_DEBUG_PARAMS,
2028#endif
2029#endif
2030#if defined(USE_DUKTAPE)
2031 DUKTAPE_SCRIPT_EXTENSIONS,
2032#endif
2033
2034#if defined(USE_WEBSOCKET)
2035 WEBSOCKET_ROOT,
2036#endif
2037#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2038 LUA_WEBSOCKET_EXTENSIONS,
2039#endif
2040
2045#if !defined(NO_CACHING)
2048#endif
2049#if !defined(NO_SSL)
2051#endif
2054
2056};
@ ENABLE_DIRECTORY_LISTING
Definition civetweb.c:2002
@ SSL_SHORT_TRUST
Definition civetweb.c:2020
@ GLOBAL_PASSWORDS_FILE
Definition civetweb.c:2004
@ ADDITIONAL_HEADER
Definition civetweb.c:2052
@ SSL_VERIFY_DEPTH
Definition civetweb.c:2016
@ ACCESS_CONTROL_ALLOW_ORIGIN
Definition civetweb.c:2041
@ SSL_PROTOCOL_VERSION
Definition civetweb.c:2019
@ SSL_DO_VERIFY_PEER
Definition civetweb.c:2012
@ SSL_CERTIFICATE
Definition civetweb.c:2008
@ RUN_AS_USER
Definition civetweb.c:1922
@ ALLOW_INDEX_SCRIPT_SUB_RES
Definition civetweb.c:2053
@ CGI2_BUFFERING
Definition civetweb.c:1975
@ SSL_CACHE_TIMEOUT
Definition civetweb.c:2013
@ CONNECTION_QUEUE_SIZE
Definition civetweb.c:1927
@ ENABLE_KEEP_ALIVE
Definition civetweb.c:1936
@ ACCESS_CONTROL_LIST
Definition civetweb.c:2006
@ CGI_INTERPRETER_ARGS
Definition civetweb.c:1962
@ SSL_CA_PATH
Definition civetweb.c:2014
@ AUTHENTICATION_DOMAIN
Definition civetweb.c:1999
@ ACCESS_CONTROL_ALLOW_HEADERS
Definition civetweb.c:2043
@ SSL_DEFAULT_VERIFY_PATHS
Definition civetweb.c:2017
@ CGI2_EXTENSIONS
Definition civetweb.c:1968
@ ENABLE_WEBDAV
Definition civetweb.c:2003
@ ACCESS_CONTROL_ALLOW_METHODS
Definition civetweb.c:2042
@ STRICT_HTTPS_MAX_AGE
Definition civetweb.c:2050
@ HIDE_FILES
Definition civetweb.c:2011
@ ERROR_LOG_FILE
Definition civetweb.c:1957
@ CGI2_INTERPRETER_ARGS
Definition civetweb.c:1971
@ STATIC_FILE_MAX_AGE
Definition civetweb.c:2046
@ LINGER_TIMEOUT
Definition civetweb.c:1926
@ URL_REWRITE_PATTERN
Definition civetweb.c:2010
@ THROTTLE
Definition civetweb.c:1935
@ SSL_CIPHER_LIST
Definition civetweb.c:2018
@ KEEP_ALIVE_TIMEOUT
Definition civetweb.c:1938
@ CGI_EXTENSIONS
Definition civetweb.c:1959
@ STATIC_FILE_CACHE_CONTROL
Definition civetweb.c:2047
@ ERROR_PAGES
Definition civetweb.c:2044
@ CGI_ENVIRONMENT
Definition civetweb.c:1960
@ PUT_DELETE_PASSWORDS_FILE
Definition civetweb.c:1997
@ ENABLE_AUTH_DOMAIN_CHECK
Definition civetweb.c:2000
@ NUM_OPTIONS
Definition civetweb.c:2055
@ MAX_REQUEST_SIZE
Definition civetweb.c:1925
@ DECODE_URL
Definition civetweb.c:1943
@ NUM_THREADS
Definition civetweb.c:1921
@ DOCUMENT_ROOT
Definition civetweb.c:1954
@ SSL_CERTIFICATE_CHAIN
Definition civetweb.c:2009
@ CGI2_ENVIRONMENT
Definition civetweb.c:1969
@ CGI_BUFFERING
Definition civetweb.c:1966
@ PROTECT_URI
Definition civetweb.c:1998
@ ACCESS_LOG_FILE
Definition civetweb.c:1956
@ REQUEST_TIMEOUT
Definition civetweb.c:1937
@ SSI_EXTENSIONS
Definition civetweb.c:2001
@ CONFIG_TCP_NODELAY
Definition civetweb.c:1923
@ SSL_CA_FILE
Definition civetweb.c:2015
@ CGI_INTERPRETER
Definition civetweb.c:1961
@ LISTEN_BACKLOG_SIZE
Definition civetweb.c:1928
@ DECODE_QUERY_STRING
Definition civetweb.c:1944
@ LISTENING_PORTS
Definition civetweb.c:1920
@ CGI2_INTERPRETER
Definition civetweb.c:1970
@ INDEX_FILES
Definition civetweb.c:2005
@ EXTRA_MIME_TYPES
Definition civetweb.c:2007

◆ anonymous enum

anonymous enum
Enumerator
REQUEST_HANDLER 
WEBSOCKET_HANDLER 
AUTH_HANDLER 

Definition at line 2225 of file civetweb.c.

@ AUTH_HANDLER
Definition civetweb.c:2225
@ REQUEST_HANDLER
Definition civetweb.c:2225
@ WEBSOCKET_HANDLER
Definition civetweb.c:2225

◆ anonymous enum

anonymous enum
Enumerator
CONTEXT_INVALID 
CONTEXT_SERVER 
CONTEXT_HTTP_CLIENT 
CONTEXT_WS_CLIENT 

Definition at line 2261 of file civetweb.c.

2261 {
2266};
@ CONTEXT_WS_CLIENT
Definition civetweb.c:2265
@ CONTEXT_INVALID
Definition civetweb.c:2262
@ CONTEXT_SERVER
Definition civetweb.c:2263
@ CONTEXT_HTTP_CLIENT
Definition civetweb.c:2264

◆ anonymous enum

anonymous enum
Enumerator
CONNECTION_TYPE_INVALID 
CONNECTION_TYPE_REQUEST 
CONNECTION_TYPE_RESPONSE 

Definition at line 2459 of file civetweb.c.

2459 {
2463};
@ CONNECTION_TYPE_RESPONSE
Definition civetweb.c:2462
@ CONNECTION_TYPE_INVALID
Definition civetweb.c:2460
@ CONNECTION_TYPE_REQUEST
Definition civetweb.c:2461

◆ anonymous enum

anonymous enum
Enumerator
PROTOCOL_TYPE_HTTP1 
PROTOCOL_TYPE_WEBSOCKET 
PROTOCOL_TYPE_HTTP2 

Definition at line 2465 of file civetweb.c.

2465 {
2469};
@ PROTOCOL_TYPE_HTTP1
Definition civetweb.c:2466
@ PROTOCOL_TYPE_HTTP2
Definition civetweb.c:2468
@ PROTOCOL_TYPE_WEBSOCKET
Definition civetweb.c:2467

Function Documentation

◆ abort_cgi_process()

static int abort_cgi_process ( void * data)
static

Definition at line 11552 of file civetweb.c.

11553{
11554 /* Waitpid checks for child status and won't work for a pid that does
11555 * not identify a child of the current process. Thus, if the pid is
11556 * reused, we will not affect a different process. */
11557 struct process_control_data *proc = (struct process_control_data *)data;
11558 int status = 0;
11559 ptrdiff_t refs;
11560 pid_t ret_pid;
11561
11562 ret_pid = waitpid(proc->pid, &status, WNOHANG);
11563 if ((ret_pid != (pid_t)-1) && (status == 0)) {
11564 /* Stop child process */
11565 DEBUG_TRACE("CGI timer: Stop child process %d\n", proc->pid);
11566 kill(proc->pid, SIGABRT);
11567
11568 /* Wait until process is terminated (don't leave zombies) */
11569 while (waitpid(proc->pid, &status, 0) != (pid_t)-1) /* nop */
11570 ;
11571 } else {
11572 DEBUG_TRACE("CGI timer: Child process %d already stopped\n", proc->pid);
11573 }
11574 /* Dec reference counter */
11575 refs = mg_atomic_dec(&proc->references);
11576 if (refs == 0) {
11577 /* no more references - free data */
11578 mg_free(data);
11579 }
11580
11581 return 0;
11582}
static FUNCTION_MAY_BE_UNUSED ptrdiff_t mg_atomic_dec(volatile ptrdiff_t *addr)
Definition civetweb.c:1144
#define DEBUG_TRACE(fmt,...)
Definition civetweb.c:242
static __inline void mg_free(void *a)
Definition civetweb.c:1492

References DEBUG_TRACE, mg_atomic_dec(), mg_free(), process_control_data::pid, and process_control_data::references.

Referenced by handle_cgi_request().

◆ accept_new_connection()

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

Definition at line 19957 of file civetweb.c.

19958{
19959 struct socket so;
19960 char src_addr[IP_ADDR_STR_LEN];
19961 socklen_t len = sizeof(so.rsa);
19962#if !defined(__ZEPHYR__)
19963 int on = 1;
19964#endif
19965 memset(&so, 0, sizeof(so));
19966
19967 if ((so.sock = accept(listener->sock, &so.rsa.sa, &len))
19968 == INVALID_SOCKET) {
19969 } else if (check_acl(ctx, &so.rsa) != 1) {
19970 sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa);
19972 "%s: %s is not allowed to connect",
19973 __func__,
19974 src_addr);
19975 closesocket(so.sock);
19976 } else {
19977 /* Put so socket structure into the queue */
19978 DEBUG_TRACE("Accepted socket %d", (int)so.sock);
19979 set_close_on_exec(so.sock, NULL, ctx);
19980 so.is_ssl = listener->is_ssl;
19981 so.ssl_redir = listener->ssl_redir;
19982 if (getsockname(so.sock, &so.lsa.sa, &len) != 0) {
19984 "%s: getsockname() failed: %s",
19985 __func__,
19986 strerror(ERRNO));
19987 }
19988
19989#if !defined(__ZEPHYR__)
19990 if ((so.lsa.sa.sa_family == AF_INET)
19991 || (so.lsa.sa.sa_family == AF_INET6)) {
19992 /* Set TCP keep-alive for TCP sockets (IPv4 and IPv6).
19993 * This is needed because if HTTP-level keep-alive
19994 * is enabled, and client resets the connection, server won't get
19995 * TCP FIN or RST and will keep the connection open forever. With
19996 * TCP keep-alive, next keep-alive handshake will figure out that
19997 * the client is down and will close the server end.
19998 * Thanks to Igor Klopov who suggested the patch. */
19999 if (setsockopt(so.sock,
20000 SOL_SOCKET,
20001 SO_KEEPALIVE,
20002 (SOCK_OPT_TYPE)&on,
20003 sizeof(on))
20004 != 0) {
20006 ctx,
20007 "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s",
20008 __func__,
20009 strerror(ERRNO));
20010 }
20011 }
20012#endif
20013
20014 /* Disable TCP Nagle's algorithm. Normally TCP packets are coalesced
20015 * to effectively fill up the underlying IP packet payload and
20016 * reduce the overhead of sending lots of small buffers. However
20017 * this hurts the server's throughput (ie. operations per second)
20018 * when HTTP 1.1 persistent connections are used and the responses
20019 * are relatively small (eg. less than 1400 bytes).
20020 */
20021 if ((ctx->dd.config[CONFIG_TCP_NODELAY] != NULL)
20022 && (!strcmp(ctx->dd.config[CONFIG_TCP_NODELAY], "1"))) {
20023 if (set_tcp_nodelay(&so, 1) != 0) {
20025 ctx,
20026 "%s: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s",
20027 __func__,
20028 strerror(ERRNO));
20029 }
20030 }
20031
20032 /* The "non blocking" property should already be
20033 * inherited from the parent socket. Set it for
20034 * non-compliant socket implementations. */
20035 set_non_blocking_mode(so.sock);
20036
20037 so.in_use = 0;
20038 produce_socket(ctx, &so);
20039 }
20040}
static void sockaddr_to_string(char *buf, size_t len, const union usa *usa)
Definition civetweb.c:3292
#define IP_ADDR_STR_LEN
Definition civetweb.c:1732
static int set_non_blocking_mode(SOCKET sock)
Definition civetweb.c:5876
const void * SOCK_OPT_TYPE
Definition civetweb.c:863
#define mg_cry_ctx_internal(ctx, fmt,...)
Definition civetweb.c:2586
#define INVALID_SOCKET
Definition civetweb.c:925
#define ERRNO
Definition civetweb.c:924
#define closesocket(a)
Definition civetweb.c:917
static int check_acl(struct mg_context *phys_ctx, const union usa *sa)
Definition civetweb.c:16229
static int set_tcp_nodelay(const struct socket *so, int nodelay_on)
Definition civetweb.c:17718
static void produce_socket(struct mg_context *ctx, const struct socket *sp)
Definition civetweb.c:19661
static void set_close_on_exec(int fd, const struct mg_connection *conn, struct mg_context *ctx)
Definition civetweb.c:5677
struct mg_domain_context dd
Definition civetweb.c:2442
char * config[NUM_OPTIONS]
Definition civetweb.c:2271
unsigned char is_ssl
Definition civetweb.c:1906
unsigned char ssl_redir
Definition civetweb.c:1907
SOCKET sock
Definition civetweb.c:1903

References check_acl(), closesocket, mg_domain_context::config, CONFIG_TCP_NODELAY, mg_context::dd, DEBUG_TRACE, ERRNO, socket::in_use, INVALID_SOCKET, IP_ADDR_STR_LEN, socket::is_ssl, socket::lsa, mg_cry_ctx_internal, NULL, produce_socket(), socket::rsa, usa::sa, set_close_on_exec(), set_non_blocking_mode(), set_tcp_nodelay(), socket::sock, sockaddr_to_string(), and socket::ssl_redir.

Referenced by master_thread_run().

◆ addenv() [1/2]

static void static void addenv ( struct cgi_environment * env,
const char * fmt,
... )
static

Definition at line 11284 of file civetweb.c.

11285{
11286 size_t i, n, space;
11287 int truncated = 0;
11288 char *added;
11289 va_list ap;
11290
11291 if ((env->varlen - env->varused) < 2) {
11292 mg_cry_internal(env->conn,
11293 "%s: Cannot register CGI variable [%s]",
11294 __func__,
11295 fmt);
11296 return;
11297 }
11298
11299 /* Calculate how much space is left in the buffer */
11300 space = (env->buflen - env->bufused);
11301
11302 do {
11303 /* Space for "\0\0" is always needed. */
11304 if (space <= 2) {
11305 /* Allocate new buffer */
11306 n = env->buflen + CGI_ENVIRONMENT_SIZE;
11307 added = (char *)mg_realloc_ctx(env->buf, n, env->conn->phys_ctx);
11308 if (!added) {
11309 /* Out of memory */
11311 env->conn,
11312 "%s: Cannot allocate memory for CGI variable [%s]",
11313 __func__,
11314 fmt);
11315 return;
11316 }
11317 /* Retarget pointers */
11318 env->buf = added;
11319 env->buflen = n;
11320 for (i = 0, n = 0; i < env->varused; i++) {
11321 env->var[i] = added + n;
11322 n += strlen(added + n) + 1;
11323 }
11324 space = (env->buflen - env->bufused);
11325 }
11326
11327 /* Make a pointer to the free space int the buffer */
11328 added = env->buf + env->bufused;
11329
11330 /* Copy VARIABLE=VALUE\0 string into the free space */
11331 va_start(ap, fmt);
11332 mg_vsnprintf(env->conn, &truncated, added, space - 1, fmt, ap);
11333 va_end(ap);
11334
11335 /* Do not add truncated strings to the environment */
11336 if (truncated) {
11337 /* Reallocate the buffer */
11338 space = 0;
11339 }
11340 } while (truncated);
11341
11342 /* Calculate number of bytes added to the environment */
11343 n = strlen(added) + 1;
11344 env->bufused += n;
11345
11346 /* Append a pointer to the added string into the envp array */
11347 env->var[env->varused] = added;
11348 env->varused++;
11349}
#define CGI_ENVIRONMENT_SIZE
Definition civetweb.c:488
#define mg_cry_internal(conn, fmt,...)
Definition civetweb.c:2583
#define mg_realloc_ctx(a, b, c)
Definition civetweb.c:1499
static void mg_vsnprintf(const struct mg_connection *conn, int *truncated, char *buf, size_t buflen, const char *fmt, va_list ap)
Definition civetweb.c:3094
struct mg_connection * conn
Definition civetweb.c:11265
struct mg_context * phys_ctx
Definition civetweb.c:2497

References CGI_ENVIRONMENT_SIZE, mg_cry_internal, mg_realloc_ctx, and mg_vsnprintf().

◆ addenv() [2/2]

static void addenv ( struct cgi_environment * env,
PRINTF_FORMAT_STRING(const char *fmt) ,
... )
static

Referenced by prepare_cgi_environment().

◆ alloc_printf()

static int alloc_printf ( char ** out_buf,
const char * fmt,
... )
static

Definition at line 6996 of file civetweb.c.

6997{
6998 va_list ap;
6999 int result;
7000
7001 va_start(ap, fmt);
7002 result = alloc_vprintf(out_buf, NULL, 0, fmt, ap);
7003 va_end(ap);
7004
7005 return result;
7006}
static int alloc_vprintf(char **out_buf, char *prealloc_buf, size_t prealloc_size, const char *fmt, va_list ap)
Definition civetweb.c:6939

References alloc_vprintf(), and NULL.

◆ alloc_vprintf()

static int alloc_vprintf ( char ** out_buf,
char * prealloc_buf,
size_t prealloc_size,
const char * fmt,
va_list ap )
static

Definition at line 6939 of file civetweb.c.

6944{
6945 va_list ap_copy;
6946 int len;
6947
6948 /* Windows is not standard-compliant, and vsnprintf() returns -1 if
6949 * buffer is too small. Also, older versions of msvcrt.dll do not have
6950 * _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
6951 * Therefore, we make two passes: on first pass, get required message
6952 * length.
6953 * On second pass, actually print the message. */
6954 va_copy(ap_copy, ap);
6955 len = vsnprintf_impl(NULL, 0, fmt, ap_copy);
6956 va_end(ap_copy);
6957
6958 if (len < 0) {
6959 /* C runtime is not standard compliant, vsnprintf() returned -1.
6960 * Switch to alternative code path that uses incremental
6961 * allocations.
6962 */
6963 va_copy(ap_copy, ap);
6964 len = alloc_vprintf2(out_buf, fmt, ap_copy);
6965 va_end(ap_copy);
6966
6967 } else if ((size_t)(len) >= prealloc_size) {
6968 /* The pre-allocated buffer not large enough. */
6969 /* Allocate a new buffer. */
6970 *out_buf = (char *)mg_malloc((size_t)(len) + 1);
6971 if (!*out_buf) {
6972 /* Allocation failed. Return -1 as "out of memory" error. */
6973 return -1;
6974 }
6975 /* Buffer allocation successful. Store the string there. */
6976 va_copy(ap_copy, ap);
6978 vsnprintf_impl(*out_buf, (size_t)(len) + 1, fmt, ap_copy));
6979 va_end(ap_copy);
6980
6981 } else {
6982 /* The pre-allocated buffer is large enough.
6983 * Use it to store the string and return the address. */
6984 va_copy(ap_copy, ap);
6986 vsnprintf_impl(prealloc_buf, prealloc_size, fmt, ap_copy));
6987 va_end(ap_copy);
6988 *out_buf = prealloc_buf;
6989 }
6990
6991 return len;
6992}
#define vsnprintf_impl
Definition civetweb.c:897
static __inline void * mg_malloc(size_t a)
Definition civetweb.c:1474
static int alloc_vprintf2(char **buf, const char *fmt, va_list ap)
Definition civetweb.c:6906
#define va_copy(x, y)
Definition civetweb.c:1003
#define IGNORE_UNUSED_RESULT(a)
Definition civetweb.c:291

References alloc_vprintf2(), IGNORE_UNUSED_RESULT, mg_malloc(), NULL, va_copy, and vsnprintf_impl.

Referenced by alloc_printf(), and mg_vprintf().

◆ alloc_vprintf2()

static int alloc_vprintf2 ( char ** buf,
const char * fmt,
va_list ap )
static

Definition at line 6906 of file civetweb.c.

6907{
6908 va_list ap_copy;
6909 size_t size = MG_BUF_LEN / 4;
6910 int len = -1;
6911
6912 *buf = NULL;
6913 while (len < 0) {
6914 if (*buf) {
6915 mg_free(*buf);
6916 }
6917
6918 size *= 4;
6919 *buf = (char *)mg_malloc(size);
6920 if (!*buf) {
6921 break;
6922 }
6923
6924 va_copy(ap_copy, ap);
6925 len = vsnprintf_impl(*buf, size - 1, fmt, ap_copy);
6926 va_end(ap_copy);
6927 (*buf)[size - 1] = 0;
6928 }
6929
6930 return len;
6931}
#define MG_BUF_LEN
Definition civetweb.c:498

References MG_BUF_LEN, mg_free(), mg_malloc(), NULL, va_copy, and vsnprintf_impl.

Referenced by alloc_vprintf().

◆ authorize()

static int authorize ( struct mg_connection * conn,
struct mg_file * filep,
const char * realm )
static

Definition at line 8861 of file civetweb.c.

8862{
8863 struct read_auth_file_struct workdata;
8864 char buf[MG_BUF_LEN];
8865
8866 if (!conn || !conn->dom_ctx) {
8867 return 0;
8868 }
8869
8870 memset(&workdata, 0, sizeof(workdata));
8871 workdata.conn = conn;
8872
8873 if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) {
8874 return 0;
8875 }
8876
8877 /* CGI needs it as REMOTE_USER */
8879 mg_strdup_ctx(workdata.ah.user, conn->phys_ctx);
8880
8881 if (realm) {
8882 workdata.domain = realm;
8883 } else {
8884 workdata.domain = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
8885 }
8886
8887 return read_auth_file(filep, &workdata, INITIAL_DEPTH);
8888}
static int read_auth_file(struct mg_file *filep, struct read_auth_file_struct *workdata, int depth)
Definition civetweb.c:8733
static char * mg_strdup_ctx(const char *str, struct mg_context *ctx)
Definition civetweb.c:3062
static int parse_auth_header(struct mg_connection *conn, char *buf, size_t buf_size, struct ah *ah)
Definition civetweb.c:8555
#define INITIAL_DEPTH
Definition civetweb.c:8715
struct mg_request_info request_info
Definition civetweb.c:2494
struct mg_domain_context * dom_ctx
Definition civetweb.c:2498
const char * remote_user
Definition civetweb.h:164
char buf[256+256+40]
Definition civetweb.c:8725
struct mg_connection * conn
Definition civetweb.c:8722

References read_auth_file_struct::ah, AUTHENTICATION_DOMAIN, read_auth_file_struct::buf, mg_domain_context::config, read_auth_file_struct::conn, mg_connection::dom_ctx, read_auth_file_struct::domain, INITIAL_DEPTH, MG_BUF_LEN, mg_strdup_ctx(), parse_auth_header(), mg_connection::phys_ctx, read_auth_file(), mg_request_info::remote_user, mg_connection::request_info, and ah::user.

Referenced by check_authorization(), is_authorized_for_put(), and mg_check_digest_access_authentication().

◆ b64reverse()

static unsigned char b64reverse ( char letter)
static

Definition at line 7358 of file civetweb.c.

7359{
7360 if ((letter >= 'A') && (letter <= 'Z')) {
7361 return (unsigned char)(letter - 'A');
7362 }
7363 if ((letter >= 'a') && (letter <= 'z')) {
7364 return (unsigned char)(letter - 'a' + 26);
7365 }
7366 if ((letter >= '0') && (letter <= '9')) {
7367 return (unsigned char)(letter - '0' + 52);
7368 }
7369 if (letter == '+') {
7370 return 62;
7371 }
7372 if (letter == '/') {
7373 return 63;
7374 }
7375 if (letter == '=') {
7376 return 255; /* normal end */
7377 }
7378 return 254; /* error */
7379}

Referenced by mg_base64_decode().

◆ bin2str()

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

Definition at line 8389 of file civetweb.c.

8390{
8391 static const char *hex = "0123456789abcdef";
8392
8393 for (; len--; p++) {
8394 *to++ = hex[p[0] >> 4];
8395 *to++ = hex[p[0] & 0x0f];
8396 }
8397 *to = '\0';
8398}

References vec::len.

Referenced by mg_md5().

◆ check_acl()

static int check_acl ( struct mg_context * phys_ctx,
const union usa * sa )
static

Definition at line 16229 of file civetweb.c.

16230{
16231 int allowed, flag, matched;
16232 struct vec vec;
16233
16234 if (phys_ctx) {
16235 const char *list = phys_ctx->dd.config[ACCESS_CONTROL_LIST];
16236
16237 /* If any ACL is set, deny by default */
16238 allowed = (list == NULL) ? '+' : '-';
16239
16240 while ((list = next_option(list, &vec, NULL)) != NULL) {
16241 flag = vec.ptr[0];
16242 matched = -1;
16243 if ((vec.len > 0) && ((flag == '+') || (flag == '-'))) {
16244 vec.ptr++;
16245 vec.len--;
16246 matched = parse_match_net(&vec, sa, 1);
16247 }
16248 if (matched < 0) {
16249 mg_cry_ctx_internal(phys_ctx,
16250 "%s: subnet must be [+|-]IP-addr[/x]",
16251 __func__);
16252 return -1;
16253 }
16254 if (matched) {
16255 allowed = flag;
16256 }
16257 }
16258
16259 return allowed == '+';
16260 }
16261 return -1;
16262}
static const char * next_option(const char *list, struct vec *val, struct vec *eq_val)
Definition civetweb.c:3899
static int parse_match_net(const struct vec *vec, const union usa *sa, int no_strict)
Definition civetweb.c:13966
size_t len
Definition civetweb.c:1866
const char * ptr
Definition civetweb.c:1865

References ACCESS_CONTROL_LIST, mg_domain_context::config, mg_context::dd, vec::len, mg_cry_ctx_internal, next_option(), NULL, parse_match_net(), and vec::ptr.

Referenced by accept_new_connection(), and set_acl_option().

◆ check_authorization()

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

Definition at line 8918 of file civetweb.c.

8919{
8920#if !defined(NO_FILESYSTEMS)
8921 char fname[UTF8_PATH_MAX];
8922 struct vec uri_vec, filename_vec;
8923 const char *list;
8924 struct mg_file file = STRUCT_FILE_INITIALIZER;
8925 int authorized = 1, truncated;
8926
8927 if (!conn || !conn->dom_ctx) {
8928 return 0;
8929 }
8930
8931 list = conn->dom_ctx->config[PROTECT_URI];
8932 while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
8933 if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) {
8934 mg_snprintf(conn,
8935 &truncated,
8936 fname,
8937 sizeof(fname),
8938 "%.*s",
8939 (int)filename_vec.len,
8940 filename_vec.ptr);
8941
8942 if (truncated
8943 || !mg_fopen(conn, fname, MG_FOPEN_MODE_READ, &file)) {
8944 mg_cry_internal(conn,
8945 "%s: cannot open %s: %s",
8946 __func__,
8947 fname,
8948 strerror(errno));
8949 }
8950 break;
8951 }
8952 }
8953
8954 if (!is_file_opened(&file.access)) {
8955 open_auth_file(conn, path, &file);
8956 }
8957
8958 if (is_file_opened(&file.access)) {
8959 authorized = authorize(conn, &file, NULL);
8960 (void)mg_fclose(&file.access); /* ignore error on read only file */
8961 }
8962
8963 return authorized;
8964#else
8965 (void)conn;
8966 (void)path;
8967 return 1;
8968#endif /* NO_FILESYSTEMS */
8969}
static void open_auth_file(struct mg_connection *conn, const char *path, struct mg_file *filep)
Definition civetweb.c:8472
static int mg_fopen(const struct mg_connection *conn, const char *path, int mode, struct mg_file *filep)
Definition civetweb.c:2915
#define MG_FOPEN_MODE_READ
Definition civetweb.c:2843
#define UTF8_PATH_MAX
Definition civetweb.c:861
static void mg_snprintf(const struct mg_connection *conn, int *truncated, char *buf, size_t buflen, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(5
static int is_file_opened(const struct mg_file_access *fileacc)
Definition civetweb.c:2853
static int authorize(struct mg_connection *conn, struct mg_file *filep, const char *realm)
Definition civetweb.c:8861
static int mg_fclose(struct mg_file_access *fileacc)
Definition civetweb.c:2986
#define STRUCT_FILE_INITIALIZER
Definition civetweb.c:1891
struct mg_file_access access
Definition civetweb.c:1887
const char * local_uri
Definition civetweb.h:157

References mg_file::access, authorize(), mg_domain_context::config, mg_connection::dom_ctx, is_file_opened(), vec::len, mg_request_info::local_uri, mg_cry_internal, mg_fclose(), mg_fopen(), MG_FOPEN_MODE_READ, mg_snprintf(), next_option(), NULL, open_auth_file(), PROTECT_URI, vec::ptr, mg_connection::request_info, STRUCT_FILE_INITIALIZER, and UTF8_PATH_MAX.

Referenced by handle_request().

◆ check_password_digest()

static int check_password_digest ( 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 8427 of file civetweb.c.

8435{
8436 char ha2[32 + 1], expected_response[32 + 1];
8437
8438 /* Some of the parameters may be NULL */
8439 if ((method == NULL) || (nonce == NULL) || (nc == NULL) || (cnonce == NULL)
8440 || (qop == NULL) || (response == NULL)) {
8441 return 0;
8442 }
8443
8444 /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */
8445 if (strlen(response) != 32) {
8446 return 0;
8447 }
8448
8449 mg_md5(ha2, method, ":", uri, NULL);
8450 mg_md5(expected_response,
8451 ha1,
8452 ":",
8453 nonce,
8454 ":",
8455 nc,
8456 ":",
8457 cnonce,
8458 ":",
8459 qop,
8460 ":",
8461 ha2,
8462 NULL);
8463
8464 return mg_strcasecmp(response, expected_response) == 0;
8465}
CIVETWEB_API int mg_strcasecmp(const char *s1, const char *s2)
Definition civetweb.c:3034
CIVETWEB_API char * mg_md5(char buf[33],...)
Definition civetweb.c:8404

References mg_md5(), mg_strcasecmp(), and NULL.

Referenced by read_auth_file().

◆ close_all_listening_sockets()

static void close_all_listening_sockets ( struct mg_context * ctx)
static

Definition at line 15456 of file civetweb.c.

15457{
15458 unsigned int i;
15459 if (!ctx) {
15460 return;
15461 }
15462
15463 for (i = 0; i < ctx->num_listening_sockets; i++) {
15465#if defined(USE_X_DOM_SOCKET)
15466 /* For unix domain sockets, the socket name represents a file that has
15467 * to be deleted. */
15468 /* See
15469 * https://stackoverflow.com/questions/15716302/so-reuseaddr-and-af-unix
15470 */
15471 if ((ctx->listening_sockets[i].lsa.sin.sin_family == AF_UNIX)
15472 && (ctx->listening_sockets[i].sock != INVALID_SOCKET)) {
15474 remove(ctx->listening_sockets[i].lsa.sun.sun_path));
15475 }
15476#endif
15478 }
15480 ctx->listening_sockets = NULL;
15483}
struct socket * listening_sockets
Definition civetweb.c:2356
struct mg_pollfd * listening_socket_fds
Definition civetweb.c:2357
unsigned int num_listening_sockets
Definition civetweb.c:2358
union usa lsa
Definition civetweb.c:1904
struct sockaddr_in sin
Definition civetweb.c:1834

References closesocket, IGNORE_UNUSED_RESULT, INVALID_SOCKET, mg_context::listening_socket_fds, mg_context::listening_sockets, socket::lsa, mg_free(), NULL, mg_context::num_listening_sockets, usa::sin, and socket::sock.

Referenced by master_thread_run(), and set_ports_option().

◆ close_connection()

static void close_connection ( struct mg_connection * conn)
static

Definition at line 17864 of file civetweb.c.

17865{
17866#if defined(USE_SERVER_STATS)
17867 conn->conn_state = 6; /* to close */
17868#endif
17869
17870#if defined(USE_LUA) && defined(USE_WEBSOCKET)
17871 if (conn->lua_websocket_state) {
17872 lua_websocket_close(conn, conn->lua_websocket_state);
17873 conn->lua_websocket_state = NULL;
17874 }
17875#endif
17876
17877 mg_lock_connection(conn);
17878
17879 /* Set close flag, so keep-alive loops will stop */
17880 conn->must_close = 1;
17881
17882 /* call the connection_close callback if assigned */
17883 if (conn->phys_ctx->callbacks.connection_close != NULL) {
17884 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
17885 conn->phys_ctx->callbacks.connection_close(conn);
17886 }
17887 }
17888
17889 /* Reset user data, after close callback is called.
17890 * Do not reuse it. If the user needs a destructor,
17891 * it must be done in the connection_close callback. */
17893
17894#if defined(USE_SERVER_STATS)
17895 conn->conn_state = 7; /* closing */
17896#endif
17897
17898#if defined(USE_MBEDTLS)
17899 if (conn->ssl != NULL) {
17900 mbed_ssl_close(conn->ssl);
17901 conn->ssl = NULL;
17902 }
17903#elif !defined(NO_SSL)
17904 if (conn->ssl != NULL) {
17905 /* Run SSL_shutdown twice to ensure completely close SSL connection
17906 */
17907 SSL_shutdown(conn->ssl);
17908 SSL_free(conn->ssl);
17909 OPENSSL_REMOVE_THREAD_STATE();
17910 conn->ssl = NULL;
17911 }
17912#endif
17913 if (conn->client.sock != INVALID_SOCKET) {
17914#if defined(__ZEPHYR__)
17915 closesocket(conn->client.sock);
17916#else
17918#endif
17919 conn->client.sock = INVALID_SOCKET;
17920 }
17921
17922 /* call the connection_closed callback if assigned */
17923 if (conn->phys_ctx->callbacks.connection_closed != NULL) {
17924 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
17926 }
17927 }
17928
17930
17931#if defined(USE_SERVER_STATS)
17932 conn->conn_state = 8; /* closed */
17933#endif
17934}
CIVETWEB_API void mg_lock_connection(struct mg_connection *conn)
Definition civetweb.c:13000
CIVETWEB_API void mg_unlock_connection(struct mg_connection *conn)
Definition civetweb.c:13009
CIVETWEB_API void mg_set_user_connection_data(const struct mg_connection *const_conn, void *data)
Definition civetweb.c:3225
static void close_socket_gracefully(struct mg_connection *conn)
Definition civetweb.c:17740
void(* connection_close)(const struct mg_connection *)
Definition civetweb.h:320
void(* connection_closed)(const struct mg_connection *)
Definition civetweb.h:330
struct socket client
Definition civetweb.c:2507
int context_type
Definition civetweb.c:2354
struct mg_callbacks callbacks
Definition civetweb.c:2432

References mg_context::callbacks, mg_connection::client, close_socket_gracefully(), closesocket, mg_callbacks::connection_close, mg_callbacks::connection_closed, CONTEXT_SERVER, mg_context::context_type, INVALID_SOCKET, mg_lock_connection(), mg_set_user_connection_data(), mg_unlock_connection(), mg_connection::must_close, NULL, mg_connection::phys_ctx, socket::sock, and mg_connection::ssl.

Referenced by mg_close_connection(), process_new_connection(), and worker_thread_run().

◆ close_socket_gracefully()

static void close_socket_gracefully ( struct mg_connection * conn)
static

Definition at line 17740 of file civetweb.c.

17741{
17742#if defined(_WIN32)
17743 char buf[MG_BUF_LEN];
17744 int n;
17745#endif
17746 struct linger linger;
17747 int error_code = 0;
17748 int linger_timeout = -2;
17749 socklen_t opt_len = sizeof(error_code);
17750
17751 if (!conn) {
17752 return;
17753 }
17754
17755 /* http://msdn.microsoft.com/en-us/library/ms739165(v=vs.85).aspx:
17756 * "Note that enabling a nonzero timeout on a nonblocking socket
17757 * is not recommended.", so set it to blocking now */
17759
17760 /* Send FIN to the client */
17761 shutdown(conn->client.sock, SHUTDOWN_WR);
17762
17763#if defined(_WIN32)
17764 /* Read and discard pending incoming data. If we do not do that and
17765 * close
17766 * the socket, the data in the send buffer may be discarded. This
17767 * behaviour is seen on Windows, when client keeps sending data
17768 * when server decides to close the connection; then when client
17769 * does recv() it gets no data back. */
17770 do {
17771 n = pull_inner(NULL, conn, buf, sizeof(buf), /* Timeout in s: */ 1.0);
17772 } while (n > 0);
17773#endif
17774
17775 if (conn->dom_ctx->config[LINGER_TIMEOUT]) {
17776 linger_timeout = atoi(conn->dom_ctx->config[LINGER_TIMEOUT]);
17777 }
17778
17779 /* Set linger option according to configuration */
17780 if (linger_timeout >= 0) {
17781 /* Set linger option to avoid socket hanging out after close. This
17782 * prevent ephemeral port exhaust problem under high QPS. */
17783 linger.l_onoff = 1;
17784
17785#if defined(_MSC_VER)
17786#pragma warning(push)
17787#pragma warning(disable : 4244)
17788#endif
17789#if defined(GCC_DIAGNOSTIC)
17790#pragma GCC diagnostic push
17791#pragma GCC diagnostic ignored "-Wconversion"
17792#endif
17793 /* Data type of linger structure elements may differ,
17794 * so we don't know what cast we need here.
17795 * Disable type conversion warnings. */
17796
17797 linger.l_linger = (linger_timeout + 999) / 1000;
17798
17799#if defined(GCC_DIAGNOSTIC)
17800#pragma GCC diagnostic pop
17801#endif
17802#if defined(_MSC_VER)
17803#pragma warning(pop)
17804#endif
17805
17806 } else {
17807 linger.l_onoff = 0;
17808 linger.l_linger = 0;
17809 }
17810
17811 if (linger_timeout < -1) {
17812 /* Default: don't configure any linger */
17813 } else if (getsockopt(conn->client.sock,
17814 SOL_SOCKET,
17815 SO_ERROR,
17816#if defined(_WIN32) /* WinSock uses different data type here */
17817 (char *)&error_code,
17818#else
17819 &error_code,
17820#endif
17821 &opt_len)
17822 != 0) {
17823 /* Cannot determine if socket is already closed. This should
17824 * not occur and never did in a test. Log an error message
17825 * and continue. */
17826 mg_cry_internal(conn,
17827 "%s: getsockopt(SOL_SOCKET SO_ERROR) failed: %s",
17828 __func__,
17829 strerror(ERRNO));
17830#if defined(_WIN32)
17831 } else if (error_code == WSAECONNRESET) {
17832#else
17833 } else if (error_code == ECONNRESET) {
17834#endif
17835 /* Socket already closed by client/peer, close socket without linger
17836 */
17837 } else {
17838
17839 /* Set linger timeout */
17840 if (setsockopt(conn->client.sock,
17841 SOL_SOCKET,
17842 SO_LINGER,
17843 (char *)&linger,
17844 sizeof(linger))
17845 != 0) {
17847 conn,
17848 "%s: setsockopt(SOL_SOCKET SO_LINGER(%i,%i)) failed: %s",
17849 __func__,
17850 linger.l_onoff,
17851 linger.l_linger,
17852 strerror(ERRNO));
17853 }
17854 }
17855
17856 /* Now we know that our FIN is ACK-ed, safe to close */
17857 closesocket(conn->client.sock);
17858 conn->client.sock = INVALID_SOCKET;
17859}
static int pull_inner(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout)
Definition civetweb.c:6210
static int set_blocking_mode(SOCKET sock)
Definition civetweb.c:5890
#define SHUTDOWN_WR
Definition civetweb.c:517

References mg_connection::client, closesocket, mg_domain_context::config, mg_connection::dom_ctx, ERRNO, INVALID_SOCKET, LINGER_TIMEOUT, MG_BUF_LEN, mg_cry_internal, NULL, pull_inner(), set_blocking_mode(), SHUTDOWN_WR, and socket::sock.

Referenced by close_connection().

◆ compare_dir_entries()

static int compare_dir_entries ( const void * p1,
const void * p2,
void * arg )
static

Definition at line 9716 of file civetweb.c.

9717{
9718 const char *query_string = (const char *)(arg != NULL ? arg : "");
9719 if (p1 && p2) {
9720 const struct de *a = (const struct de *)p1, *b = (const struct de *)p2;
9721 int cmp_result = 0;
9722
9723 if ((query_string == NULL) || (query_string[0] == '\0')) {
9724 query_string = "n";
9725 }
9726
9727 /* Sort Directories vs Files */
9728 if (a->file.is_directory && !b->file.is_directory) {
9729 return -1; /* Always put directories on top */
9730 } else if (!a->file.is_directory && b->file.is_directory) {
9731 return 1; /* Always put directories on top */
9732 }
9733
9734 /* Sort by size or date */
9735 if (*query_string == 's') {
9736 cmp_result = (a->file.size == b->file.size)
9737 ? 0
9738 : ((a->file.size > b->file.size) ? 1 : -1);
9739 } else if (*query_string == 'd') {
9740 cmp_result =
9741 (a->file.last_modified == b->file.last_modified)
9742 ? 0
9743 : ((a->file.last_modified > b->file.last_modified) ? 1
9744 : -1);
9745 }
9746
9747 /* Sort by name:
9748 * if (*query_string == 'n') ...
9749 * but also sort files of same size/date by name as secondary criterion.
9750 */
9751 if (cmp_result == 0) {
9752 cmp_result = strcmp(a->file_name, b->file_name);
9753 }
9754
9755 /* For descending order, invert result */
9756 return (query_string[1] == 'd') ? -cmp_result : cmp_result;
9757 }
9758 return 0;
9759}
CURL_EXTERN int void * arg
Definition curl.h:2622
struct mg_file_stat file
Definition civetweb.c:2579

References arg, and NULL.

Referenced by handle_directory_request().

◆ connect_socket()

static int connect_socket ( struct mg_context * ctx,
const char * host,
int port,
int use_ssl,
struct mg_error_data * error,
SOCKET * sock,
union usa * sa )
static

Definition at line 9287 of file civetweb.c.

9296{
9297 int ip_ver = 0;
9298 int conn_ret = -1;
9299 int sockerr = 0;
9300 *sock = INVALID_SOCKET;
9301 memset(sa, 0, sizeof(*sa));
9302
9303 if (host == NULL) {
9304 if (error != NULL) {
9307 NULL, /* No truncation check for ebuf */
9308 error->text,
9309 error->text_buffer_size,
9310 "%s",
9311 "NULL host");
9312 }
9313 return 0;
9314 }
9315
9316#if defined(USE_X_DOM_SOCKET)
9317 if (port == -99) {
9318 /* Unix domain socket */
9319 size_t hostlen = strlen(host);
9320 if (hostlen >= sizeof(sa->sun.sun_path)) {
9321 if (error != NULL) {
9324 NULL, /* No truncation check for ebuf */
9325 error->text,
9326 error->text_buffer_size,
9327 "%s",
9328 "host length exceeds limit");
9329 }
9330 return 0;
9331 }
9332 } else
9333#endif
9334 if ((port <= 0) || !is_valid_port((unsigned)port)) {
9335 if (error != NULL) {
9338 NULL, /* No truncation check for ebuf */
9339 error->text,
9340 error->text_buffer_size,
9341 "%s",
9342 "invalid port");
9343 }
9344 return 0;
9345 }
9346
9347#if !defined(NO_SSL) && !defined(USE_MBEDTLS) && !defined(NO_SSL_DL)
9348#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
9349 if (use_ssl && (TLS_client_method == NULL)) {
9350 if (error != NULL) {
9353 NULL, /* No truncation check for ebuf */
9354 error->text,
9355 error->text_buffer_size,
9356 "%s",
9357 "SSL is not initialized");
9358 }
9359 return 0;
9360 }
9361#else
9362 if (use_ssl && (SSLv23_client_method == NULL)) {
9363 if (error != 0) {
9366 NULL, /* No truncation check for ebuf */
9367 error->text,
9368 error->text_buffer_size,
9369 "%s",
9370 "SSL is not initialized");
9371 }
9372 return 0;
9373 }
9374#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0*/
9375#else
9376 (void)use_ssl;
9377#endif /* NO SSL */
9378
9379#if defined(USE_X_DOM_SOCKET)
9380 if (port == -99) {
9381 size_t hostlen = strlen(host);
9382 /* check (hostlen < sizeof(sun.sun_path)) already passed above */
9383 ip_ver = -99;
9384 sa->sun.sun_family = AF_UNIX;
9385 memset(sa->sun.sun_path, 0, sizeof(sa->sun.sun_path));
9386 memcpy(sa->sun.sun_path, host, hostlen);
9387 } else
9388#endif
9389 if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin), 1)) {
9390 sa->sin.sin_port = htons((uint16_t)port);
9391 ip_ver = 4;
9392#if defined(USE_IPV6)
9393 } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6), 1)) {
9394 sa->sin6.sin6_port = htons((uint16_t)port);
9395 ip_ver = 6;
9396 } else if (host[0] == '[') {
9397 /* While getaddrinfo on Windows will work with [::1],
9398 * getaddrinfo on Linux only works with ::1 (without []). */
9399 size_t l = strlen(host + 1);
9400 char *h = (l > 1) ? mg_strdup_ctx(host + 1, ctx) : NULL;
9401 if (h) {
9402 h[l - 1] = 0;
9403 if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6), 0)) {
9404 sa->sin6.sin6_port = htons((uint16_t)port);
9405 ip_ver = 6;
9406 }
9407 mg_free(h);
9408 }
9409#endif
9410 }
9411
9412 if (ip_ver == 0) {
9413 if (error != NULL) {
9416 NULL, /* No truncation check for ebuf */
9417 error->text,
9418 error->text_buffer_size,
9419 "%s",
9420 "host not found");
9421 }
9422 return 0;
9423 }
9424
9425 if (ip_ver == 4) {
9426 *sock = socket(PF_INET, SOCK_STREAM, 0);
9427 }
9428#if defined(USE_IPV6)
9429 else if (ip_ver == 6) {
9430 *sock = socket(PF_INET6, SOCK_STREAM, 0);
9431 }
9432#endif
9433#if defined(USE_X_DOM_SOCKET)
9434 else if (ip_ver == -99) {
9435 *sock = socket(AF_UNIX, SOCK_STREAM, 0);
9436 }
9437#endif
9438
9439 if (*sock == INVALID_SOCKET) {
9440 if (error != NULL) {
9442 error->code_sub = (unsigned)ERRNO;
9444 NULL, /* No truncation check for ebuf */
9445 error->text,
9446 error->text_buffer_size,
9447 "socket(): %s",
9448 strerror(ERRNO));
9449 }
9450 return 0;
9451 }
9452
9453 if (0 != set_non_blocking_mode(*sock)) {
9454 if (error != NULL) {
9456 error->code_sub = (unsigned)ERRNO;
9458 NULL, /* No truncation check for ebuf */
9459 error->text,
9460 error->text_buffer_size,
9461 "Cannot set socket to non-blocking: %s",
9462 strerror(ERRNO));
9463 }
9464 closesocket(*sock);
9465 *sock = INVALID_SOCKET;
9466 return 0;
9467 }
9468
9469 set_close_on_exec(*sock, NULL, ctx);
9470
9471 if (ip_ver == 4) {
9472 /* connected with IPv4 */
9473 conn_ret = connect(*sock,
9474 (struct sockaddr *)((void *)&sa->sin),
9475 sizeof(sa->sin));
9476 }
9477#if defined(USE_IPV6)
9478 else if (ip_ver == 6) {
9479 /* connected with IPv6 */
9480 conn_ret = connect(*sock,
9481 (struct sockaddr *)((void *)&sa->sin6),
9482 sizeof(sa->sin6));
9483 }
9484#endif
9485#if defined(USE_X_DOM_SOCKET)
9486 else if (ip_ver == -99) {
9487 /* connected to domain socket */
9488 conn_ret = connect(*sock,
9489 (struct sockaddr *)((void *)&sa->sun),
9490 sizeof(sa->sun));
9491 }
9492#endif
9493
9494 if (conn_ret != 0) {
9495 sockerr = ERRNO;
9496 }
9497
9498#if defined(_WIN32)
9499 if ((conn_ret != 0) && (sockerr == WSAEWOULDBLOCK)) {
9500#else
9501 if ((conn_ret != 0) && (sockerr == EINPROGRESS)) {
9502#endif
9503 /* Data for getsockopt */
9504 void *psockerr = &sockerr;
9505 int ret;
9506
9507#if defined(_WIN32)
9508 int len = (int)sizeof(sockerr);
9509#else
9510 socklen_t len = (socklen_t)sizeof(sockerr);
9511#endif
9512
9513 /* Data for poll */
9514 struct mg_pollfd pfd[1];
9515 int pollres;
9516 int ms_wait = 10000; /* 10 second timeout */
9517 stop_flag_t nonstop;
9518 STOP_FLAG_ASSIGN(&nonstop, 0);
9519
9520 /* For a non-blocking socket, the connect sequence is:
9521 * 1) call connect (will not block)
9522 * 2) wait until the socket is ready for writing (select or poll)
9523 * 3) check connection state with getsockopt
9524 */
9525 pfd[0].fd = *sock;
9526 pfd[0].events = POLLOUT;
9527 pollres = mg_poll(pfd, 1, ms_wait, ctx ? &(ctx->stop_flag) : &nonstop);
9528
9529 if (pollres != 1) {
9530 /* Not connected */
9531 if (error != NULL) {
9534 NULL, /* No truncation check for ebuf */
9535 error->text,
9536 error->text_buffer_size,
9537 "connect(%s:%d): timeout",
9538 host,
9539 port);
9540 }
9541 closesocket(*sock);
9542 *sock = INVALID_SOCKET;
9543 return 0;
9544 }
9545
9546#if defined(_WIN32)
9547 ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, (char *)psockerr, &len);
9548#else
9549 ret = getsockopt(*sock, SOL_SOCKET, SO_ERROR, psockerr, &len);
9550#endif
9551
9552 if ((ret == 0) && (sockerr == 0)) {
9553 conn_ret = 0;
9554 }
9555 }
9556
9557 if (conn_ret != 0) {
9558 /* Not connected */
9559 if (error != NULL) {
9561 error->code_sub = (unsigned)ERRNO;
9563 NULL, /* No truncation check for ebuf */
9564 error->text,
9565 error->text_buffer_size,
9566 "connect(%s:%d): error %s",
9567 host,
9568 port,
9569 strerror(sockerr));
9570 }
9571 closesocket(*sock);
9572 *sock = INVALID_SOCKET;
9573 return 0;
9574 }
9575
9576 return 1;
9577}
static int mg_poll(struct mg_pollfd *pfd, unsigned int n, int milliseconds, const stop_flag_t *stop_flag)
Definition civetweb.c:5938
#define STOP_FLAG_ASSIGN(f, v)
Definition civetweb.c:2324
static int mg_inet_pton(int af, const char *src, void *dst, size_t dstlen, int resolve_src)
Definition civetweb.c:9246
static int is_valid_port(unsigned long port)
Definition civetweb.c:9239
#define mg_pollfd
Definition civetweb.c:948
int volatile stop_flag_t
Definition civetweb.c:2321
@ MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED
Definition civetweb.h:1758
@ MG_ERROR_DATA_CODE_OS_ERROR
Definition civetweb.h:1761
@ MG_ERROR_DATA_CODE_CONNECT_FAILED
Definition civetweb.h:1785
@ MG_ERROR_DATA_CODE_CONNECT_TIMEOUT
Definition civetweb.h:1782
@ MG_ERROR_DATA_CODE_INVALID_PARAM
Definition civetweb.h:1737
@ MG_ERROR_DATA_CODE_HOST_NOT_FOUND
Definition civetweb.h:1779
static void error(LoadState *S, const char *why)
stop_flag_t stop_flag
Definition civetweb.c:2373

References CIVETWEB_API, closesocket, ERRNO, error(), INVALID_SOCKET, is_valid_port(), MG_ERROR_DATA_CODE_CONNECT_FAILED, MG_ERROR_DATA_CODE_CONNECT_TIMEOUT, MG_ERROR_DATA_CODE_HOST_NOT_FOUND, MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED, MG_ERROR_DATA_CODE_INVALID_PARAM, MG_ERROR_DATA_CODE_OS_ERROR, mg_free(), mg_inet_pton(), mg_poll(), mg_pollfd, mg_snprintf(), mg_strdup_ctx(), NULL, set_close_on_exec(), set_non_blocking_mode(), usa::sin, mg_context::stop_flag, and STOP_FLAG_ASSIGN.

Referenced by mg_connect_client_impl().

◆ construct_etag()

static void construct_etag ( char * buf,
size_t buf_len,
const struct mg_file_stat * filestat )
static

Definition at line 10181 of file civetweb.c.

10182{
10183 if ((filestat != NULL) && (buf != NULL)) {
10185 NULL, /* All calls to construct_etag use 64 byte buffer */
10186 buf,
10187 buf_len,
10188 "\"%lx.%" INT64_FMT "\"",
10189 (unsigned long)filestat->last_modified,
10190 filestat->size);
10191 }
10192}
#define INT64_FMT
Definition civetweb.c:926
uint64_t size
Definition civetweb.c:1871
time_t last_modified
Definition civetweb.c:1872

References INT64_FMT, mg_file_stat::last_modified, mg_snprintf(), NULL, and mg_file_stat::size.

Referenced by handle_not_modified_static_file_request(), handle_static_file_request(), and is_not_modified().

◆ consume_socket()

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

Definition at line 19624 of file civetweb.c.

19625{
19626 (void)thread_index;
19627
19628 (void)pthread_mutex_lock(&ctx->thread_mutex);
19629 DEBUG_TRACE("%s", "going idle");
19630
19631 /* If the queue is empty, wait. We're idle at this point. */
19632 while ((ctx->sq_head == ctx->sq_tail)
19633 && (STOP_FLAG_IS_ZERO(&ctx->stop_flag))) {
19634 pthread_cond_wait(&ctx->sq_full, &ctx->thread_mutex);
19635 }
19636
19637 /* If we're stopping, sq_head may be equal to sq_tail. */
19638 if (ctx->sq_head > ctx->sq_tail) {
19639 /* Copy socket from the queue and increment tail */
19640 *sp = ctx->squeue[ctx->sq_tail % ctx->sq_size];
19641 ctx->sq_tail++;
19642
19643 DEBUG_TRACE("grabbed socket %d, going busy", sp ? sp->sock : -1);
19644
19645 /* Wrap pointers if needed */
19646 while (ctx->sq_tail > ctx->sq_size) {
19647 ctx->sq_tail -= ctx->sq_size;
19648 ctx->sq_head -= ctx->sq_size;
19649 }
19650 }
19651
19652 (void)pthread_cond_signal(&ctx->sq_empty);
19653 (void)pthread_mutex_unlock(&ctx->thread_mutex);
19654
19655 return STOP_FLAG_IS_ZERO(&ctx->stop_flag);
19656}
#define STOP_FLAG_IS_ZERO(f)
Definition civetweb.c:2322
pthread_cond_t sq_empty
Definition civetweb.c:2392
struct socket * squeue
Definition civetweb.c:2387
volatile int sq_head
Definition civetweb.c:2389
pthread_mutex_t thread_mutex
Definition civetweb.c:2374
pthread_cond_t sq_full
Definition civetweb.c:2391
volatile int sq_tail
Definition civetweb.c:2390

References DEBUG_TRACE, socket::sock, mg_context::sq_empty, mg_context::sq_full, mg_context::sq_head, mg_context::sq_size, mg_context::sq_tail, mg_context::squeue, mg_context::stop_flag, STOP_FLAG_IS_ZERO, and mg_context::thread_mutex.

Referenced by worker_thread_run().

◆ dav_lock_file()

static void dav_lock_file ( struct mg_connection * conn,
const char * path )
static

Definition at line 12802 of file civetweb.c.

12803{
12804 /* internal function - therefore conn is assumed to be valid */
12805 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12806 uint64_t new_locktime;
12807 int lock_index = -1;
12808 int i;
12809 uint64_t LOCK_DURATION_NS =
12810 (uint64_t)(LOCK_DURATION_S) * (uint64_t)1000000000;
12811 struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
12812
12813 if (!path || !conn->dom_ctx || !conn->request_info.remote_user) {
12814 return;
12815 }
12816 mg_get_request_link(conn, link_buf, sizeof(link_buf));
12817
12818 /* const char *refresh = mg_get_header(conn, "If"); */
12819 /* Link refresh should have an "If" header:
12820 * http://www.webdav.org/specs/rfc2518.html#n-example---refreshing-a-write-lock
12821 * But it seems Windows Explorer does not send them.
12822 */
12823
12825 new_locktime = mg_get_current_time_ns();
12826
12827 /* Find a slot for a lock */
12828 while (lock_index < 0) {
12829 /* find existing lock */
12830 for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
12831 if (!strcmp(dav_lock[i].path, link_buf)) {
12832 if (!strcmp(conn->request_info.remote_user, dav_lock[i].user)) {
12833 /* locked by the same user */
12834 dav_lock[i].locktime = new_locktime;
12835 lock_index = i;
12836 break;
12837 } else {
12838 /* already locked by someone else */
12839 if (new_locktime
12840 > (dav_lock[i].locktime + LOCK_DURATION_NS)) {
12841 /* Lock expired */
12842 dav_lock[i].path[0] = 0;
12843 } else {
12844 /* Lock still valid */
12846 mg_send_http_error(conn, 423, "%s", "Already locked");
12847 return;
12848 }
12849 }
12850 }
12851 }
12852
12853 /* create new lock token */
12854 for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
12855 if (dav_lock[i].path[0] == 0) {
12856 char s[32];
12857 dav_lock[i].locktime = mg_get_current_time_ns();
12858 sprintf(s, "%" UINT64_FMT, (uint64_t)dav_lock[i].locktime);
12859 mg_md5(dav_lock[i].token,
12860 link_buf,
12861 "\x01",
12862 s,
12863 "\x01",
12865 NULL);
12866 mg_strlcpy(dav_lock[i].path,
12867 link_buf,
12868 sizeof(dav_lock[i].path));
12869 mg_strlcpy(dav_lock[i].user,
12871 sizeof(dav_lock[i].user));
12872 lock_index = i;
12873 break;
12874 }
12875 }
12876 if (lock_index < 0) {
12877 /* too many locks. Find oldest lock */
12878 uint64_t oldest_locktime = dav_lock[0].locktime;
12879 lock_index = 0;
12880 for (i = 1; i < NUM_WEBDAV_LOCKS; i++) {
12881 if (dav_lock[i].locktime < oldest_locktime) {
12882 oldest_locktime = dav_lock[i].locktime;
12883 lock_index = i;
12884 }
12885 }
12886 /* invalidate oldest lock */
12887 dav_lock[lock_index].path[0] = 0;
12888 }
12889 }
12891
12892 /* return 200 "OK" */
12893 conn->must_close = 1;
12894 mg_response_header_start(conn, 200);
12898 "Content-Type",
12899 "application/xml; charset=utf-8",
12900 -1);
12901 mg_response_header_add(conn, "Lock-Token", dav_lock[lock_index].token, -1);
12903
12904 /* Content */
12905 mg_printf(conn,
12906 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
12907 "<d:prop xmlns:d=\"DAV:\">\n"
12908 " <d:lockdiscovery>\n"
12909 " <d:activelock>\n"
12910 " <d:lockscope><d:exclusive/></d:lockscope>\n"
12911 " <d:locktype><d:write/></d:locktype>\n"
12912 " <d:owner>\n"
12913 " <d:href>%s</d:href>\n"
12914 " </d:owner>\n"
12915 " <d:timeout>Second-%u</d:timeout>\n"
12916 " <d:locktoken><d:href>%s</d:href></d:locktoken>\n"
12917 " <d:lockroot>\n"
12918 " <d:href>%s</d:href>\n"
12919 " </d:lockroot>\n"
12920 " </d:activelock>\n"
12921 " </d:lockdiscovery>\n"
12922 " </d:prop>\n",
12923 dav_lock[lock_index].user,
12925 dav_lock[lock_index].token,
12926 dav_lock[lock_index].path);
12927}
CIVETWEB_API void mg_lock_context(struct mg_context *ctx)
Definition civetweb.c:13018
#define LOCK_DURATION_S
Definition civetweb.c:2333
static FUNCTION_MAY_BE_UNUSED uint64_t mg_get_current_time_ns(void)
Definition civetweb.c:1676
#define UINT64_FMT
Definition civetweb.c:927
static void send_static_cache_header(struct mg_connection *conn)
Definition civetweb.c:4074
CIVETWEB_API void mg_unlock_context(struct mg_context *ctx)
Definition civetweb.c:13027
CIVETWEB_API int mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen)
Definition civetweb.c:3745
static void send_additional_header(struct mg_connection *conn)
Definition civetweb.c:4122
CIVETWEB_API int mg_send_http_error(struct mg_connection *conn, int status, const char *fmt,...)
Definition civetweb.c:4562
CIVETWEB_API int mg_printf(struct mg_connection *conn, const char *fmt,...)
Definition civetweb.c:7034
static void mg_strlcpy(char *dst, const char *src, size_t n)
Definition civetweb.c:3002
#define NUM_WEBDAV_LOCKS
Definition civetweb.c:2330
CIVETWEB_API int mg_response_header_send(struct mg_connection *conn)
CIVETWEB_API int mg_response_header_add(struct mg_connection *conn, const char *header, const char *value, int value_len)
CIVETWEB_API int mg_response_header_start(struct mg_connection *conn, int status)
CURL_EXTERN CURLMcode curl_socket_t s
Definition multi.h:318
struct twebdav_lock webdav_lock[NUM_WEBDAV_LOCKS]
Definition civetweb.c:2408
char user[UTF8_PATH_MAX *2]
Definition civetweb.c:2341
char token[33]
Definition civetweb.c:2339
char path[UTF8_PATH_MAX *2]
Definition civetweb.c:2340
uint64_t locktime
Definition civetweb.c:2338

References mg_connection::dom_ctx, LOCK_DURATION_S, twebdav_lock::locktime, mg_get_current_time_ns(), mg_get_request_link(), mg_lock_context(), mg_md5(), mg_printf(), mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_send_http_error(), mg_strlcpy(), mg_unlock_context(), mg_connection::must_close, NULL, NUM_WEBDAV_LOCKS, twebdav_lock::path, mg_connection::phys_ctx, mg_request_info::remote_user, mg_connection::request_info, s, send_additional_header(), send_static_cache_header(), twebdav_lock::token, UINT64_FMT, twebdav_lock::user, UTF8_PATH_MAX, and mg_context::webdav_lock.

Referenced by handle_request().

◆ dav_mkcol()

static void dav_mkcol ( struct mg_connection * conn,
const char * path )
static

Definition at line 11915 of file civetweb.c.

11916{
11917 int rc, body_len;
11918 struct de de;
11919
11920 if (conn == NULL) {
11921 return;
11922 }
11923
11924 /* TODO (mid): Check the mg_send_http_error situations in this function
11925 */
11926
11927 memset(&de.file, 0, sizeof(de.file));
11928 if (!mg_stat(conn, path, &de.file)) {
11929 mg_cry_internal(conn,
11930 "%s: mg_stat(%s) failed: %s",
11931 __func__,
11932 path,
11933 strerror(ERRNO));
11934 }
11935
11936 if (de.file.last_modified) {
11937 /* TODO (mid): This check does not seem to make any sense ! */
11938 /* TODO (mid): Add a webdav unit test first, before changing
11939 * anything here. */
11941 conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO));
11942 return;
11943 }
11944
11945 body_len = conn->data_len - conn->request_len;
11946 if (body_len > 0) {
11948 conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO));
11949 return;
11950 }
11951
11952 rc = mg_mkdir(conn, path, 0755);
11953 DEBUG_TRACE("mkdir %s: %i", path, rc);
11954 if (rc == 0) {
11955 /* Create 201 "Created" response */
11956 mg_response_header_start(conn, 201);
11959 mg_response_header_add(conn, "Content-Length", "0", -1);
11960
11961 /* Send all headers - there is no body */
11963 } else {
11964 int http_status = 500;
11965 switch (errno) {
11966 case EEXIST:
11967 http_status = 405;
11968 break;
11969 case EACCES:
11970 http_status = 403;
11971 break;
11972 case ENOENT:
11973 http_status = 409;
11974 break;
11975 }
11976
11977 mg_send_http_error(conn,
11978 http_status,
11979 "Error processing %s: %s",
11980 path,
11981 strerror(ERRNO));
11982 }
11983}
#define mg_mkdir(conn, path, mode)
Definition civetweb.c:918
static int mg_stat(const struct mg_connection *conn, const char *path, struct mg_file_stat *filep)
Definition civetweb.c:5650

References mg_connection::data_len, DEBUG_TRACE, ERRNO, de::file, mg_file_stat::last_modified, mg_cry_internal, mg_mkdir, mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_send_http_error(), mg_stat(), NULL, mg_connection::request_len, send_additional_header(), and send_static_cache_header().

Referenced by handle_request().

◆ dav_move_file()

static void dav_move_file ( struct mg_connection * conn,
const char * path,
int do_copy )
static

Definition at line 11994 of file civetweb.c.

11995{
11996 const char *overwrite_hdr;
11997 const char *destination_hdr;
11998 const char *root;
11999 int rc, dest_uri_type;
12000 int http_status = 400;
12001 int do_overwrite = 0;
12002 int destination_ok = 0;
12003 char dest_path[UTF8_PATH_MAX];
12004 struct mg_file_stat ignored;
12005
12006 if (conn == NULL) {
12007 return;
12008 }
12009
12010 root = conn->dom_ctx->config[DOCUMENT_ROOT];
12011 overwrite_hdr = mg_get_header(conn, "Overwrite");
12012 destination_hdr = mg_get_header(conn, "Destination");
12013 if ((overwrite_hdr != NULL) && (toupper(overwrite_hdr[0]) == 'T')) {
12014 do_overwrite = 1;
12015 }
12016
12017 if ((destination_hdr == NULL) || (destination_hdr[0] == 0)) {
12018 mg_send_http_error(conn, 400, "%s", "Missing destination");
12019 return;
12020 }
12021
12022 if (root != NULL) {
12023 char *local_dest = NULL;
12024 dest_uri_type = get_uri_type(destination_hdr);
12025 if (dest_uri_type == 2) {
12026 local_dest = mg_strdup_ctx(destination_hdr, conn->phys_ctx);
12027 } else if ((dest_uri_type == 3) || (dest_uri_type == 4)) {
12028 const char *h =
12029 get_rel_url_at_current_server(destination_hdr, conn);
12030 if (h) {
12031 size_t len = strlen(h);
12032 local_dest = mg_malloc_ctx(len + 1, conn->phys_ctx);
12033 mg_url_decode(h, (int)len, local_dest, (int)len + 1, 0);
12034 }
12035 }
12036 if (local_dest != NULL) {
12037 remove_dot_segments(local_dest);
12038 if (local_dest[0] == '/') {
12039 int trunc_check = 0;
12040 mg_snprintf(conn,
12041 &trunc_check,
12042 dest_path,
12043 sizeof(dest_path),
12044 "%s/%s",
12045 root,
12046 local_dest);
12047 if (trunc_check == 0) {
12048 destination_ok = 1;
12049 }
12050 }
12051 mg_free(local_dest);
12052 }
12053 }
12054
12055 if (!destination_ok) {
12056 mg_send_http_error(conn, 502, "%s", "Illegal destination");
12057 return;
12058 }
12059
12060 /* Check now if this file exists */
12061 if (mg_stat(conn, dest_path, &ignored)) {
12062 /* File exists */
12063 if (do_overwrite) {
12064 /* Overwrite allowed: delete the file first */
12065 if (0 != remove(dest_path)) {
12066 /* No overwrite: return error */
12067 mg_send_http_error(conn,
12068 403,
12069 "Cannot overwrite file: %s",
12070 dest_path);
12071 return;
12072 }
12073 } else {
12074 /* No overwrite: return error */
12075 mg_send_http_error(conn,
12076 412,
12077 "Destination already exists: %s",
12078 dest_path);
12079 return;
12080 }
12081 }
12082
12083 /* Copy / Move / Rename operation. */
12084 DEBUG_TRACE("%s %s to %s", (do_copy ? "copy" : "move"), path, dest_path);
12085#if defined(_WIN32)
12086 {
12087 /* For Windows, we need to convert from UTF-8 to UTF-16 first. */
12088 wchar_t wSource[UTF16_PATH_MAX];
12089 wchar_t wDest[UTF16_PATH_MAX];
12090 BOOL ok;
12091
12092 path_to_unicode(conn, path, wSource, ARRAY_SIZE(wSource));
12093 path_to_unicode(conn, dest_path, wDest, ARRAY_SIZE(wDest));
12094 if (do_copy) {
12095 ok = CopyFileW(wSource, wDest, do_overwrite ? FALSE : TRUE);
12096 } else {
12097 ok = MoveFileExW(wSource,
12098 wDest,
12099 do_overwrite ? MOVEFILE_REPLACE_EXISTING : 0);
12100 }
12101 if (ok) {
12102 rc = 0;
12103 } else {
12104 DWORD lastErr = GetLastError();
12105 if (lastErr == ERROR_ALREADY_EXISTS) {
12106 mg_send_http_error(conn,
12107 412,
12108 "Destination already exists: %s",
12109 dest_path);
12110 return;
12111 }
12112 rc = -1;
12113 http_status = 400;
12114 }
12115 }
12116
12117#else
12118 {
12119 /* Linux uses already UTF-8, we don't need to convert file names. */
12120
12121 if (do_copy) {
12122 /* TODO: COPY for Linux. */
12123 mg_send_http_error(conn, 403, "%s", "COPY forbidden");
12124 return;
12125 }
12126
12127 rc = rename(path, dest_path);
12128 if (rc) {
12129 switch (errno) {
12130 case EEXIST:
12131 http_status = 412;
12132 break;
12133 case EACCES:
12134 http_status = 403;
12135 break;
12136 case ENOENT:
12137 http_status = 409;
12138 break;
12139 }
12140 }
12141 }
12142#endif
12143
12144 if (rc == 0) {
12145 /* Create 204 "No Content" response */
12146 mg_response_header_start(conn, 204);
12147 mg_response_header_add(conn, "Content-Length", "0", -1);
12148
12149 /* Send all headers - there is no body */
12151 } else {
12152 mg_send_http_error(conn, http_status, "Operation failed");
12153 }
12154}
#define mg_malloc_ctx(a, c)
Definition civetweb.c:1497
static const char * get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn)
Definition civetweb.c:18465
CIVETWEB_API const char * mg_get_header(const struct mg_connection *conn, const char *name)
Definition civetweb.c:3855
CIVETWEB_API int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int is_form_url_encoded)
Definition civetweb.c:7048
#define ARRAY_SIZE(array)
Definition civetweb.c:506
static int get_uri_type(const char *uri)
Definition civetweb.c:18395
static void remove_dot_segments(char *inout)
Definition civetweb.c:8069
#define TRUE
Definition gmacros.h:933
#define FALSE
Definition gmacros.h:929

References ARRAY_SIZE, mg_domain_context::config, DEBUG_TRACE, DOCUMENT_ROOT, mg_connection::dom_ctx, FALSE, get_rel_url_at_current_server(), get_uri_type(), mg_free(), mg_get_header(), mg_malloc_ctx, mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_send_http_error(), mg_snprintf(), mg_stat(), mg_strdup_ctx(), mg_url_decode(), NULL, mg_connection::phys_ctx, remove_dot_segments(), TRUE, and UTF8_PATH_MAX.

Referenced by handle_request().

◆ dav_proppatch()

static void dav_proppatch ( struct mg_connection * conn,
const char * path )
static

Definition at line 12964 of file civetweb.c.

12965{
12966 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12967
12968 if (!conn || !path || !conn->dom_ctx) {
12969 return;
12970 }
12971
12972 /* return 207 "Multi-Status" */
12973 conn->must_close = 1;
12974 mg_response_header_start(conn, 207);
12978 "Content-Type",
12979 "application/xml; charset=utf-8",
12980 -1);
12982
12983 mg_get_request_link(conn, link_buf, sizeof(link_buf));
12984
12985 /* Content */
12986 mg_printf(conn,
12987 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
12988 "<d:multistatus xmlns:d='DAV:'>\n"
12989 "<d:response>\n<d:href>%s</d:href>\n",
12990 link_buf);
12991 mg_printf(conn,
12992 "<d:propstat><d:status>HTTP/1.1 403 "
12993 "Forbidden</d:status></d:propstat>\n");
12994 mg_printf(conn, "%s\n", "</d:response></d:multistatus>");
12995}

References mg_connection::dom_ctx, mg_get_request_link(), mg_printf(), mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_connection::must_close, twebdav_lock::path, send_additional_header(), send_static_cache_header(), and UTF8_PATH_MAX.

Referenced by handle_request().

◆ dav_unlock_file()

static void dav_unlock_file ( struct mg_connection * conn,
const char * path )
static

Definition at line 12931 of file civetweb.c.

12932{
12933 /* internal function - therefore conn is assumed to be valid */
12934 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12935 struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
12936 int lock_index;
12937
12938 if (!path || !conn->dom_ctx || !conn->request_info.remote_user) {
12939 return;
12940 }
12941
12942 mg_get_request_link(conn, link_buf, sizeof(link_buf));
12943
12945 /* find existing lock */
12946 for (lock_index = 0; lock_index < NUM_WEBDAV_LOCKS; lock_index++) {
12947 if (!strcmp(dav_lock[lock_index].path, link_buf)) {
12948 /* Success: return 204 "No Content" */
12950 conn->must_close = 1;
12951 mg_response_header_start(conn, 204);
12953 return;
12954 }
12955 }
12957
12958 /* Error: Cannot unlock a resource that is not locked */
12959 mg_send_http_error(conn, 423, "%s", "Lock not found");
12960}

References mg_connection::dom_ctx, mg_get_request_link(), mg_lock_context(), mg_response_header_send(), mg_response_header_start(), mg_send_http_error(), mg_unlock_context(), mg_connection::must_close, NUM_WEBDAV_LOCKS, twebdav_lock::path, mg_connection::phys_ctx, mg_request_info::remote_user, mg_connection::request_info, UTF8_PATH_MAX, and mg_context::webdav_lock.

Referenced by handle_request().

◆ delete_file()

static void delete_file ( struct mg_connection * conn,
const char * path )
static

Definition at line 12293 of file civetweb.c.

12294{
12295 struct de de;
12296 memset(&de.file, 0, sizeof(de.file));
12297 if (!mg_stat(conn, path, &de.file)) {
12298 /* mg_stat returns 0 if the file does not exist */
12299 mg_send_http_error(conn,
12300 404,
12301 "Error: Cannot delete file\nFile %s not found",
12302 path);
12303 return;
12304 }
12305
12306 DEBUG_TRACE("delete %s", path);
12307
12308 if (de.file.is_directory) {
12309 if (remove_directory(conn, path)) {
12310 /* Delete is successful: Return 204 without content. */
12311 mg_send_http_error(conn, 204, "%s", "");
12312 } else {
12313 /* Delete is not successful: Return 500 (Server error). */
12314 mg_send_http_error(conn, 500, "Error: Could not delete %s", path);
12315 }
12316 return;
12317 }
12318
12319 /* This is an existing file (not a directory).
12320 * Check if write permission is granted. */
12321 if (access(path, W_OK) != 0) {
12322 /* File is read only */
12324 conn,
12325 403,
12326 "Error: Delete not possible\nDeleting %s is not allowed",
12327 path);
12328 return;
12329 }
12330
12331 /* Try to delete it. */
12332 if (mg_remove(conn, path) == 0) {
12333 /* Delete was successful: Return 204 without content. */
12334 mg_response_header_start(conn, 204);
12337 mg_response_header_add(conn, "Content-Length", "0", -1);
12339
12340 } else {
12341 /* Delete not successful (file locked). */
12342 mg_send_http_error(conn,
12343 423,
12344 "Error: Cannot delete file\nremove(%s): %s",
12345 path,
12346 strerror(ERRNO));
12347 }
12348}
#define mg_remove(conn, x)
Definition civetweb.c:919
static void send_no_cache_header(struct mg_connection *conn)
Definition civetweb.c:4056
static int remove_directory(struct mg_connection *conn, const char *dir)
Definition civetweb.c:9836

References DEBUG_TRACE, ERRNO, de::file, mg_file_stat::is_directory, mg_remove, mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_send_http_error(), mg_stat(), remove_directory(), send_additional_header(), and send_no_cache_header().

Referenced by handle_request().

◆ dir_scan_callback()

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

Definition at line 9911 of file civetweb.c.

9912{
9913 struct dir_scan_data *dsd = (struct dir_scan_data *)data;
9914 struct de *entries = dsd->entries;
9915
9916 if ((entries == NULL) || (dsd->num_entries >= dsd->arr_size)) {
9917 /* Here "entries" is a temporary pointer and can be replaced,
9918 * "dsd->entries" is the original pointer */
9919 entries =
9920 (struct de *)mg_realloc(entries,
9921 dsd->arr_size * 2 * sizeof(entries[0]));
9922 if (entries == NULL) {
9923 /* stop scan */
9924 return 1;
9925 }
9926 dsd->entries = entries;
9927 dsd->arr_size *= 2;
9928 }
9929 entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
9930 if (entries[dsd->num_entries].file_name == NULL) {
9931 /* stop scan */
9932 return 1;
9933 }
9934 entries[dsd->num_entries].file = de->file;
9935 dsd->num_entries++;
9936
9937 return 0;
9938}
static char * mg_strdup(const char *str)
Definition civetweb.c:3068
static __inline void * mg_realloc(void *a, size_t b)
Definition civetweb.c:1486
char * file_name
Definition civetweb.c:2578
size_t num_entries
Definition civetweb.c:9904
struct de * entries
Definition civetweb.c:9903
size_t arr_size
Definition civetweb.c:9905

References dir_scan_data::arr_size, dir_scan_data::entries, de::file, de::file_name, mg_realloc(), mg_strdup(), NULL, and dir_scan_data::num_entries.

Referenced by handle_directory_request().

◆ discard_unread_request_data()

static void discard_unread_request_data ( struct mg_connection * conn)
static

Definition at line 6484 of file civetweb.c.

6485{
6486 char buf[MG_BUF_LEN];
6487
6488 while (mg_read(conn, buf, sizeof(buf)) > 0)
6489 ;
6490}
CIVETWEB_API int mg_read(struct mg_connection *conn, void *buf, size_t len)
Definition civetweb.c:6614

References MG_BUF_LEN, and mg_read().

Referenced by handle_request().

◆ do_ssi_exec()

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

Definition at line 12443 of file civetweb.c.

12444{
12445 char cmd[1024] = "";
12446 struct mg_file file = STRUCT_FILE_INITIALIZER;
12447
12448 if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) {
12449 mg_cry_internal(conn, "Bad SSI #exec: [%s]", tag);
12450 } else {
12451 cmd[1023] = 0;
12452 if ((file.access.fp = popen(cmd, "r")) == NULL) {
12453 mg_cry_internal(conn,
12454 "Cannot SSI #exec: [%s]: %s",
12455 cmd,
12456 strerror(ERRNO));
12457 } else {
12458 send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
12459 pclose(file.access.fp);
12460 }
12461 }
12462}
static void send_file_data(struct mg_connection *conn, struct mg_file *filep, int64_t offset, int64_t len, int no_buffering)
Definition civetweb.c:10055
#define INT64_MAX
Definition civetweb.c:513

References mg_file::access, ERRNO, mg_file_access::fp, INT64_MAX, mg_cry_internal, NULL, send_file_data(), and STRUCT_FILE_INITIALIZER.

Referenced by send_ssi_file().

◆ do_ssi_include()

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

Definition at line 12358 of file civetweb.c.

12362{
12363 char file_name[MG_BUF_LEN], path[512], *p;
12364 struct mg_file file = STRUCT_FILE_INITIALIZER;
12365 size_t len;
12366 int truncated = 0;
12367
12368 if (conn == NULL) {
12369 return;
12370 }
12371
12372 /* sscanf() is safe here, since send_ssi_file() also uses buffer
12373 * of size MG_BUF_LEN to get the tag. So strlen(tag) is
12374 * always < MG_BUF_LEN. */
12375 if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) {
12376 /* File name is relative to the webserver root */
12377 file_name[511] = 0;
12378 (void)mg_snprintf(conn,
12379 &truncated,
12380 path,
12381 sizeof(path),
12382 "%s/%s",
12384 file_name);
12385
12386 } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) {
12387 /* File name is relative to the webserver working directory
12388 * or it is absolute system path */
12389 file_name[511] = 0;
12390 (void)
12391 mg_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name);
12392
12393 } else if ((sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1)
12394 || (sscanf(tag, " \"%511[^\"]\"", file_name) == 1)) {
12395 /* File name is relative to the current document */
12396 file_name[511] = 0;
12397 (void)mg_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi);
12398
12399 if (!truncated) {
12400 if ((p = strrchr(path, '/')) != NULL) {
12401 p[1] = '\0';
12402 }
12403 len = strlen(path);
12404 (void)mg_snprintf(conn,
12405 &truncated,
12406 path + len,
12407 sizeof(path) - len,
12408 "%s",
12409 file_name);
12410 }
12411
12412 } else {
12413 mg_cry_internal(conn, "Bad SSI #include: [%s]", tag);
12414 return;
12415 }
12416
12417 if (truncated) {
12418 mg_cry_internal(conn, "SSI #include path length overflow: [%s]", tag);
12419 return;
12420 }
12421
12422 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) {
12423 mg_cry_internal(conn,
12424 "Cannot open SSI #include: [%s]: fopen(%s): %s",
12425 tag,
12426 path,
12427 strerror(ERRNO));
12428 } else {
12429 fclose_on_exec(&file.access, conn);
12430 if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path)
12431 > 0) {
12432 send_ssi_file(conn, path, &file, include_level + 1);
12433 } else {
12434 send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
12435 }
12436 (void)mg_fclose(&file.access); /* Ignore errors for readonly files */
12437 }
12438}
static void fclose_on_exec(struct mg_file_access *filep, struct mg_connection *conn)
Definition civetweb.c:10196
static void send_ssi_file(struct mg_connection *, const char *, struct mg_file *, int)
Definition civetweb.c:12482

References mg_file::access, mg_domain_context::config, DOCUMENT_ROOT, mg_connection::dom_ctx, ERRNO, fclose_on_exec(), de::file_name, INT64_MAX, MG_BUF_LEN, mg_cry_internal, mg_fclose(), mg_fopen(), MG_FOPEN_MODE_READ, mg_snprintf(), NULL, send_file_data(), send_ssi_file(), SSI_EXTENSIONS, and STRUCT_FILE_INITIALIZER.

Referenced by send_ssi_file().

◆ extention_matches_script()

static int extention_matches_script ( struct mg_connection * conn,
const char * filename )
static

Definition at line 7504 of file civetweb.c.

7508{
7509#if !defined(NO_CGI)
7510 int cgi_config_idx, inc, max;
7511#endif
7512
7513#if defined(USE_LUA)
7514 if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS],
7515 filename)
7516 > 0) {
7517 return 1;
7518 }
7519#endif
7520#if defined(USE_DUKTAPE)
7521 if (match_prefix_strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
7522 filename)
7523 > 0) {
7524 return 1;
7525 }
7526#endif
7527#if !defined(NO_CGI)
7530 for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) {
7531 if ((conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL)
7532 && (match_prefix_strlen(
7533 conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx],
7534 filename)
7535 > 0)) {
7536 return 1;
7537 }
7538 }
7539#endif
7540 /* filename and conn could be unused, if all preocessor conditions
7541 * are false (no script language supported). */
7542 (void)filename;
7543 (void)conn;
7544
7545 return 0;
7546}

References CGI2_EXTENSIONS, CGI_EXTENSIONS, mg_domain_context::config, mg_connection::dom_ctx, NULL, and PUT_DELETE_PASSWORDS_FILE.

Referenced by interpret_uri().

◆ extention_matches_template_text()

static int extention_matches_template_text ( struct mg_connection * conn,
const char * filename )
static

Definition at line 7550 of file civetweb.c.

7554{
7555#if defined(USE_LUA)
7556 if (match_prefix_strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
7557 filename)
7558 > 0) {
7559 return 1;
7560 }
7561#endif
7562 if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], filename)
7563 > 0) {
7564 return 1;
7565 }
7566 return 0;
7567}

References mg_domain_context::config, mg_connection::dom_ctx, and SSI_EXTENSIONS.

Referenced by interpret_uri().

◆ fake_connection()

static struct mg_connection * fake_connection ( struct mg_connection * fc,
struct mg_context * ctx )
static

Definition at line 3470 of file civetweb.c.

3471{
3472 static const struct mg_connection conn_zero = {0};
3473 *fc = conn_zero;
3474 fc->phys_ctx = ctx;
3475 fc->dom_ctx = &(ctx->dd);
3476 return fc;
3477}

References mg_context::dd, mg_connection::dom_ctx, and mg_connection::phys_ctx.

Referenced by master_thread_run(), mg_cry_internal_wrap(), set_close_on_exec(), and set_gpass_option().

◆ fclose_on_exec()

static void fclose_on_exec ( struct mg_file_access * filep,
struct mg_connection * conn )
static

Definition at line 10196 of file civetweb.c.

10197{
10198 if (filep != NULL && filep->fp != NULL) {
10199#if defined(_WIN32)
10200 (void)conn; /* Unused. */
10201#else
10202 if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) {
10203 mg_cry_internal(conn,
10204 "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
10205 __func__,
10206 strerror(ERRNO));
10207 }
10208#endif
10209 }
10210}

References ERRNO, mg_file_access::fp, mg_cry_internal, and NULL.

Referenced by do_ssi_include(), handle_ssi_file_request(), handle_static_file_request(), mg_send_file_body(), and put_file().

◆ forward_body_data()

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

Definition at line 11184 of file civetweb.c.

11185{
11186 const char *expect;
11187 char buf[MG_BUF_LEN];
11188 int success = 0;
11189
11190 if (!conn) {
11191 return 0;
11192 }
11193
11194 expect = mg_get_header(conn, "Expect");
11195 DEBUG_ASSERT(fp != NULL);
11196 if (!fp) {
11197 mg_send_http_error(conn, 500, "%s", "Error: NULL File");
11198 return 0;
11199 }
11200
11201 if ((expect != NULL) && (mg_strcasecmp(expect, "100-continue") != 0)) {
11202 /* Client sent an "Expect: xyz" header and xyz is not 100-continue.
11203 */
11204 mg_send_http_error(conn, 417, "Error: Can not fulfill expectation");
11205 } else {
11206 if (expect != NULL) {
11207 (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
11208 conn->status_code = 100;
11209 } else {
11210 conn->status_code = 200;
11211 }
11212
11213 DEBUG_ASSERT(conn->consumed_content == 0);
11214
11215 if (conn->consumed_content != 0) {
11216 mg_send_http_error(conn, 500, "%s", "Error: Size mismatch");
11217 return 0;
11218 }
11219
11220 for (;;) {
11221 int nread = mg_read(conn, buf, sizeof(buf));
11222 if (nread <= 0) {
11223 success = (nread == 0);
11224 break;
11225 }
11226 if (push_all(conn->phys_ctx, fp, sock, ssl, buf, nread) != nread) {
11227 break;
11228 }
11229 }
11230
11231 /* Each error code path in this function must send an error */
11232 if (!success) {
11233 /* NOTE: Maybe some data has already been sent. */
11234 /* TODO (low): If some data has been sent, a correct error
11235 * reply can no longer be sent, so just close the connection */
11236 mg_send_http_error(conn, 500, "%s", "");
11237 }
11238 }
11239
11240 return success;
11241}
#define DEBUG_ASSERT(cond)
Definition civetweb.c:260
static int push_all(struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int len)
Definition civetweb.c:6162
int64_t consumed_content
Definition civetweb.c:2524

References mg_connection::consumed_content, DEBUG_ASSERT, MG_BUF_LEN, mg_get_header(), mg_printf(), mg_read(), mg_send_http_error(), mg_strcasecmp(), NULL, mg_connection::phys_ctx, push_all(), and mg_connection::status_code.

Referenced by handle_cgi_request(), and put_file().

◆ free_context()

static void free_context ( struct mg_context * ctx)
static

Definition at line 20249 of file civetweb.c.

20250{
20251 int i;
20252 struct mg_handler_info *tmp_rh;
20253
20254 if (ctx == NULL) {
20255 return;
20256 }
20257
20258 /* Call user callback */
20259 if (ctx->callbacks.exit_context) {
20260 ctx->callbacks.exit_context(ctx);
20261 }
20262
20263 /* All threads exited, no sync is needed. Destroy thread mutex and
20264 * condvars
20265 */
20266 (void)pthread_mutex_destroy(&ctx->thread_mutex);
20267
20268#if defined(ALTERNATIVE_QUEUE)
20269 mg_free(ctx->client_socks);
20270 if (ctx->client_wait_events != NULL) {
20271 for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) {
20272 event_destroy(ctx->client_wait_events[i]);
20273 }
20274 mg_free(ctx->client_wait_events);
20275 }
20276#else
20277 (void)pthread_cond_destroy(&ctx->sq_empty);
20278 (void)pthread_cond_destroy(&ctx->sq_full);
20279 mg_free(ctx->squeue);
20280#endif
20281
20282 /* Destroy other context global data structures mutex */
20283 (void)pthread_mutex_destroy(&ctx->nonce_mutex);
20284
20285#if defined(USE_LUA)
20286 (void)pthread_mutex_destroy(&ctx->lua_bg_mutex);
20287#endif
20288
20289 /* Deallocate config parameters */
20290 for (i = 0; i < NUM_OPTIONS; i++) {
20291 if (ctx->dd.config[i] != NULL) {
20292#if defined(_MSC_VER)
20293#pragma warning(suppress : 6001)
20294#endif
20295 mg_free(ctx->dd.config[i]);
20296 }
20297 }
20298
20299 /* Deallocate request handlers */
20300 while (ctx->dd.handlers) {
20301 tmp_rh = ctx->dd.handlers;
20302 ctx->dd.handlers = tmp_rh->next;
20303 mg_free(tmp_rh->uri);
20304 mg_free(tmp_rh);
20305 }
20306
20307#if defined(USE_MBEDTLS)
20308 if (ctx->dd.ssl_ctx != NULL) {
20309 mbed_sslctx_uninit(ctx->dd.ssl_ctx);
20310 mg_free(ctx->dd.ssl_ctx);
20311 ctx->dd.ssl_ctx = NULL;
20312 }
20313
20314#elif !defined(NO_SSL)
20315 /* Deallocate SSL context */
20316 if (ctx->dd.ssl_ctx != NULL) {
20317 void *ssl_ctx = (void *)ctx->dd.ssl_ctx;
20318 int callback_ret =
20320 ? 0
20321 : (ctx->callbacks.external_ssl_ctx(&ssl_ctx, ctx->user_data));
20322
20323 if (callback_ret == 0) {
20324 SSL_CTX_free(ctx->dd.ssl_ctx);
20325 }
20326 /* else: ignore error and omit SSL_CTX_free in case
20327 * callback_ret is 1 */
20328 }
20329#endif /* !NO_SSL */
20330
20331 /* Deallocate worker thread ID array */
20333
20334 /* Deallocate worker thread ID array */
20336
20337 /* deallocate system name string */
20338 mg_free(ctx->systemName);
20339
20340 /* Deallocate context itself */
20341 mg_free(ctx);
20342}
void(* exit_context)(const struct mg_context *ctx)
Definition civetweb.h:372
int(* external_ssl_ctx)(void **ssl_ctx, void *user_data)
Definition civetweb.h:278
pthread_t * worker_threadids
Definition civetweb.c:2379
struct mg_connection * worker_connections
Definition civetweb.c:2360
char * systemName
Definition civetweb.c:2411
pthread_mutex_t nonce_mutex
Definition civetweb.c:2427
SSL_CTX * ssl_ctx
Definition civetweb.c:2270
struct mg_handler_info * handlers
Definition civetweb.c:2272
struct mg_handler_info * next
Definition civetweb.c:2257

References mg_context::callbacks, mg_domain_context::config, mg_context::dd, mg_callbacks::exit_context, mg_callbacks::external_ssl_ctx, mg_domain_context::handlers, mg_free(), mg_handler_info::next, mg_context::nonce_mutex, NULL, NUM_OPTIONS, mg_context::sq_empty, mg_context::sq_full, mg_context::squeue, mg_domain_context::ssl_ctx, mg_context::systemName, mg_context::thread_mutex, mg_handler_info::uri, mg_context::user_data, mg_context::worker_connections, and mg_context::worker_threadids.

Referenced by mg_start2(), and mg_stop().

◆ get_first_ssl_listener_index()

static int get_first_ssl_listener_index ( const struct mg_context * ctx)
static

Definition at line 14121 of file civetweb.c.

14122{
14123 unsigned int i;
14124 int idx = -1;
14125 if (ctx) {
14126 for (i = 0; ((idx == -1) && (i < ctx->num_listening_sockets)); i++) {
14127 idx = ctx->listening_sockets[i].is_ssl ? ((int)(i)) : -1;
14128 }
14129 }
14130 return idx;
14131}

References socket::is_ssl, and mg_context::listening_sockets.

Referenced by handle_request().

◆ get_header()

static const char * get_header ( const struct mg_header * hdr,
int num_hdr,
const char * name )
static

Definition at line 3820 of file civetweb.c.

3821{
3822 int i;
3823 for (i = 0; i < num_hdr; i++) {
3824 if (!mg_strcasecmp(name, hdr[i].name)) {
3825 return hdr[i].value;
3826 }
3827 }
3828
3829 return NULL;
3830}
const char * name
Definition lsqlite3.c:2154
const char * value
Definition civetweb.h:145

References mg_strcasecmp(), name, NULL, and mg_header::value.

Referenced by get_host_from_request_info(), get_request(), get_response(), handle_cgi_request(), handle_request(), and mg_get_header().

◆ get_host_from_request_info()

static void get_host_from_request_info ( struct vec * host,
const struct mg_request_info * ri )
static

Definition at line 14136 of file civetweb.c.

14137{
14138 const char *host_header =
14139 get_header(ri->http_headers, ri->num_headers, "Host");
14140
14141 host->ptr = NULL;
14142 host->len = 0;
14143
14144 if (host_header != NULL) {
14145 const char *pos;
14146
14147 /* If the "Host" is an IPv6 address, like [::1], parse until ]
14148 * is found. */
14149 if (*host_header == '[') {
14150 pos = strchr(host_header, ']');
14151 if (!pos) {
14152 /* Malformed hostname starts with '[', but no ']' found */
14153 DEBUG_TRACE("%s", "Host name format error '[' without ']'");
14154 return;
14155 }
14156 /* terminate after ']' */
14157 host->ptr = host_header;
14158 host->len = (size_t)(pos + 1 - host_header);
14159 } else {
14160 /* Otherwise, a ':' separates hostname and port number */
14161 pos = strchr(host_header, ':');
14162 if (pos != NULL) {
14163 host->len = (size_t)(pos - host_header);
14164 } else {
14165 host->len = strlen(host_header);
14166 }
14167 host->ptr = host_header;
14168 }
14169 }
14170}
static const char * get_header(const struct mg_header *hdr, int num_hdr, const char *name)
Definition civetweb.c:3820
struct mg_header http_headers[MG_MAX_HEADERS]
Definition civetweb.h:179

References DEBUG_TRACE, get_header(), mg_request_info::http_headers, vec::len, NULL, mg_request_info::num_headers, and vec::ptr.

Referenced by switch_domain_context().

◆ get_http_header_len()

static int get_http_header_len ( const char * buf,
int buflen )
static

Definition at line 7952 of file civetweb.c.

7953{
7954 int i;
7955 for (i = 0; i < buflen; i++) {
7956 /* Do an unsigned comparison in some conditions below */
7957 const unsigned char c = (unsigned char)buf[i];
7958
7959 if ((c < 128) && ((char)c != '\r') && ((char)c != '\n')
7960 && !isprint(c)) {
7961 /* abort scan as soon as one malformed character is found */
7962 return -1;
7963 }
7964
7965 if (i < buflen - 1) {
7966 if ((buf[i] == '\n') && (buf[i + 1] == '\n')) {
7967 /* Two newline, no carriage return - not standard compliant,
7968 * but it should be accepted */
7969 return i + 2;
7970 }
7971 }
7972
7973 if (i < buflen - 3) {
7974 if ((buf[i] == '\r') && (buf[i + 1] == '\n') && (buf[i + 2] == '\r')
7975 && (buf[i + 3] == '\n')) {
7976 /* Two \r\n - standard compliant */
7977 return i + 4;
7978 }
7979 }
7980 }
7981
7982 return 0;
7983}

Referenced by parse_http_request(), parse_http_response(), and read_message().

◆ get_http_method_info()

static const struct mg_http_method_info * get_http_method_info ( const char * method)
static

Definition at line 10880 of file civetweb.c.

10881{
10882 /* Check if the method is known to the server. The list of all known
10883 * HTTP methods can be found here at
10884 * http://www.iana.org/assignments/http-methods/http-methods.xhtml
10885 */
10886 const struct mg_http_method_info *m = http_methods;
10887
10888 while (m->name) {
10889 if (!strcmp(m->name, method)) {
10890 return m;
10891 }
10892 m++;
10893 }
10894 return NULL;
10895}
static const struct mg_http_method_info http_methods[]
Definition civetweb.c:10813
const char * name
Definition civetweb.c:10803

References http_methods, mg_http_method_info::name, and NULL.

Referenced by is_valid_http_method().

◆ get_http_version()

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

Definition at line 3876 of file civetweb.c.

3877{
3878 if (!conn) {
3879 return NULL;
3880 }
3881
3883 return conn->request_info.http_version;
3884 }
3886 return conn->response_info.http_version;
3887 }
3888 return NULL;
3889}
int connection_type
Definition civetweb.c:2486
struct mg_response_info response_info
Definition civetweb.c:2495
const char * http_version
Definition civetweb.h:161
const char * http_version
Definition civetweb.h:194

References mg_connection::connection_type, CONNECTION_TYPE_REQUEST, CONNECTION_TYPE_RESPONSE, mg_request_info::http_version, mg_response_info::http_version, NULL, mg_connection::request_info, and mg_connection::response_info.

Referenced by should_keep_alive().

◆ get_message()

static int get_message ( struct mg_connection * conn,
char * ebuf,
size_t ebuf_len,
int * err )
static

Definition at line 18569 of file civetweb.c.

18570{
18571 if (ebuf_len > 0) {
18572 ebuf[0] = '\0';
18573 }
18574 *err = 0;
18575
18577
18578 if (!conn) {
18579 mg_snprintf(conn,
18580 NULL, /* No truncation check for ebuf */
18581 ebuf,
18582 ebuf_len,
18583 "%s",
18584 "Internal error");
18585 *err = 500;
18586 return 0;
18587 }
18588
18589 /* Set the time the request was received. This value should be used for
18590 * timeouts. */
18591 clock_gettime(CLOCK_MONOTONIC, &(conn->req_time));
18592
18593 conn->request_len =
18594 read_message(NULL, conn, conn->buf, conn->buf_size, &conn->data_len);
18595 DEBUG_ASSERT(conn->request_len < 0 || conn->data_len >= conn->request_len);
18596 if ((conn->request_len >= 0) && (conn->data_len < conn->request_len)) {
18597 mg_snprintf(conn,
18598 NULL, /* No truncation check for ebuf */
18599 ebuf,
18600 ebuf_len,
18601 "%s",
18602 "Invalid message size");
18603 *err = 500;
18604 return 0;
18605 }
18606
18607 if ((conn->request_len == 0) && (conn->data_len == conn->buf_size)) {
18608 mg_snprintf(conn,
18609 NULL, /* No truncation check for ebuf */
18610 ebuf,
18611 ebuf_len,
18612 "%s",
18613 "Message too large");
18614 *err = 413;
18615 return 0;
18616 }
18617
18618 if (conn->request_len <= 0) {
18619 if (conn->data_len > 0) {
18620 mg_snprintf(conn,
18621 NULL, /* No truncation check for ebuf */
18622 ebuf,
18623 ebuf_len,
18624 "%s",
18625 "Malformed message");
18626 *err = 400;
18627 } else {
18628 /* Server did not recv anything -> just close the connection */
18629 conn->must_close = 1;
18630 mg_snprintf(conn,
18631 NULL, /* No truncation check for ebuf */
18632 ebuf,
18633 ebuf_len,
18634 "%s",
18635 "No data received");
18636 *err = 0;
18637 }
18638 return 0;
18639 }
18640 return 1;
18641}
static void reset_per_request_attributes(struct mg_connection *conn)
Definition civetweb.c:17676
static int read_message(FILE *fp, struct mg_connection *conn, char *buf, int bufsiz, int *nread)
Definition civetweb.c:11107
struct timespec req_time
Definition civetweb.c:2515

References mg_connection::buf, mg_connection::buf_size, mg_connection::data_len, DEBUG_ASSERT, mg_snprintf(), mg_connection::must_close, NULL, read_message(), mg_connection::req_time, mg_connection::request_len, and reset_per_request_attributes().

Referenced by get_request(), and get_response().

◆ get_mime_type()

static void get_mime_type ( struct mg_connection * conn,
const char * path,
struct vec * vec )
static

Definition at line 8354 of file civetweb.c.

8355{
8356 struct vec ext_vec, mime_vec;
8357 const char *list, *ext;
8358 size_t path_len;
8359
8360 path_len = strlen(path);
8361
8362 if ((conn == NULL) || (vec == NULL)) {
8363 if (vec != NULL) {
8364 memset(vec, '\0', sizeof(struct vec));
8365 }
8366 return;
8367 }
8368
8369 /* Scan user-defined mime types first, in case user wants to
8370 * override default mime types. */
8371 list = conn->dom_ctx->config[EXTRA_MIME_TYPES];
8372 while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
8373 /* ext now points to the path suffix */
8374 ext = path + path_len - ext_vec.len;
8375 if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
8376 *vec = mime_vec;
8377 return;
8378 }
8379 }
8380
8382 vec->len = strlen(vec->ptr);
8383}
CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len)
Definition civetweb.c:3019
CIVETWEB_API const char * mg_get_builtin_mime_type(const char *path)
Definition civetweb.c:8332

References mg_domain_context::config, mg_connection::dom_ctx, EXTRA_MIME_TYPES, vec::len, mg_get_builtin_mime_type(), mg_strncasecmp(), next_option(), NULL, and vec::ptr.

Referenced by handle_static_file_request().

◆ get_month_index()

static int get_month_index ( const char * s)
static

Definition at line 7989 of file civetweb.c.

7990{
7991 size_t i;
7992
7993 for (i = 0; i < ARRAY_SIZE(month_names); i++) {
7994 if (!strcmp(s, month_names[i])) {
7995 return (int)i;
7996 }
7997 }
7998
7999 return -1;
8000}
static const char month_names[][4]
Definition civetweb.c:1815

References ARRAY_SIZE, month_names, and s.

Referenced by parse_date_string().

◆ get_option_index()

static int get_option_index ( const char * name)
static

Definition at line 3159 of file civetweb.c.

3160{
3161 int i;
3162
3163 for (i = 0; config_options[i].name != NULL; i++) {
3164 if (strcmp(config_options[i].name, name) == 0) {
3165 return i;
3166 }
3167 }
3168 return -1;
3169}
static const struct mg_option config_options[]
Definition civetweb.c:2062
const char * name
Definition civetweb.h:686

References config_options, mg_option::name, name, and NULL.

Referenced by mg_get_option(), mg_start2(), and mg_start_domain2().

◆ get_proto_name()

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

Definition at line 3574 of file civetweb.c.

3575{
3576#if defined(__clang__)
3577#pragma clang diagnostic push
3578#pragma clang diagnostic ignored "-Wunreachable-code"
3579 /* Depending on USE_WEBSOCKET and NO_SSL, some oft the protocols might be
3580 * not supported. Clang raises an "unreachable code" warning for parts of ?:
3581 * unreachable, but splitting into four different #ifdef clauses here is
3582 * more complicated.
3583 */
3584#endif
3585
3586 const struct mg_request_info *ri = &conn->request_info;
3587
3588 const char *proto = ((conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)
3589 ? (ri->is_ssl ? "wss" : "ws")
3590 : (ri->is_ssl ? "https" : "http"));
3591
3592 return proto;
3593
3594#if defined(__clang__)
3595#pragma clang diagnostic pop
3596#endif
3597}
const char * proto
Definition civetweb.c:18378

References mg_request_info::is_ssl, proto, mg_connection::protocol_type, PROTOCOL_TYPE_WEBSOCKET, and mg_connection::request_info.

Referenced by mg_construct_local_link().

◆ get_random()

static uint64_t get_random ( void )
static

Definition at line 5909 of file civetweb.c.

5910{
5911 static uint64_t lfsr = 0; /* Linear feedback shift register */
5912 static uint64_t lcg = 0; /* Linear congruential generator */
5913 uint64_t now = mg_get_current_time_ns();
5914
5915 if (lfsr == 0) {
5916 /* lfsr will be only 0 if has not been initialized,
5917 * so this code is called only once. */
5918 lfsr = mg_get_current_time_ns();
5919 lcg = mg_get_current_time_ns();
5920 } else {
5921 /* Get the next step of both random number generators. */
5922 lfsr = (lfsr >> 1)
5923 | ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1)
5924 << 63);
5925 lcg = lcg * 6364136223846793005LL + 1442695040888963407LL;
5926 }
5927
5928 /* Combining two pseudo-random number generators and a high resolution
5929 * part
5930 * of the current server time will make it hard (impossible?) to guess
5931 * the
5932 * next number. */
5933 return (lfsr ^ lcg ^ now);
5934}

References mg_get_current_time_ns().

Referenced by mg_start2(), and mg_start_domain2().

◆ get_rel_url_at_current_server()

static const char * get_rel_url_at_current_server ( const char * uri,
const struct mg_connection * conn )
static

Definition at line 18465 of file civetweb.c.

18466{
18467 const char *server_domain;
18468 size_t server_domain_len;
18469 size_t request_domain_len = 0;
18470 unsigned long port = 0;
18471 int i, auth_domain_check_enabled;
18472 const char *hostbegin = NULL;
18473 const char *hostend = NULL;
18474 const char *portbegin;
18475 char *portend;
18476
18477 auth_domain_check_enabled =
18479
18480 /* DNS is case insensitive, so use case insensitive string compare here
18481 */
18482 for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
18483 if (mg_strncasecmp(uri,
18486 == 0) {
18487
18488 hostbegin = uri + abs_uri_protocols[i].proto_len;
18489 hostend = strchr(hostbegin, '/');
18490 if (!hostend) {
18491 return 0;
18492 }
18493 portbegin = strchr(hostbegin, ':');
18494 if ((!portbegin) || (portbegin > hostend)) {
18495 port = abs_uri_protocols[i].default_port;
18496 request_domain_len = (size_t)(hostend - hostbegin);
18497 } else {
18498 port = strtoul(portbegin + 1, &portend, 10);
18499 if ((portend != hostend) || (port <= 0)
18500 || !is_valid_port(port)) {
18501 return 0;
18502 }
18503 request_domain_len = (size_t)(portbegin - hostbegin);
18504 }
18505 /* protocol found, port set */
18506 break;
18507 }
18508 }
18509
18510 if (!port) {
18511 /* port remains 0 if the protocol is not found */
18512 return 0;
18513 }
18514
18515 /* Check if the request is directed to a different server. */
18516 /* First check if the port is the same. */
18517 if (ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa)) != port) {
18518 /* Request is directed to a different port */
18519 return 0;
18520 }
18521
18522 /* Finally check if the server corresponds to the authentication
18523 * domain of the server (the server domain).
18524 * Allow full matches (like http://mydomain.com/path/file.ext), and
18525 * allow subdomain matches (like http://www.mydomain.com/path/file.ext),
18526 * but do not allow substrings (like
18527 * http://notmydomain.com/path/file.ext
18528 * or http://mydomain.com.fake/path/file.ext).
18529 */
18530 if (auth_domain_check_enabled) {
18531 server_domain = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
18532 server_domain_len = strlen(server_domain);
18533 if ((server_domain_len == 0) || (hostbegin == NULL)) {
18534 return 0;
18535 }
18536 if ((request_domain_len == server_domain_len)
18537 && (!memcmp(server_domain, hostbegin, server_domain_len))) {
18538 /* Request is directed to this server - full name match. */
18539 } else {
18540 if (request_domain_len < (server_domain_len + 2)) {
18541 /* Request is directed to another server: The server name
18542 * is longer than the request name.
18543 * Drop this case here to avoid overflows in the
18544 * following checks. */
18545 return 0;
18546 }
18547 if (hostbegin[request_domain_len - server_domain_len - 1] != '.') {
18548 /* Request is directed to another server: It could be a
18549 * substring
18550 * like notmyserver.com */
18551 return 0;
18552 }
18553 if (0
18554 != memcmp(server_domain,
18555 hostbegin + request_domain_len - server_domain_len,
18556 server_domain_len)) {
18557 /* Request is directed to another server:
18558 * The server name is different. */
18559 return 0;
18560 }
18561 }
18562 }
18563
18564 return hostend;
18565}
#define USA_IN_PORT_UNSAFE(s)
Definition civetweb.c:1860
static const struct @6 abs_uri_protocols[]
size_t proto_len
Definition civetweb.c:18379

References abs_uri_protocols, AUTHENTICATION_DOMAIN, mg_connection::client, mg_domain_context::config, mg_connection::dom_ctx, ENABLE_AUTH_DOMAIN_CHECK, is_valid_port(), socket::lsa, mg_strcasecmp(), mg_strncasecmp(), NULL, proto, proto_len, and USA_IN_PORT_UNSAFE.

Referenced by dav_move_file(), and process_new_connection().

◆ get_req_headers()

static int get_req_headers ( const struct mg_request_info * ri,
const char * name,
const char ** output,
int output_max_size )
static

Definition at line 3836 of file civetweb.c.

3840{
3841 int i;
3842 int cnt = 0;
3843 if (ri) {
3844 for (i = 0; i < ri->num_headers && cnt < output_max_size; i++) {
3845 if (!mg_strcasecmp(name, ri->http_headers[i].name)) {
3846 output[cnt++] = ri->http_headers[i].value;
3847 }
3848 }
3849 }
3850 return cnt;
3851}
static const char * output
const char * name
Definition civetweb.h:144

References mg_request_info::http_headers, mg_strcasecmp(), mg_header::name, name, mg_request_info::num_headers, output, and mg_header::value.

Referenced by should_switch_to_protocol().

◆ get_request()

static int get_request ( struct mg_connection * conn,
char * ebuf,
size_t ebuf_len,
int * err )
static

Definition at line 18645 of file civetweb.c.

18646{
18647 const char *cl;
18648
18649 conn->connection_type =
18650 CONNECTION_TYPE_REQUEST; /* request (valid of not) */
18651
18652 if (!get_message(conn, ebuf, ebuf_len, err)) {
18653 return 0;
18654 }
18655
18656 if (parse_http_request(conn->buf, conn->buf_size, &conn->request_info)
18657 <= 0) {
18658 mg_snprintf(conn,
18659 NULL, /* No truncation check for ebuf */
18660 ebuf,
18661 ebuf_len,
18662 "%s",
18663 "Bad request");
18664 *err = 400;
18665 return 0;
18666 }
18667
18668 /* Message is a valid request */
18669
18670 if (!switch_domain_context(conn)) {
18671 mg_snprintf(conn,
18672 NULL, /* No truncation check for ebuf */
18673 ebuf,
18674 ebuf_len,
18675 "%s",
18676 "Bad request: Host mismatch");
18677 *err = 400;
18678 return 0;
18679 }
18680
18681#if USE_ZLIB
18682 if (((cl = get_header(conn->request_info.http_headers,
18684 "Accept-Encoding"))
18685 != NULL)
18686 && strstr(cl, "gzip")) {
18687 conn->accept_gzip = 1;
18688 }
18689#endif
18690 if (((cl = get_header(conn->request_info.http_headers,
18692 "Transfer-Encoding"))
18693 != NULL)
18694 && mg_strcasecmp(cl, "identity")) {
18695 if (mg_strcasecmp(cl, "chunked")) {
18696 mg_snprintf(conn,
18697 NULL, /* No truncation check for ebuf */
18698 ebuf,
18699 ebuf_len,
18700 "%s",
18701 "Bad request");
18702 *err = 400;
18703 return 0;
18704 }
18705 conn->is_chunked = 1;
18706 conn->content_len = 0; /* not yet read */
18707 } else if ((cl = get_header(conn->request_info.http_headers,
18709 "Content-Length"))
18710 != NULL) {
18711 /* Request has content length set */
18712 char *endptr = NULL;
18713 conn->content_len = strtoll(cl, &endptr, 10);
18714 if ((endptr == cl) || (conn->content_len < 0)) {
18715 mg_snprintf(conn,
18716 NULL, /* No truncation check for ebuf */
18717 ebuf,
18718 ebuf_len,
18719 "%s",
18720 "Bad request");
18721 *err = 411;
18722 return 0;
18723 }
18724 /* Publish the content length back to the request info. */
18726 } else {
18727 /* There is no exception, see RFC7230. */
18728 conn->content_len = 0;
18729 }
18730
18731 return 1;
18732}
static int parse_http_request(char *buf, int len, struct mg_request_info *ri)
Definition civetweb.c:10915
static int switch_domain_context(struct mg_connection *conn)
Definition civetweb.c:14174
static int get_message(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
Definition civetweb.c:18569
int64_t content_len
Definition civetweb.c:2518
long long content_length
Definition civetweb.h:168

References mg_connection::accept_gzip, mg_connection::buf, mg_connection::buf_size, mg_connection::connection_type, CONNECTION_TYPE_REQUEST, mg_connection::content_len, mg_request_info::content_length, get_header(), get_message(), mg_request_info::http_headers, mg_connection::is_chunked, mg_snprintf(), mg_strcasecmp(), NULL, mg_request_info::num_headers, parse_http_request(), mg_connection::request_info, and switch_domain_context().

Referenced by process_new_connection().

◆ get_request_handler()

static int get_request_handler ( struct mg_connection * conn,
int handler_type,
mg_request_handler * handler,
struct mg_websocket_subprotocols ** subprotocols,
mg_websocket_connect_handler * connect_handler,
mg_websocket_ready_handler * ready_handler,
mg_websocket_data_handler * data_handler,
mg_websocket_close_handler * close_handler,
mg_authorization_handler * auth_handler,
void ** cbdata,
struct mg_handler_info ** handler_info )
static

Definition at line 14560 of file civetweb.c.

14571{
14572 const struct mg_request_info *request_info = mg_get_request_info(conn);
14573 if (request_info) {
14574 const char *uri = request_info->local_uri;
14575 size_t urilen = strlen(uri);
14576 struct mg_handler_info *tmp_rh;
14577 int step, matched;
14578
14579 if (!conn || !conn->phys_ctx || !conn->dom_ctx) {
14580 return 0;
14581 }
14582
14584
14585 for (step = 0; step < 3; step++) {
14586 for (tmp_rh = conn->dom_ctx->handlers; tmp_rh != NULL;
14587 tmp_rh = tmp_rh->next) {
14588 if (tmp_rh->handler_type != handler_type) {
14589 continue;
14590 }
14591 if (step == 0) {
14592 /* first try for an exact match */
14593 matched = (tmp_rh->uri_len == urilen)
14594 && (strcmp(tmp_rh->uri, uri) == 0);
14595 } else if (step == 1) {
14596 /* next try for a partial match, we will accept
14597 uri/something */
14598 matched =
14599 (tmp_rh->uri_len < urilen)
14600 && (uri[tmp_rh->uri_len] == '/')
14601 && (memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0);
14602 } else {
14603 /* finally try for pattern match */
14604 matched =
14605 match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0;
14606 }
14607 if (matched) {
14609 *subprotocols = tmp_rh->subprotocols;
14611 *ready_handler = tmp_rh->ready_handler;
14612 *data_handler = tmp_rh->data_handler;
14613 *close_handler = tmp_rh->close_handler;
14614 } else if (handler_type == REQUEST_HANDLER) {
14615 if (tmp_rh->removing) {
14616 /* Treat as none found */
14617 step = 2;
14618 break;
14619 }
14620 *handler = tmp_rh->handler;
14621 /* Acquire handler and give it back */
14622 tmp_rh->refcount++;
14623 *handler_info = tmp_rh;
14624 } else { /* AUTH_HANDLER */
14625 *auth_handler = tmp_rh->auth_handler;
14626 }
14627 *cbdata = tmp_rh->cbdata;
14629 return 1;
14630 }
14631 }
14632 }
14633
14635 }
14636 return 0; /* none found */
14637}
CIVETWEB_API const struct mg_request_info * mg_get_request_info(const struct mg_connection *conn)
Definition civetweb.c:3521
mg_request_handler handler
Definition civetweb.c:2237
mg_authorization_handler auth_handler
Definition civetweb.c:2251
mg_websocket_close_handler close_handler
Definition civetweb.c:2245
unsigned int refcount
Definition civetweb.c:2238
mg_websocket_connect_handler connect_handler
Definition civetweb.c:2242
struct mg_websocket_subprotocols * subprotocols
Definition civetweb.c:2248
mg_websocket_data_handler data_handler
Definition civetweb.c:2244
mg_websocket_ready_handler ready_handler
Definition civetweb.c:2243

References mg_handler_info::auth_handler, mg_handler_info::cbdata, mg_handler_info::close_handler, mg_handler_info::connect_handler, mg_handler_info::data_handler, mg_connection::dom_ctx, mg_handler_info::handler, mg_handler_info::handler_type, mg_domain_context::handlers, mg_request_info::local_uri, mg_get_request_info(), mg_lock_context(), mg_unlock_context(), mg_handler_info::next, NULL, mg_connection::phys_ctx, mg_handler_info::ready_handler, mg_handler_info::refcount, mg_handler_info::removing, REQUEST_HANDLER, mg_handler_info::subprotocols, mg_handler_info::uri, mg_handler_info::uri_len, and WEBSOCKET_HANDLER.

Referenced by handle_request().

◆ get_response()

static int get_response ( struct mg_connection * conn,
char * ebuf,
size_t ebuf_len,
int * err )
static

Definition at line 18737 of file civetweb.c.

18738{
18739 const char *cl;
18740
18741 conn->connection_type =
18742 CONNECTION_TYPE_RESPONSE; /* response (valid or not) */
18743
18744 if (!get_message(conn, ebuf, ebuf_len, err)) {
18745 return 0;
18746 }
18747
18748 if (parse_http_response(conn->buf, conn->buf_size, &conn->response_info)
18749 <= 0) {
18750 mg_snprintf(conn,
18751 NULL, /* No truncation check for ebuf */
18752 ebuf,
18753 ebuf_len,
18754 "%s",
18755 "Bad response");
18756 *err = 400;
18757 return 0;
18758 }
18759
18760 /* Message is a valid response */
18761
18762 if (((cl = get_header(conn->response_info.http_headers,
18764 "Transfer-Encoding"))
18765 != NULL)
18766 && mg_strcasecmp(cl, "identity")) {
18767 if (mg_strcasecmp(cl, "chunked")) {
18768 mg_snprintf(conn,
18769 NULL, /* No truncation check for ebuf */
18770 ebuf,
18771 ebuf_len,
18772 "%s",
18773 "Bad request");
18774 *err = 400;
18775 return 0;
18776 }
18777 conn->is_chunked = 1;
18778 conn->content_len = 0; /* not yet read */
18779 } else if ((cl = get_header(conn->response_info.http_headers,
18781 "Content-Length"))
18782 != NULL) {
18783 char *endptr = NULL;
18784 conn->content_len = strtoll(cl, &endptr, 10);
18785 if ((endptr == cl) || (conn->content_len < 0)) {
18786 mg_snprintf(conn,
18787 NULL, /* No truncation check for ebuf */
18788 ebuf,
18789 ebuf_len,
18790 "%s",
18791 "Bad request");
18792 *err = 411;
18793 return 0;
18794 }
18795 /* Publish the content length back to the response info. */
18797
18798 /* TODO: check if it is still used in response_info */
18800
18801 /* TODO: we should also consider HEAD method */
18802 if (conn->response_info.status_code == 304) {
18803 conn->content_len = 0;
18804 }
18805 } else {
18806 /* TODO: we should also consider HEAD method */
18807 if (((conn->response_info.status_code >= 100)
18808 && (conn->response_info.status_code <= 199))
18809 || (conn->response_info.status_code == 204)
18810 || (conn->response_info.status_code == 304)) {
18811 conn->content_len = 0;
18812 } else {
18813 conn->content_len = -1; /* unknown content length */
18814 }
18815 }
18816
18817 return 1;
18818}
static int parse_http_response(char *buf, int len, struct mg_response_info *ri)
Definition civetweb.c:11001
long long content_length
Definition civetweb.h:196
struct mg_header http_headers[MG_MAX_HEADERS]
Definition civetweb.h:200

References mg_connection::buf, mg_connection::buf_size, mg_connection::connection_type, CONNECTION_TYPE_RESPONSE, mg_connection::content_len, mg_request_info::content_length, mg_response_info::content_length, get_header(), get_message(), mg_response_info::http_headers, mg_connection::is_chunked, mg_snprintf(), mg_strcasecmp(), NULL, mg_response_info::num_headers, parse_http_response(), mg_connection::request_info, mg_connection::response_info, and mg_response_info::status_code.

Referenced by mg_connect_websocket_client_impl(), mg_download(), and mg_get_response().

◆ get_system_name()

static void get_system_name ( char ** sysName)
static

Definition at line 20389 of file civetweb.c.

20390{
20391#if defined(_WIN32)
20392 char name[128];
20393 DWORD dwVersion = 0;
20394 DWORD dwMajorVersion = 0;
20395 DWORD dwMinorVersion = 0;
20396 DWORD dwBuild = 0;
20397 BOOL wowRet, isWoW = FALSE;
20398
20399#if defined(_MSC_VER)
20400#pragma warning(push)
20401 /* GetVersion was declared deprecated */
20402#pragma warning(disable : 4996)
20403#endif
20404 dwVersion = GetVersion();
20405#if defined(_MSC_VER)
20406#pragma warning(pop)
20407#endif
20408
20409 dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
20410 dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
20411 dwBuild = ((dwVersion < 0x80000000) ? (DWORD)(HIWORD(dwVersion)) : 0);
20412 (void)dwBuild;
20413
20414 wowRet = IsWow64Process(GetCurrentProcess(), &isWoW);
20415
20416 sprintf(name,
20417 "Windows %u.%u%s",
20418 (unsigned)dwMajorVersion,
20419 (unsigned)dwMinorVersion,
20420 (wowRet ? (isWoW ? " (WoW64)" : "") : " (?)"));
20421
20422 *sysName = mg_strdup(name);
20423
20424#elif defined(__ZEPHYR__)
20425 *sysName = mg_strdup("Zephyr OS");
20426#else
20427 struct utsname name;
20428 memset(&name, 0, sizeof(name));
20429 uname(&name);
20430 *sysName = mg_strdup(name.sysname);
20431#endif
20432}

References FALSE, mg_strdup(), and name.

Referenced by mg_start2().

◆ get_uri_type()

static int get_uri_type ( const char * uri)
static

Definition at line 18395 of file civetweb.c.

18396{
18397 int i;
18398 const char *hostend, *portbegin;
18399 char *portend;
18400 unsigned long port;
18401
18402 /* According to the HTTP standard
18403 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
18404 * URI can be an asterisk (*) or should start with slash (relative uri),
18405 * or it should start with the protocol (absolute uri). */
18406 if ((uri[0] == '*') && (uri[1] == '\0')) {
18407 /* asterisk */
18408 return 1;
18409 }
18410
18411 /* Valid URIs according to RFC 3986
18412 * (https://www.ietf.org/rfc/rfc3986.txt)
18413 * must only contain reserved characters :/?#[]@!$&'()*+,;=
18414 * and unreserved characters A-Z a-z 0-9 and -._~
18415 * and % encoded symbols.
18416 */
18417 for (i = 0; uri[i] != 0; i++) {
18418 if (uri[i] < 33) {
18419 /* control characters and spaces are invalid */
18420 return 0;
18421 }
18422 /* Allow everything else here (See #894) */
18423 }
18424
18425 /* A relative uri starts with a / character */
18426 if (uri[0] == '/') {
18427 /* relative uri */
18428 return 2;
18429 }
18430
18431 /* It could be an absolute uri: */
18432 /* This function only checks if the uri is valid, not if it is
18433 * addressing the current server. So civetweb can also be used
18434 * as a proxy server. */
18435 for (i = 0; abs_uri_protocols[i].proto != NULL; i++) {
18436 if (mg_strncasecmp(uri,
18439 == 0) {
18440
18441 hostend = strchr(uri + abs_uri_protocols[i].proto_len, '/');
18442 if (!hostend) {
18443 return 0;
18444 }
18445 portbegin = strchr(uri + abs_uri_protocols[i].proto_len, ':');
18446 if (!portbegin) {
18447 return 3;
18448 }
18449
18450 port = strtoul(portbegin + 1, &portend, 10);
18451 if ((portend != hostend) || (port <= 0) || !is_valid_port(port)) {
18452 return 0;
18453 }
18454
18455 return 4;
18456 }
18457 }
18458
18459 return 0;
18460}

References abs_uri_protocols, is_valid_port(), mg_strncasecmp(), NULL, proto, and proto_len.

Referenced by dav_move_file(), and process_new_connection().

◆ gmt_time_string()

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

Definition at line 3341 of file civetweb.c.

3342{
3343#if !defined(REENTRANT_TIME)
3344 struct tm *tm;
3345
3346 tm = ((t != NULL) ? gmtime(t) : NULL);
3347 if (tm != NULL) {
3348#else
3349 struct tm _tm;
3350 struct tm *tm = &_tm;
3351
3352 if (t != NULL) {
3353 gmtime_r(t, tm);
3354#endif
3355 strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
3356 } else {
3357 mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
3358 }
3359}

References mg_strlcpy(), and NULL.

Referenced by handle_directory_request(), handle_not_modified_static_file_request(), handle_request(), handle_ssi_file_request(), handle_static_file_request(), mg_get_context_info(), and print_props().

◆ handle_cgi_request()

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

Definition at line 11587 of file civetweb.c.

11590{
11591 char *buf;
11592 size_t buflen;
11593 int headers_len, data_len, i, truncated;
11594 int fdin[2] = {-1, -1}, fdout[2] = {-1, -1}, fderr[2] = {-1, -1};
11595 const char *status, *status_text;
11596 char *pbuf, dir[UTF8_PATH_MAX], *p;
11597 struct mg_request_info ri;
11598 struct cgi_environment blk;
11599 FILE *in = NULL, *out = NULL, *err = NULL;
11600 struct mg_file fout = STRUCT_FILE_INITIALIZER;
11601 pid_t pid = (pid_t)-1;
11602 struct process_control_data *proc = NULL;
11603 char *cfg_buffering = conn->dom_ctx->config[CGI_BUFFERING + cgi_config_idx];
11604 int no_buffering = 0;
11605
11606#if defined(USE_TIMERS)
11607 double cgi_timeout;
11608 if (conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) {
11609 /* Get timeout in seconds */
11610 cgi_timeout =
11611 atof(conn->dom_ctx->config[CGI_TIMEOUT + cgi_config_idx]) * 0.001;
11612 } else {
11613 cgi_timeout =
11614 atof(config_options[REQUEST_TIMEOUT].default_value) * 0.001;
11615 }
11616#endif
11617 if (cfg_buffering != NULL) {
11618 if (!mg_strcasecmp(cfg_buffering, "no")) {
11619 no_buffering = 1;
11620 }
11621 }
11622
11623 buf = NULL;
11624 buflen = conn->phys_ctx->max_request_size;
11625 i = prepare_cgi_environment(conn, prog, &blk, cgi_config_idx);
11626 if (i != 0) {
11627 blk.buf = NULL;
11628 blk.var = NULL;
11629 goto done;
11630 }
11631
11632 /* CGI must be executed in its own directory. 'dir' must point to the
11633 * directory containing executable program, 'p' must point to the
11634 * executable program name relative to 'dir'. */
11635 (void)mg_snprintf(conn, &truncated, dir, sizeof(dir), "%s", prog);
11636
11637 if (truncated) {
11638 mg_cry_internal(conn, "Error: CGI program \"%s\": Path too long", prog);
11639 mg_send_http_error(conn, 500, "Error: %s", "CGI path too long");
11640 goto done;
11641 }
11642
11643 if ((p = strrchr(dir, '/')) != NULL) {
11644 *p++ = '\0';
11645 } else {
11646 dir[0] = '.';
11647 dir[1] = '\0';
11648 p = (char *)prog;
11649 }
11650
11651 if ((pipe(fdin) != 0) || (pipe(fdout) != 0) || (pipe(fderr) != 0)) {
11652 status = strerror(ERRNO);
11654 conn,
11655 "Error: CGI program \"%s\": Can not create CGI pipes: %s",
11656 prog,
11657 status);
11658 mg_send_http_error(conn,
11659 500,
11660 "Error: Cannot create CGI pipe: %s",
11661 status);
11662 goto done;
11663 }
11664
11665 proc = (struct process_control_data *)
11666 mg_malloc_ctx(sizeof(struct process_control_data), conn->phys_ctx);
11667 if (proc == NULL) {
11668 mg_cry_internal(conn, "Error: CGI program \"%s\": Out or memory", prog);
11669 mg_send_http_error(conn, 500, "Error: Out of memory [%s]", prog);
11670 goto done;
11671 }
11672
11673 DEBUG_TRACE("CGI: spawn %s %s\n", dir, p);
11675 conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir, cgi_config_idx);
11676
11677 if (pid == (pid_t)-1) {
11678 status = strerror(ERRNO);
11680 conn,
11681 "Error: CGI program \"%s\": Can not spawn CGI process: %s",
11682 prog,
11683 status);
11684 mg_send_http_error(conn, 500, "Error: Cannot spawn CGI process");
11685 mg_free(proc);
11686 proc = NULL;
11687 goto done;
11688 }
11689
11690 /* Store data in shared process_control_data */
11691 proc->pid = pid;
11692 proc->references = 1;
11693
11694#if defined(USE_TIMERS)
11695 if (cgi_timeout > 0.0) {
11696 proc->references = 2;
11697
11698 // Start a timer for CGI
11699 timer_add(conn->phys_ctx,
11700 cgi_timeout /* in seconds */,
11701 0.0,
11702 1,
11704 (void *)proc,
11705 NULL);
11706 }
11707#endif
11708
11709 /* Parent closes only one side of the pipes.
11710 * If we don't mark them as closed, close() attempt before
11711 * return from this function throws an exception on Windows.
11712 * Windows does not like when closed descriptor is closed again. */
11713 (void)close(fdin[0]);
11714 (void)close(fdout[1]);
11715 (void)close(fderr[1]);
11716 fdin[0] = fdout[1] = fderr[1] = -1;
11717
11718 if (((in = fdopen(fdin[1], "wb")) == NULL)
11719 || ((out = fdopen(fdout[0], "rb")) == NULL)
11720 || ((err = fdopen(fderr[0], "rb")) == NULL)) {
11721 status = strerror(ERRNO);
11722 mg_cry_internal(conn,
11723 "Error: CGI program \"%s\": Can not open fd: %s",
11724 prog,
11725 status);
11726 mg_send_http_error(conn,
11727 500,
11728 "Error: CGI can not open fd\nfdopen: %s",
11729 status);
11730 goto done;
11731 }
11732
11733 setbuf(in, NULL);
11734 setbuf(out, NULL);
11735 setbuf(err, NULL);
11736 fout.access.fp = out;
11737
11738 if ((conn->content_len != 0) || (conn->is_chunked)) {
11739 DEBUG_TRACE("CGI: send body data (%" INT64_FMT ")\n",
11740 conn->content_len);
11741
11742 /* This is a POST/PUT request, or another request with body data. */
11743 if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
11744 /* Error sending the body data */
11746 conn,
11747 "Error: CGI program \"%s\": Forward body data failed",
11748 prog);
11749 goto done;
11750 }
11751 }
11752
11753 /* Close so child gets an EOF. */
11754 fclose(in);
11755 in = NULL;
11756 fdin[1] = -1;
11757
11758 /* Now read CGI reply into a buffer. We need to set correct
11759 * status code, thus we need to see all HTTP headers first.
11760 * Do not send anything back to client, until we buffer in all
11761 * HTTP headers. */
11762 data_len = 0;
11763 buf = (char *)mg_malloc_ctx(buflen, conn->phys_ctx);
11764 if (buf == NULL) {
11765 mg_send_http_error(conn,
11766 500,
11767 "Error: Not enough memory for CGI buffer (%u bytes)",
11768 (unsigned int)buflen);
11770 conn,
11771 "Error: CGI program \"%s\": Not enough memory for buffer (%u "
11772 "bytes)",
11773 prog,
11774 (unsigned int)buflen);
11775 goto done;
11776 }
11777
11778 DEBUG_TRACE("CGI: %s", "wait for response");
11779 headers_len = read_message(out, conn, buf, (int)buflen, &data_len);
11780 DEBUG_TRACE("CGI: response: %li", (signed long)headers_len);
11781
11782 if (headers_len <= 0) {
11783
11784 /* Could not parse the CGI response. Check if some error message on
11785 * stderr. */
11786 i = pull_all(err, conn, buf, (int)buflen);
11787 if (i > 0) {
11788 /* CGI program explicitly sent an error */
11789 /* Write the error message to the internal log */
11790 mg_cry_internal(conn,
11791 "Error: CGI program \"%s\" sent error "
11792 "message: [%.*s]",
11793 prog,
11794 i,
11795 buf);
11796 /* Don't send the error message back to the client */
11797 mg_send_http_error(conn,
11798 500,
11799 "Error: CGI program \"%s\" failed.",
11800 prog);
11801 } else {
11802 /* CGI program did not explicitly send an error, but a broken
11803 * respon header */
11804 mg_cry_internal(conn,
11805 "Error: CGI program sent malformed or too big "
11806 "(>%u bytes) HTTP headers: [%.*s]",
11807 (unsigned)buflen,
11808 data_len,
11809 buf);
11810
11811 mg_send_http_error(conn,
11812 500,
11813 "Error: CGI program sent malformed or too big "
11814 "(>%u bytes) HTTP headers: [%.*s]",
11815 (unsigned)buflen,
11816 data_len,
11817 buf);
11818 }
11819
11820 /* in both cases, abort processing CGI */
11821 goto done;
11822 }
11823
11824 pbuf = buf;
11825 buf[headers_len - 1] = '\0';
11826 ri.num_headers = parse_http_headers(&pbuf, ri.http_headers);
11827
11828 /* Make up and send the status line */
11829 status_text = "OK";
11830 if ((status = get_header(ri.http_headers, ri.num_headers, "Status"))
11831 != NULL) {
11832 conn->status_code = atoi(status);
11833 status_text = status;
11834 while (isdigit((unsigned char)*status_text) || *status_text == ' ') {
11835 status_text++;
11836 }
11837 } else if (get_header(ri.http_headers, ri.num_headers, "Location")
11838 != NULL) {
11839 conn->status_code = 307;
11840 } else {
11841 conn->status_code = 200;
11842 }
11843
11844 if (!should_keep_alive(conn)) {
11845 conn->must_close = 1;
11846 }
11847
11848 DEBUG_TRACE("CGI: response %u %s", conn->status_code, status_text);
11849
11850 (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text);
11851
11852 /* Send headers */
11853 for (i = 0; i < ri.num_headers; i++) {
11854 DEBUG_TRACE("CGI header: %s: %s",
11855 ri.http_headers[i].name,
11856 ri.http_headers[i].value);
11857 mg_printf(conn,
11858 "%s: %s\r\n",
11859 ri.http_headers[i].name,
11860 ri.http_headers[i].value);
11861 }
11862 mg_write(conn, "\r\n", 2);
11863
11864 /* Send chunk of data that may have been read after the headers */
11865 mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len));
11866
11867 /* Read the rest of CGI output and send to the client */
11868 DEBUG_TRACE("CGI: %s", "forward all data");
11869 send_file_data(conn, &fout, 0, INT64_MAX, no_buffering); /* send CGI data */
11870 DEBUG_TRACE("CGI: %s", "all data sent");
11871
11872done:
11873 mg_free(blk.var);
11874 mg_free(blk.buf);
11875
11876 if (pid != (pid_t)-1) {
11877 abort_cgi_process((void *)proc);
11878 }
11879
11880 if (fdin[0] != -1) {
11881 close(fdin[0]);
11882 }
11883 if (fdout[1] != -1) {
11884 close(fdout[1]);
11885 }
11886 if (fderr[1] != -1) {
11887 close(fderr[1]);
11888 }
11889
11890 if (in != NULL) {
11891 fclose(in);
11892 } else if (fdin[1] != -1) {
11893 close(fdin[1]);
11894 }
11895
11896 if (out != NULL) {
11897 fclose(out);
11898 } else if (fdout[0] != -1) {
11899 close(fdout[0]);
11900 }
11901
11902 if (err != NULL) {
11903 fclose(err);
11904 } else if (fderr[0] != -1) {
11905 close(fderr[0]);
11906 }
11907
11908 mg_free(buf);
11909}
static pid_t spawn_process(struct mg_connection *conn, const char *prog, char *envblk, char *envp[], int fdin[2], int fdout[2], int fderr[2], const char *dir, int cgi_config_idx)
Definition civetweb.c:5768
static int abort_cgi_process(void *data)
Definition civetweb.c:11552
static int should_keep_alive(const struct mg_connection *conn)
Definition civetweb.c:3984
static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
Definition civetweb.c:10725
static int forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
Definition civetweb.c:11184
CIVETWEB_API int mg_write(struct mg_connection *conn, const void *buf, size_t len)
Definition civetweb.c:6779
static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
Definition civetweb.c:6439
static int prepare_cgi_environment(struct mg_connection *conn, const char *prog, struct cgi_environment *env, int cgi_config_idx)
Definition civetweb.c:11354
unsigned int max_request_size
Definition civetweb.c:2401

References abort_cgi_process(), mg_file::access, cgi_environment::buf, CGI_BUFFERING, mg_domain_context::config, config_options, mg_connection::content_len, DEBUG_TRACE, mg_connection::dom_ctx, ERRNO, forward_body_data(), mg_file_access::fp, get_header(), mg_request_info::http_headers, INT64_FMT, INT64_MAX, INVALID_SOCKET, mg_connection::is_chunked, mg_context::max_request_size, mg_cry_internal, mg_free(), mg_malloc_ctx, mg_printf(), mg_send_http_error(), mg_snprintf(), mg_strcasecmp(), mg_write(), mg_connection::must_close, mg_header::name, NULL, mg_request_info::num_headers, parse_http_headers(), mg_connection::phys_ctx, process_control_data::pid, prepare_cgi_environment(), pull_all(), read_message(), process_control_data::references, REQUEST_TIMEOUT, send_file_data(), should_keep_alive(), spawn_process(), mg_connection::status_code, STRUCT_FILE_INITIALIZER, UTF8_PATH_MAX, mg_header::value, and cgi_environment::var.

Referenced by handle_file_based_request().

◆ handle_directory_request()

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

Definition at line 9942 of file civetweb.c.

9943{
9944 size_t i;
9945 int sort_direction;
9946 struct dir_scan_data data = {NULL, 0, 128};
9947 char date[64], *esc, *p;
9948 const char *title;
9949 time_t curtime = time(NULL);
9950
9951 if (!conn) {
9952 return;
9953 }
9954
9955 if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
9956 mg_send_http_error(conn,
9957 500,
9958 "Error: Cannot open directory\nopendir(%s): %s",
9959 dir,
9960 strerror(ERRNO));
9961 return;
9962 }
9963
9964 gmt_time_string(date, sizeof(date), &curtime);
9965
9966 esc = NULL;
9967 title = conn->request_info.local_uri;
9968 if (title[strcspn(title, "&<>")]) {
9969 /* HTML escaping needed */
9970 esc = (char *)mg_malloc(strlen(title) * 5 + 1);
9971 if (esc) {
9972 for (i = 0, p = esc; title[i]; i++, p += strlen(p)) {
9973 mg_strlcpy(p, title + i, 2);
9974 if (*p == '&') {
9975 strcpy(p, "&amp;");
9976 } else if (*p == '<') {
9977 strcpy(p, "&lt;");
9978 } else if (*p == '>') {
9979 strcpy(p, "&gt;");
9980 }
9981 }
9982 } else {
9983 title = "";
9984 }
9985 }
9986
9987 sort_direction = ((conn->request_info.query_string != NULL)
9988 && (conn->request_info.query_string[0] != '\0')
9989 && (conn->request_info.query_string[1] == 'd'))
9990 ? 'a'
9991 : 'd';
9992
9993 conn->must_close = 1;
9994
9995 /* Create 200 OK response */
9996 mg_response_header_start(conn, 200);
10000 "Content-Type",
10001 "text/html; charset=utf-8",
10002 -1);
10003
10004 /* Send all headers */
10006
10007 /* Body */
10008 mg_printf(conn,
10009 "<!DOCTYPE html>"
10010 "<html><head><title>Index of %s</title>"
10011 "<style>th {text-align: left;}</style></head>"
10012 "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
10013 "<tr><th><a href=\"?n%c\">Name</a></th>"
10014 "<th><a href=\"?d%c\">Modified</a></th>"
10015 "<th><a href=\"?s%c\">Size</a></th></tr>"
10016 "<tr><td colspan=\"3\"><hr></td></tr>",
10017 esc ? esc : title,
10018 esc ? esc : title,
10019 sort_direction,
10020 sort_direction,
10021 sort_direction);
10022 mg_free(esc);
10023
10024 /* Print first entry - link to a parent directory */
10025 mg_printf(conn,
10026 "<tr><td><a href=\"%s\">%s</a></td>"
10027 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
10028 "..",
10029 "Parent directory",
10030 "-",
10031 "-");
10032
10033 /* Sort and print directory entries */
10034 if (data.entries != NULL) {
10035 mg_sort(data.entries,
10036 data.num_entries,
10037 sizeof(data.entries[0]),
10039 (void *)conn->request_info.query_string);
10040 for (i = 0; i < data.num_entries; i++) {
10041 print_dir_entry(conn, &data.entries[i]);
10042 mg_free(data.entries[i].file_name);
10043 }
10044 mg_free(data.entries);
10045 }
10046
10047 mg_printf(conn, "%s", "</table></pre></body></html>");
10048 conn->status_code = 200;
10049}
static int print_dir_entry(struct mg_connection *conn, struct de *de)
Definition civetweb.c:9609
static int compare_dir_entries(const void *p1, const void *p2, void *arg)
Definition civetweb.c:9716
static void gmt_time_string(char *buf, size_t buf_len, time_t *t)
Definition civetweb.c:3341
static int dir_scan_callback(struct de *de, void *data)
Definition civetweb.c:9911
static int scan_directory(struct mg_connection *conn, const char *dir, void *data, int(*cb)(struct de *, void *))
Definition civetweb.c:9777
const char * query_string
Definition civetweb.h:162

References compare_dir_entries(), dir_scan_callback(), dir_scan_data::entries, ERRNO, de::file_name, gmt_time_string(), mg_request_info::local_uri, mg_free(), mg_malloc(), mg_printf(), mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_send_http_error(), mg_strlcpy(), mg_connection::must_close, NULL, dir_scan_data::num_entries, print_dir_entry(), mg_request_info::query_string, mg_connection::request_info, scan_directory(), send_additional_header(), send_static_cache_header(), and mg_connection::status_code.

Referenced by handle_request(), and mg_send_mime_file2().

◆ handle_file_based_request()

static void handle_file_based_request ( struct mg_connection * conn,
const char * path,
struct mg_file * filep )
static

Definition at line 15354 of file civetweb.c.

15357{
15358#if !defined(NO_CGI)
15359 int cgi_config_idx, inc, max;
15360#endif
15361
15362 if (!conn || !conn->dom_ctx) {
15363 return;
15364 }
15365
15366#if defined(USE_LUA)
15367 if (match_prefix_strlen(conn->dom_ctx->config[LUA_SERVER_PAGE_EXTENSIONS],
15368 path)
15369 > 0) {
15370 if (is_in_script_path(conn, path)) {
15371 /* Lua server page: an SSI like page containing mostly plain
15372 * html code plus some tags with server generated contents. */
15373 handle_lsp_request(conn, path, file, NULL);
15374 } else {
15375 /* Script was in an illegal path */
15376 mg_send_http_error(conn, 403, "%s", "Forbidden");
15377 }
15378 return;
15379 }
15380
15381 if (match_prefix_strlen(conn->dom_ctx->config[LUA_SCRIPT_EXTENSIONS], path)
15382 > 0) {
15383 if (is_in_script_path(conn, path)) {
15384 /* Lua in-server module script: a CGI like script used to
15385 * generate the entire reply. */
15386 mg_exec_lua_script(conn, path, NULL);
15387 } else {
15388 /* Script was in an illegal path */
15389 mg_send_http_error(conn, 403, "%s", "Forbidden");
15390 }
15391 return;
15392 }
15393#endif
15394
15395#if defined(USE_DUKTAPE)
15396 if (match_prefix_strlen(conn->dom_ctx->config[DUKTAPE_SCRIPT_EXTENSIONS],
15397 path)
15398 > 0) {
15399 if (is_in_script_path(conn, path)) {
15400 /* Call duktape to generate the page */
15401 mg_exec_duktape_script(conn, path);
15402 } else {
15403 /* Script was in an illegal path */
15404 mg_send_http_error(conn, 403, "%s", "Forbidden");
15405 }
15406 return;
15407 }
15408#endif
15409
15410#if !defined(NO_CGI)
15413 for (cgi_config_idx = 0; cgi_config_idx < max; cgi_config_idx += inc) {
15414 if (conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx] != NULL) {
15415 if (match_prefix_strlen(
15416 conn->dom_ctx->config[CGI_EXTENSIONS + cgi_config_idx],
15417 path)
15418 > 0) {
15419 if (is_in_script_path(conn, path)) {
15420 /* CGI scripts may support all HTTP methods */
15421 handle_cgi_request(conn, path, cgi_config_idx);
15422 } else {
15423 /* Script was in an illegal path */
15424 mg_send_http_error(conn, 403, "%s", "Forbidden");
15425 }
15426 return;
15427 }
15428 }
15429 }
15430#endif /* !NO_CGI */
15431
15432 if (match_prefix_strlen(conn->dom_ctx->config[SSI_EXTENSIONS], path) > 0) {
15433 if (is_in_script_path(conn, path)) {
15434 handle_ssi_file_request(conn, path, file);
15435 } else {
15436 /* Script was in an illegal path */
15437 mg_send_http_error(conn, 403, "%s", "Forbidden");
15438 }
15439 return;
15440 }
15441
15442#if !defined(NO_CACHING)
15443 if ((!conn->in_error_handler) && is_not_modified(conn, &file->stat)) {
15444 /* Send 304 "Not Modified" - this must not send any body data */
15446 return;
15447 }
15448#endif /* !NO_CACHING */
15449
15450 handle_static_file_request(conn, path, file, NULL, NULL);
15451}
static void handle_static_file_request(struct mg_connection *conn, const char *path, struct mg_file *filep, const char *mime_type, const char *additional_headers)
Definition civetweb.c:10220
static void handle_ssi_file_request(struct mg_connection *conn, const char *path, struct mg_file *filep)
Definition civetweb.c:12588
static int is_not_modified(const struct mg_connection *conn, const struct mg_file_stat *filestat)
Definition civetweb.c:10468
static int is_in_script_path(const struct mg_connection *conn, const char *path)
Definition civetweb.c:14645
static void handle_not_modified_static_file_request(struct mg_connection *conn, struct mg_file *filep)
Definition civetweb.c:10483
static void handle_cgi_request(struct mg_connection *conn, const char *prog, int cgi_config_idx)
Definition civetweb.c:11587
int in_error_handler
Definition civetweb.c:2537

References CGI2_EXTENSIONS, CGI_EXTENSIONS, mg_domain_context::config, mg_connection::dom_ctx, handle_cgi_request(), handle_not_modified_static_file_request(), handle_ssi_file_request(), handle_static_file_request(), mg_connection::in_error_handler, is_in_script_path(), is_not_modified(), mg_send_http_error(), NULL, PUT_DELETE_PASSWORDS_FILE, SSI_EXTENSIONS, and mg_file::stat.

Referenced by handle_request(), and mg_send_http_error_impl().

◆ handle_not_modified_static_file_request()

static void handle_not_modified_static_file_request ( struct mg_connection * conn,
struct mg_file * filep )
static

Definition at line 10483 of file civetweb.c.

10485{
10486 char lm[64], etag[64];
10487
10488 if ((conn == NULL) || (filep == NULL)) {
10489 return;
10490 }
10491
10492 gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified);
10493 construct_etag(etag, sizeof(etag), &filep->stat);
10494
10495 /* Create 304 "not modified" response */
10496 mg_response_header_start(conn, 304);
10499 mg_response_header_add(conn, "Last-Modified", lm, -1);
10500 mg_response_header_add(conn, "Etag", etag, -1);
10501
10502 /* Send all headers */
10504}
static void construct_etag(char *buf, size_t buf_len, const struct mg_file_stat *filestat)
Definition civetweb.c:10181
struct mg_file_stat stat
Definition civetweb.c:1886

References construct_etag(), gmt_time_string(), mg_file_stat::last_modified, mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), NULL, send_additional_header(), send_static_cache_header(), and mg_file::stat.

Referenced by handle_file_based_request(), handle_request(), and mg_send_mime_file2().

◆ handle_propfind()

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

Definition at line 12759 of file civetweb.c.

12762{
12763 const char *depth = mg_get_header(conn, "Depth");
12764
12765 if (!conn || !path || !filep || !conn->dom_ctx) {
12766 return;
12767 }
12768
12769 /* return 207 "Multi-Status" */
12770 conn->must_close = 1;
12771 mg_response_header_start(conn, 207);
12775 "Content-Type",
12776 "application/xml; charset=utf-8",
12777 -1);
12779
12780 /* Content */
12781 mg_printf(conn,
12782 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
12783 "<d:multistatus xmlns:d='DAV:'>\n");
12784
12785 /* Print properties for the requested resource itself */
12786 print_props(conn, conn->request_info.local_uri, "", filep);
12787
12788 /* If it is a directory, print directory entries too if Depth is not 0
12789 */
12790 if (filep->is_directory
12792 "yes")
12793 && ((depth == NULL) || (strcmp(depth, "0") != 0))) {
12794 scan_directory(conn, path, conn, &print_dav_dir_entry);
12795 }
12796
12797 mg_printf(conn, "%s\n", "</d:multistatus>");
12798}
static int print_dav_dir_entry(struct de *de, void *data)
Definition civetweb.c:12745
static int print_props(struct mg_connection *conn, const char *uri, const char *name, struct mg_file_stat *filep)
Definition civetweb.c:12660
guint depth

References mg_domain_context::config, depth, mg_connection::dom_ctx, ENABLE_DIRECTORY_LISTING, mg_file_stat::is_directory, mg_request_info::local_uri, mg_get_header(), mg_printf(), mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_strcasecmp(), mg_connection::must_close, NULL, print_dav_dir_entry(), print_props(), mg_connection::request_info, scan_directory(), send_additional_header(), and send_static_cache_header().

Referenced by handle_request().

◆ handle_request()

static void handle_request ( struct mg_connection * conn)
static

Definition at line 14704 of file civetweb.c.

14705{
14706 struct mg_request_info *ri = &conn->request_info;
14707 char path[UTF8_PATH_MAX];
14708 int uri_len, ssl_index;
14709 int is_found = 0, is_script_resource = 0, is_websocket_request = 0,
14710 is_put_or_delete_request = 0, is_callback_resource = 0,
14711 is_template_text_file = 0, is_webdav_request = 0;
14712 int i;
14713 struct mg_file file = STRUCT_FILE_INITIALIZER;
14714 mg_request_handler callback_handler = NULL;
14715 struct mg_handler_info *handler_info = NULL;
14717 mg_websocket_connect_handler ws_connect_handler = NULL;
14718 mg_websocket_ready_handler ws_ready_handler = NULL;
14719 mg_websocket_data_handler ws_data_handler = NULL;
14720 mg_websocket_close_handler ws_close_handler = NULL;
14721 void *callback_data = NULL;
14722 mg_authorization_handler auth_handler = NULL;
14723 void *auth_callback_data = NULL;
14724 int handler_type;
14725 time_t curtime = time(NULL);
14726 char date[64];
14727 char *tmp;
14728
14729 path[0] = 0;
14730
14731 /* 0. Reset internal state (required for HTTP/2 proxy) */
14732 conn->request_state = 0;
14733
14734 /* 1. get the request url */
14735 /* 1.1. split into url and query string */
14736 if ((conn->request_info.query_string = strchr(ri->request_uri, '?'))
14737 != NULL) {
14738 *((char *)conn->request_info.query_string++) = '\0';
14739 }
14740
14741 /* 1.2. do a https redirect, if required. Do not decode URIs yet. */
14742 if (!conn->client.is_ssl && conn->client.ssl_redir) {
14743 ssl_index = get_first_ssl_listener_index(conn->phys_ctx);
14744 if (ssl_index >= 0) {
14745 int port = (int)ntohs(USA_IN_PORT_UNSAFE(
14746 &(conn->phys_ctx->listening_sockets[ssl_index].lsa)));
14747 redirect_to_https_port(conn, port);
14748 } else {
14749 /* A http to https forward port has been specified,
14750 * but no https port to forward to. */
14751 mg_send_http_error(conn,
14752 503,
14753 "%s",
14754 "Error: SSL forward not configured properly");
14755 mg_cry_internal(conn,
14756 "%s",
14757 "Can not redirect to SSL, no SSL port available");
14758 }
14759 return;
14760 }
14761 uri_len = (int)strlen(ri->local_uri);
14762
14763 /* 1.3. decode url (if config says so) */
14764 if (should_decode_url(conn)) {
14765 url_decode_in_place((char *)ri->local_uri);
14766 }
14767
14768 /* URL decode the query-string only if explicitly set in the configuration
14769 */
14770 if (conn->request_info.query_string) {
14771 if (should_decode_query_string(conn)) {
14773 }
14774 }
14775
14776 /* 1.4. clean URIs, so a path like allowed_dir/../forbidden_file is not
14777 * possible. The fact that we cleaned the URI is stored in that the
14778 * pointer to ri->local_ur and ri->local_uri_raw are now different.
14779 * ri->local_uri_raw still points to memory allocated in
14780 * worker_thread_run(). ri->local_uri is private to the request so we
14781 * don't have to use preallocated memory here. */
14782 tmp = mg_strdup(ri->local_uri_raw);
14783 if (!tmp) {
14784 /* Out of memory. We cannot do anything reasonable here. */
14785 return;
14786 }
14788 ri->local_uri = tmp;
14789
14790 /* step 1. completed, the url is known now */
14791 DEBUG_TRACE("REQUEST: %s %s", ri->request_method, ri->local_uri);
14792
14793 /* 2. if this ip has limited speed, set it for this connection */
14795 &conn->client.rsa,
14796 ri->local_uri);
14797
14798 /* 3. call a "handle everything" callback, if registered */
14799 if (conn->phys_ctx->callbacks.begin_request != NULL) {
14800 /* Note that since V1.7 the "begin_request" function is called
14801 * before an authorization check. If an authorization check is
14802 * required, use a request_handler instead. */
14803 i = conn->phys_ctx->callbacks.begin_request(conn);
14804 if (i > 0) {
14805 /* callback already processed the request. Store the
14806 return value as a status code for the access log. */
14807 conn->status_code = i;
14808 if (!conn->must_close) {
14810 }
14811 DEBUG_TRACE("%s", "begin_request handled request");
14812 return;
14813 } else if (i == 0) {
14814 /* civetweb should process the request */
14815 } else {
14816 /* unspecified - may change with the next version */
14817 DEBUG_TRACE("%s", "done (undocumented behavior)");
14818 return;
14819 }
14820 }
14821
14822 /* request not yet handled by a handler or redirect, so the request
14823 * is processed here */
14824
14825 /* 4. Check for CORS preflight requests and handle them (if configured).
14826 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
14827 */
14828 if (!strcmp(ri->request_method, "OPTIONS")) {
14829 /* Send a response to CORS preflights only if
14830 * access_control_allow_methods is not NULL and not an empty string.
14831 * In this case, scripts can still handle CORS. */
14832 const char *cors_meth_cfg =
14834 const char *cors_orig_cfg =
14836 const char *cors_origin =
14837 get_header(ri->http_headers, ri->num_headers, "Origin");
14838 const char *cors_acrm = get_header(ri->http_headers,
14839 ri->num_headers,
14840 "Access-Control-Request-Method");
14841
14842 /* Todo: check if cors_origin is in cors_orig_cfg.
14843 * Or, let the client check this. */
14844
14845 if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
14846 && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
14847 && (cors_origin != NULL) && (cors_acrm != NULL)) {
14848 /* This is a valid CORS preflight, and the server is configured
14849 * to handle it automatically. */
14850 const char *cors_acrh =
14852 ri->num_headers,
14853 "Access-Control-Request-Headers");
14854
14855 gmt_time_string(date, sizeof(date), &curtime);
14856 mg_printf(conn,
14857 "HTTP/1.1 200 OK\r\n"
14858 "Date: %s\r\n"
14859 "Access-Control-Allow-Origin: %s\r\n"
14860 "Access-Control-Allow-Methods: %s\r\n"
14861 "Content-Length: 0\r\n"
14862 "Connection: %s\r\n",
14863 date,
14864 cors_orig_cfg,
14865 ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
14867
14868 if (cors_acrh != NULL) {
14869 /* CORS request is asking for additional headers */
14870 const char *cors_hdr_cfg =
14872
14873 if ((cors_hdr_cfg != NULL) && (*cors_hdr_cfg != 0)) {
14874 /* Allow only if access_control_allow_headers is
14875 * not NULL and not an empty string. If this
14876 * configuration is set to *, allow everything.
14877 * Otherwise this configuration must be a list
14878 * of allowed HTTP header names. */
14879 mg_printf(conn,
14880 "Access-Control-Allow-Headers: %s\r\n",
14881 ((cors_hdr_cfg[0] == '*') ? cors_acrh
14882 : cors_hdr_cfg));
14883 }
14884 }
14885 mg_printf(conn, "Access-Control-Max-Age: 60\r\n");
14886 mg_printf(conn, "\r\n");
14887 DEBUG_TRACE("%s", "OPTIONS done");
14888 return;
14889 }
14890 }
14891
14892 /* 5. interpret the url to find out how the request must be handled
14893 */
14894 /* 5.1. first test, if the request targets the regular http(s)://
14895 * protocol namespace or the websocket ws(s):// protocol namespace.
14896 */
14897 is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET);
14898#if defined(USE_WEBSOCKET)
14899 handler_type = is_websocket_request ? WEBSOCKET_HANDLER : REQUEST_HANDLER;
14900#else
14901 handler_type = REQUEST_HANDLER;
14902#endif /* defined(USE_WEBSOCKET) */
14903
14904 if (is_websocket_request) {
14905 HTTP1_only;
14906 }
14907
14908 /* 5.2. check if the request will be handled by a callback */
14909 if (get_request_handler(conn,
14910 handler_type,
14911 &callback_handler,
14912 &subprotocols,
14913 &ws_connect_handler,
14914 &ws_ready_handler,
14915 &ws_data_handler,
14916 &ws_close_handler,
14917 NULL,
14918 &callback_data,
14919 &handler_info)) {
14920 /* 5.2.1. A callback will handle this request. All requests
14921 * handled by a callback have to be considered as requests
14922 * to a script resource. */
14923 is_callback_resource = 1;
14924 is_script_resource = 1;
14925 is_put_or_delete_request = is_put_or_delete_method(conn);
14926 /* Never handle a C callback according to File WebDav rules,
14927 * even if it is a webdav method */
14928 is_webdav_request = 0; /* is_civetweb_webdav_method(conn); */
14929 } else {
14930 no_callback_resource:
14931
14932 /* 5.2.2. No callback is responsible for this request. The URI
14933 * addresses a file based resource (static content or Lua/cgi
14934 * scripts in the file system). */
14935 is_callback_resource = 0;
14936 interpret_uri(conn,
14937 path,
14938 sizeof(path),
14939 &file.stat,
14940 &is_found,
14941 &is_script_resource,
14942 &is_websocket_request,
14943 &is_put_or_delete_request,
14944 &is_webdav_request,
14945 &is_template_text_file);
14946 }
14947
14948 /* 5.3. A webdav request (PROPFIND/PROPPATCH/LOCK/UNLOCK) */
14949 if (is_webdav_request) {
14950 /* TODO: Do we need a config option? */
14951 const char *webdav_enable = conn->dom_ctx->config[ENABLE_WEBDAV];
14952 if (webdav_enable[0] != 'y') {
14953 mg_send_http_error(conn,
14954 405,
14955 "%s method not allowed",
14957 DEBUG_TRACE("%s", "webdav rejected");
14958 return;
14959 }
14960 }
14961
14962 /* 6. authorization check */
14963 /* 6.1. a custom authorization handler is installed */
14964 if (get_request_handler(conn,
14966 NULL,
14967 NULL,
14968 NULL,
14969 NULL,
14970 NULL,
14971 NULL,
14972 &auth_handler,
14973 &auth_callback_data,
14974 NULL)) {
14975 if (!auth_handler(conn, auth_callback_data)) {
14976
14977 /* Callback handler will not be used anymore. Release it */
14978 release_handler_ref(conn, handler_info);
14979 DEBUG_TRACE("%s", "auth handler rejected request");
14980 return;
14981 }
14982 } else if (is_put_or_delete_request && !is_script_resource
14983 && !is_callback_resource) {
14984 HTTP1_only;
14985 /* 6.2. this request is a PUT/DELETE to a real file */
14986 /* 6.2.1. thus, the server must have real files */
14987#if defined(NO_FILES)
14988 if (1) {
14989#else
14990 if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL
14992#endif
14993 /* This code path will not be called for request handlers */
14994 DEBUG_ASSERT(handler_info == NULL);
14995
14996 /* This server does not have any real files, thus the
14997 * PUT/DELETE methods are not valid. */
14998 mg_send_http_error(conn,
14999 405,
15000 "%s method not allowed",
15002 DEBUG_TRACE("%s", "all file based put/delete requests rejected");
15003 return;
15004 }
15005
15006#if !defined(NO_FILES)
15007 /* 6.2.2. Check if put authorization for static files is
15008 * available.
15009 */
15010 if (!is_authorized_for_put(conn)) {
15012 DEBUG_TRACE("%s", "file write needs authorization");
15013 return;
15014 }
15015#endif
15016
15017 } else {
15018 /* 6.3. This is either a OPTIONS, GET, HEAD or POST request,
15019 * or it is a PUT or DELETE request to a resource that does not
15020 * correspond to a file. Check authorization. */
15021 if (!check_authorization(conn, path)) {
15023
15024 /* Callback handler will not be used anymore. Release it */
15025 release_handler_ref(conn, handler_info);
15026 DEBUG_TRACE("%s", "access authorization required");
15027 return;
15028 }
15029 }
15030
15031 /* request is authorized or does not need authorization */
15032
15033 /* 7. check if there are request handlers for this uri */
15034 if (is_callback_resource) {
15035 HTTP1_only;
15036 if (!is_websocket_request) {
15037 i = callback_handler(conn, callback_data);
15038
15039 /* Callback handler will not be used anymore. Release it */
15040 release_handler_ref(conn, handler_info);
15041
15042 if (i > 0) {
15043 /* Do nothing, callback has served the request. Store
15044 * then return value as status code for the log and discard
15045 * all data from the client not used by the callback. */
15046 conn->status_code = i;
15047 if (!conn->must_close) {
15049 }
15050 } else {
15051 /* The handler did NOT handle the request. */
15052 /* Some proper reactions would be:
15053 * a) close the connections without sending anything
15054 * b) send a 404 not found
15055 * c) try if there is a file matching the URI
15056 * It would be possible to do a, b or c in the callback
15057 * implementation, and return 1 - we cannot do anything
15058 * here, that is not possible in the callback.
15059 *
15060 * TODO: What would be the best reaction here?
15061 * (Note: The reaction may change, if there is a better
15062 * idea.)
15063 */
15064
15065 /* For the moment, use option c: We look for a proper file,
15066 * but since a file request is not always a script resource,
15067 * the authorization check might be different. */
15068 callback_handler = NULL;
15069
15070 /* Here we are at a dead end:
15071 * According to URI matching, a callback should be
15072 * responsible for handling the request,
15073 * we called it, but the callback declared itself
15074 * not responsible.
15075 * We use a goto here, to get out of this dead end,
15076 * and continue with the default handling.
15077 * A goto here is simpler and better to understand
15078 * than some curious loop. */
15079 goto no_callback_resource;
15080 }
15081 } else {
15082#if defined(USE_WEBSOCKET)
15083 handle_websocket_request(conn,
15084 path,
15085 is_callback_resource,
15087 ws_connect_handler,
15088 ws_ready_handler,
15089 ws_data_handler,
15090 ws_close_handler,
15091 callback_data);
15092#endif
15093 }
15094 DEBUG_TRACE("%s", "websocket handling done");
15095 return;
15096 }
15097
15098 /* 8. handle websocket requests */
15099#if defined(USE_WEBSOCKET)
15100 if (is_websocket_request) {
15101 HTTP1_only;
15102 if (is_script_resource) {
15103
15104 if (is_in_script_path(conn, path)) {
15105 /* Websocket Lua script */
15106 handle_websocket_request(conn,
15107 path,
15108 0 /* Lua Script */,
15109 NULL,
15110 NULL,
15111 NULL,
15112 NULL,
15113 NULL,
15114 conn->phys_ctx->user_data);
15115 } else {
15116 /* Script was in an illegal path */
15117 mg_send_http_error(conn, 403, "%s", "Forbidden");
15118 }
15119 } else {
15120 mg_send_http_error(conn, 404, "%s", "Not found");
15121 }
15122 DEBUG_TRACE("%s", "websocket script done");
15123 return;
15124 } else
15125#endif
15126
15127#if defined(NO_FILES)
15128 /* 9a. In case the server uses only callbacks, this uri is
15129 * unknown.
15130 * Then, all request handling ends here. */
15131 mg_send_http_error(conn, 404, "%s", "Not Found");
15132
15133#else
15134 /* 9b. This request is either for a static file or resource handled
15135 * by a script file. Thus, a DOCUMENT_ROOT must exist. */
15136 if (conn->dom_ctx->config[DOCUMENT_ROOT] == NULL) {
15137 mg_send_http_error(conn, 404, "%s", "Not Found");
15138 DEBUG_TRACE("%s", "no document root available");
15139 return;
15140 }
15141
15142 /* 10. Request is handled by a script */
15143 if (is_script_resource) {
15144 HTTP1_only;
15145 handle_file_based_request(conn, path, &file);
15146 DEBUG_TRACE("%s", "script handling done");
15147 return;
15148 }
15149
15150 /* Request was not handled by a callback or script. It will be
15151 * handled by a server internal method. */
15152
15153 /* 11. Handle put/delete/mkcol requests */
15154 if (is_put_or_delete_request) {
15155 HTTP1_only;
15156 /* 11.1. PUT method */
15157 if (!strcmp(ri->request_method, "PUT")) {
15158 put_file(conn, path);
15159 DEBUG_TRACE("handling %s request to %s done",
15160 ri->request_method,
15161 path);
15162 return;
15163 }
15164 /* 11.2. DELETE method */
15165 if (!strcmp(ri->request_method, "DELETE")) {
15166 delete_file(conn, path);
15167 DEBUG_TRACE("handling %s request to %s done",
15168 ri->request_method,
15169 path);
15170 return;
15171 }
15172 /* 11.3. MKCOL method */
15173 if (!strcmp(ri->request_method, "MKCOL")) {
15174 dav_mkcol(conn, path);
15175 DEBUG_TRACE("handling %s request to %s done",
15176 ri->request_method,
15177 path);
15178 return;
15179 }
15180 /* 11.4. MOVE method */
15181 if (!strcmp(ri->request_method, "MOVE")) {
15182 dav_move_file(conn, path, 0);
15183 DEBUG_TRACE("handling %s request to %s done",
15184 ri->request_method,
15185 path);
15186 return;
15187 }
15188 if (!strcmp(ri->request_method, "COPY")) {
15189 dav_move_file(conn, path, 1);
15190 DEBUG_TRACE("handling %s request to %s done",
15191 ri->request_method,
15192 path);
15193 return;
15194 }
15195 /* 11.5. LOCK method */
15196 if (!strcmp(ri->request_method, "LOCK")) {
15197 dav_lock_file(conn, path);
15198 DEBUG_TRACE("handling %s request to %s done",
15199 ri->request_method,
15200 path);
15201 return;
15202 }
15203 /* 11.6. UNLOCK method */
15204 if (!strcmp(ri->request_method, "UNLOCK")) {
15205 dav_unlock_file(conn, path);
15206 DEBUG_TRACE("handling %s request to %s done",
15207 ri->request_method,
15208 path);
15209 return;
15210 }
15211 /* 11.7. PROPPATCH method */
15212 if (!strcmp(ri->request_method, "PROPPATCH")) {
15213 dav_proppatch(conn, path);
15214 DEBUG_TRACE("handling %s request to %s done",
15215 ri->request_method,
15216 path);
15217 return;
15218 }
15219 /* 11.8. Other methods, e.g.: PATCH
15220 * This method is not supported for static resources,
15221 * only for scripts (Lua, CGI) and callbacks. */
15222 mg_send_http_error(conn,
15223 405,
15224 "%s method not allowed",
15226 DEBUG_TRACE("method %s on %s is not supported",
15227 ri->request_method,
15228 path);
15229 return;
15230 }
15231
15232 /* 11. File does not exist, or it was configured that it should be
15233 * hidden */
15234 if (!is_found || (must_hide_file(conn, path))) {
15235 mg_send_http_error(conn, 404, "%s", "Not found");
15236 DEBUG_TRACE("handling %s request to %s: file not found",
15237 ri->request_method,
15238 path);
15239 return;
15240 }
15241
15242 /* 12. Directory uris should end with a slash */
15243 if (file.stat.is_directory && ((uri_len = (int)strlen(ri->local_uri)) > 0)
15244 && (ri->local_uri[uri_len - 1] != '/')) {
15245
15246 /* Path + server root */
15247 size_t buflen = UTF8_PATH_MAX * 2 + 2;
15248 char *new_path;
15249
15250 if (ri->query_string) {
15251 buflen += strlen(ri->query_string);
15252 }
15253 new_path = (char *)mg_malloc_ctx(buflen, conn->phys_ctx);
15254 if (!new_path) {
15255 mg_send_http_error(conn, 500, "out or memory");
15256 } else {
15257 mg_get_request_link(conn, new_path, buflen - 1);
15258 strcat(new_path, "/");
15259 if (ri->query_string) {
15260 /* Append ? and query string */
15261 strcat(new_path, "?");
15262 strcat(new_path, ri->query_string);
15263 }
15264 mg_send_http_redirect(conn, new_path, 301);
15265 mg_free(new_path);
15266 }
15267 DEBUG_TRACE("%s request to %s: directory redirection sent",
15268 ri->request_method,
15269 path);
15270 return;
15271 }
15272
15273 /* 13. Handle other methods than GET/HEAD */
15274 /* 13.1. Handle PROPFIND */
15275 if (!strcmp(ri->request_method, "PROPFIND")) {
15276 handle_propfind(conn, path, &file.stat);
15277 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15278 return;
15279 }
15280 /* 13.2. Handle OPTIONS for files */
15281 if (!strcmp(ri->request_method, "OPTIONS")) {
15282 /* This standard handler is only used for real files.
15283 * Scripts should support the OPTIONS method themselves, to allow a
15284 * maximum flexibility.
15285 * Lua and CGI scripts may fully support CORS this way (including
15286 * preflights). */
15287 send_options(conn);
15288 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15289 return;
15290 }
15291 /* 13.3. everything but GET and HEAD (e.g. POST) */
15292 if ((0 != strcmp(ri->request_method, "GET"))
15293 && (0 != strcmp(ri->request_method, "HEAD"))) {
15294 mg_send_http_error(conn,
15295 405,
15296 "%s method not allowed",
15298 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15299 return;
15300 }
15301
15302 /* 14. directories */
15303 if (file.stat.is_directory) {
15304 /* Substitute files have already been handled above. */
15305 /* Here we can either generate and send a directory listing,
15306 * or send an "access denied" error. */
15308 "yes")) {
15309 handle_directory_request(conn, path);
15310 } else {
15311 mg_send_http_error(conn,
15312 403,
15313 "%s",
15314 "Error: Directory listing denied");
15315 }
15316 DEBUG_TRACE("handling %s request to %s done", ri->request_method, path);
15317 return;
15318 }
15319
15320 /* 15. Files with search/replace patterns: LSP and SSI */
15321 if (is_template_text_file) {
15322 HTTP1_only;
15323 handle_file_based_request(conn, path, &file);
15324 DEBUG_TRACE("handling %s request to %s done (template)",
15325 ri->request_method,
15326 path);
15327 return;
15328 }
15329
15330 /* 16. Static file - maybe cached */
15331#if !defined(NO_CACHING)
15332 if ((!conn->in_error_handler) && is_not_modified(conn, &file.stat)) {
15333 /* Send 304 "Not Modified" - this must not send any body data */
15335 DEBUG_TRACE("handling %s request to %s done (not modified)",
15336 ri->request_method,
15337 path);
15338 return;
15339 }
15340#endif /* !NO_CACHING */
15341
15342 /* 17. Static file - not cached */
15343 handle_static_file_request(conn, path, &file, NULL, NULL);
15344 DEBUG_TRACE("handling %s request to %s done (static)",
15345 ri->request_method,
15346 path);
15347
15348#endif /* !defined(NO_FILES) */
15349}
static int is_authorized_for_put(struct mg_connection *conn)
Definition civetweb.c:9035
static int check_authorization(struct mg_connection *conn, const char *path)
Definition civetweb.c:8918
static void redirect_to_https_port(struct mg_connection *conn, int port)
Definition civetweb.c:14235
static void put_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:12158
static void delete_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:12293
static void dav_move_file(struct mg_connection *conn, const char *path, int do_copy)
Definition civetweb.c:11994
static int must_hide_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:9763
static void dav_proppatch(struct mg_connection *conn, const char *path)
Definition civetweb.c:12964
static void send_authorization_request(struct mg_connection *conn, const char *realm)
Definition civetweb.c:8974
static void handle_file_based_request(struct mg_connection *conn, const char *path, struct mg_file *filep)
Definition civetweb.c:15354
static void dav_lock_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:12802
static void dav_unlock_file(struct mg_connection *conn, const char *path)
Definition civetweb.c:12931
static int get_first_ssl_listener_index(const struct mg_context *ctx)
Definition civetweb.c:14121
static void handle_directory_request(struct mg_connection *conn, const char *dir)
Definition civetweb.c:9942
static int set_throttle(const char *spec, const union usa *rsa, const char *uri)
Definition civetweb.c:14075
static const char * suggest_connection_header(const struct mg_connection *conn)
Definition civetweb.c:4046
static void send_options(struct mg_connection *conn)
Definition civetweb.c:12632
static void release_handler_ref(struct mg_connection *conn, struct mg_handler_info *handler_info)
Definition civetweb.c:14687
static void handle_propfind(struct mg_connection *conn, const char *path, struct mg_file_stat *filep)
Definition civetweb.c:12759
static void interpret_uri(struct mg_connection *conn, char *filename, size_t filename_buf_len, struct mg_file_stat *filestat, int *is_found, int *is_script_resource, int *is_websocket_request, int *is_put_or_delete_request, int *is_webdav_request, int *is_template_text)
Definition civetweb.c:7622
static int is_put_or_delete_method(const struct mg_connection *conn)
Definition civetweb.c:7464
static void dav_mkcol(struct mg_connection *conn, const char *path)
Definition civetweb.c:11915
static int get_request_handler(struct mg_connection *conn, int handler_type, mg_request_handler *handler, struct mg_websocket_subprotocols **subprotocols, mg_websocket_connect_handler *connect_handler, mg_websocket_ready_handler *ready_handler, mg_websocket_data_handler *data_handler, mg_websocket_close_handler *close_handler, mg_authorization_handler *auth_handler, void **cbdata, struct mg_handler_info **handler_info)
Definition civetweb.c:14560
static int should_decode_query_string(const struct mg_connection *conn)
Definition civetweb.c:4034
static int should_decode_url(const struct mg_connection *conn)
Definition civetweb.c:4023
CIVETWEB_API int mg_send_http_redirect(struct mg_connection *conn, const char *target_url, int redirect_code)
Definition civetweb.c:4618
#define HTTP1_only
Definition civetweb.c:6609
static void discard_unread_request_data(struct mg_connection *conn)
Definition civetweb.c:6484
static void url_decode_in_place(char *buf)
Definition civetweb.c:7080
int(* mg_authorization_handler)(struct mg_connection *conn, void *cbdata)
Definition civetweb.h:606
void(* mg_websocket_ready_handler)(struct mg_connection *, void *)
Definition civetweb.h:549
int(* mg_websocket_data_handler)(struct mg_connection *, int, char *, size_t, void *)
Definition civetweb.h:550
int(* mg_request_handler)(struct mg_connection *conn, void *cbdata)
Definition civetweb.h:492
void(* mg_websocket_close_handler)(const struct mg_connection *, void *)
Definition civetweb.h:555
int(* mg_websocket_connect_handler)(const struct mg_connection *, void *)
Definition civetweb.h:547
int(* begin_request)(struct mg_connection *)
Definition civetweb.h:233
void * user_data
Definition civetweb.c:2433
const char * local_uri_raw
Definition civetweb.h:154
const char * request_method
Definition civetweb.h:151
const char * request_uri
Definition civetweb.h:152
const char ** subprotocols
Definition civetweb.h:564
union usa rsa
Definition civetweb.c:1905

References ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, AUTH_HANDLER, mg_callbacks::begin_request, mg_context::callbacks, check_authorization(), mg_connection::client, mg_domain_context::config, dav_lock_file(), dav_mkcol(), dav_move_file(), dav_proppatch(), dav_unlock_file(), DEBUG_ASSERT, DEBUG_TRACE, delete_file(), discard_unread_request_data(), DOCUMENT_ROOT, mg_connection::dom_ctx, ENABLE_DIRECTORY_LISTING, ENABLE_WEBDAV, get_first_ssl_listener_index(), get_header(), get_request_handler(), gmt_time_string(), handle_directory_request(), handle_file_based_request(), handle_not_modified_static_file_request(), handle_propfind(), handle_static_file_request(), HTTP1_only, mg_request_info::http_headers, mg_connection::in_error_handler, interpret_uri(), is_authorized_for_put(), mg_file_stat::is_directory, is_in_script_path(), is_not_modified(), is_put_or_delete_method(), socket::is_ssl, mg_context::listening_sockets, mg_request_info::local_uri, mg_request_info::local_uri_raw, socket::lsa, mg_cry_internal, mg_free(), mg_get_request_link(), mg_malloc_ctx, mg_printf(), mg_send_http_error(), mg_send_http_redirect(), mg_strcasecmp(), mg_strdup(), mg_connection::must_close, must_hide_file(), NULL, mg_request_info::num_headers, mg_connection::phys_ctx, mg_connection::protocol_type, PROTOCOL_TYPE_WEBSOCKET, PUT_DELETE_PASSWORDS_FILE, put_file(), mg_request_info::query_string, redirect_to_https_port(), release_handler_ref(), remove_dot_segments(), REQUEST_HANDLER, mg_connection::request_info, mg_request_info::request_method, mg_connection::request_state, mg_request_info::request_uri, socket::rsa, send_authorization_request(), send_options(), set_throttle(), should_decode_query_string(), should_decode_url(), socket::ssl_redir, mg_file::stat, mg_connection::status_code, STRUCT_FILE_INITIALIZER, mg_websocket_subprotocols::subprotocols, suggest_connection_header(), THROTTLE, mg_connection::throttle, url_decode_in_place(), USA_IN_PORT_UNSAFE, mg_context::user_data, UTF8_PATH_MAX, and WEBSOCKET_HANDLER.

Referenced by handle_request_stat_log().

◆ handle_request_stat_log()

static void handle_request_stat_log ( struct mg_connection * conn)
static

Definition at line 6562 of file civetweb.c.

6563{
6564#if defined(USE_SERVER_STATS)
6565 struct timespec tnow;
6566 conn->conn_state = 4; /* processing */
6567#endif
6568
6569 handle_request(conn);
6570
6571
6572#if defined(USE_SERVER_STATS)
6573 conn->conn_state = 5; /* processed */
6574
6575 clock_gettime(CLOCK_MONOTONIC, &tnow);
6576 conn->processing_time = mg_difftimespec(&tnow, &(conn->req_time));
6577
6578 mg_atomic_add64(&(conn->phys_ctx->total_data_read), conn->consumed_content);
6579 mg_atomic_add64(&(conn->phys_ctx->total_data_written),
6580 conn->num_bytes_sent);
6581#endif
6582
6583 DEBUG_TRACE("%s", "handle_request done");
6584
6585 if (conn->phys_ctx->callbacks.end_request != NULL) {
6586 conn->phys_ctx->callbacks.end_request(conn, conn->status_code);
6587 DEBUG_TRACE("%s", "end_request callback done");
6588 }
6589 log_access(conn);
6590}
static void log_access(const struct mg_connection *)
Definition civetweb.c:16063
static double mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
Definition civetweb.c:3364
static void handle_request(struct mg_connection *)
Definition civetweb.c:14704
void(* end_request)(const struct mg_connection *, int reply_status_code)
Definition civetweb.h:236
int64_t num_bytes_sent
Definition civetweb.c:2517

References mg_context::callbacks, mg_connection::consumed_content, DEBUG_TRACE, mg_callbacks::end_request, handle_request(), log_access(), mg_difftimespec(), NULL, mg_connection::num_bytes_sent, mg_connection::phys_ctx, mg_connection::req_time, and mg_connection::status_code.

Referenced by process_new_connection().

◆ handle_ssi_file_request()

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

Definition at line 12588 of file civetweb.c.

12591{
12592 char date[64];
12593 time_t curtime = time(NULL);
12594
12595 if ((conn == NULL) || (path == NULL) || (filep == NULL)) {
12596 return;
12597 }
12598
12599 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
12600 /* File exists (precondition for calling this function),
12601 * but can not be opened by the server. */
12602 mg_send_http_error(conn,
12603 500,
12604 "Error: Cannot read file\nfopen(%s): %s",
12605 path,
12606 strerror(ERRNO));
12607 } else {
12608 /* Set "must_close" for HTTP/1.x, since we do not know the
12609 * content length */
12610 conn->must_close = 1;
12611 gmt_time_string(date, sizeof(date), &curtime);
12612 fclose_on_exec(&filep->access, conn);
12613
12614 /* 200 OK response */
12615 mg_response_header_start(conn, 200);
12618 send_cors_header(conn);
12619 mg_response_header_add(conn, "Content-Type", "text/html", -1);
12621
12622 /* Header sent, now send body */
12623 send_ssi_file(conn, path, filep, 0);
12624 (void)mg_fclose(&filep->access); /* Ignore errors for readonly files */
12625 }
12626}
static void send_cors_header(struct mg_connection *conn)
Definition civetweb.c:4149

References mg_file::access, ERRNO, fclose_on_exec(), gmt_time_string(), mg_fclose(), mg_fopen(), MG_FOPEN_MODE_READ, mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_send_http_error(), mg_connection::must_close, NULL, send_additional_header(), send_cors_header(), send_no_cache_header(), and send_ssi_file().

Referenced by handle_file_based_request().

◆ handle_static_file_request()

static void handle_static_file_request ( struct mg_connection * conn,
const char * path,
struct mg_file * filep,
const char * mime_type,
const char * additional_headers )
static

Definition at line 10220 of file civetweb.c.

10225{
10226 char lm[64], etag[64];
10227 char range[128]; /* large enough, so there will be no overflow */
10228 const char *range_hdr;
10229 int64_t cl, r1, r2;
10230 struct vec mime_vec;
10231 int n, truncated;
10232 char gz_path[UTF8_PATH_MAX];
10233 const char *encoding = 0;
10234 int is_head_request;
10235
10236#if defined(USE_ZLIB)
10237 /* Compression is allowed, unless there is a reason not to use
10238 * compression. If the file is already compressed, too small or a
10239 * "range" request was made, on the fly compression is not possible. */
10240 int allow_on_the_fly_compression = 1;
10241#endif
10242
10243 if ((conn == NULL) || (conn->dom_ctx == NULL) || (filep == NULL)) {
10244 return;
10245 }
10246
10247 is_head_request = !strcmp(conn->request_info.request_method, "HEAD");
10248
10249 if (mime_type == NULL) {
10250 get_mime_type(conn, path, &mime_vec);
10251 } else {
10252 mime_vec.ptr = mime_type;
10253 mime_vec.len = strlen(mime_type);
10254 }
10255 if (filep->stat.size > INT64_MAX) {
10256 mg_send_http_error(conn,
10257 500,
10258 "Error: File size is too large to send\n%" INT64_FMT,
10259 filep->stat.size);
10260 return;
10261 }
10262 cl = (int64_t)filep->stat.size;
10263 conn->status_code = 200;
10264 range[0] = '\0';
10265
10266#if defined(USE_ZLIB)
10267 /* if this file is in fact a pre-gzipped file, rewrite its filename
10268 * it's important to rewrite the filename after resolving
10269 * the mime type from it, to preserve the actual file's type */
10270 if (!conn->accept_gzip) {
10271 allow_on_the_fly_compression = 0;
10272 }
10273#endif
10274
10275 /* Check if there is a range header */
10276 range_hdr = mg_get_header(conn, "Range");
10277
10278 /* For gzipped files, add *.gz */
10279 if (filep->stat.is_gzipped) {
10280 mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path);
10281
10282 if (truncated) {
10283 mg_send_http_error(conn,
10284 500,
10285 "Error: Path of zipped file too long (%s)",
10286 path);
10287 return;
10288 }
10289
10290 path = gz_path;
10291 encoding = "gzip";
10292
10293#if defined(USE_ZLIB)
10294 /* File is already compressed. No "on the fly" compression. */
10295 allow_on_the_fly_compression = 0;
10296#endif
10297 } else if ((conn->accept_gzip) && (range_hdr == NULL)
10298 && (filep->stat.size >= MG_FILE_COMPRESSION_SIZE_LIMIT)) {
10299 struct mg_file_stat file_stat;
10300
10301 mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path);
10302
10303 if (!truncated && mg_stat(conn, gz_path, &file_stat)
10304 && !file_stat.is_directory) {
10305 file_stat.is_gzipped = 1;
10306 filep->stat = file_stat;
10307 cl = (int64_t)filep->stat.size;
10308 path = gz_path;
10309 encoding = "gzip";
10310
10311#if defined(USE_ZLIB)
10312 /* File is already compressed. No "on the fly" compression. */
10313 allow_on_the_fly_compression = 0;
10314#endif
10315 }
10316 }
10317
10318 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
10319 mg_send_http_error(conn,
10320 500,
10321 "Error: Cannot open file\nfopen(%s): %s",
10322 path,
10323 strerror(ERRNO));
10324 return;
10325 }
10326
10327 fclose_on_exec(&filep->access, conn);
10328
10329 /* If "Range" request was made: parse header, send only selected part
10330 * of the file. */
10331 r1 = r2 = 0;
10332 if ((range_hdr != NULL)
10333 && ((n = parse_range_header(range_hdr, &r1, &r2)) > 0) && (r1 >= 0)
10334 && (r2 >= 0)) {
10335 /* actually, range requests don't play well with a pre-gzipped
10336 * file (since the range is specified in the uncompressed space) */
10337 if (filep->stat.is_gzipped) {
10339 conn,
10340 416, /* 416 = Range Not Satisfiable */
10341 "%s",
10342 "Error: Range requests in gzipped files are not supported");
10343 (void)mg_fclose(
10344 &filep->access); /* ignore error on read only file */
10345 return;
10346 }
10347 conn->status_code = 206;
10348 cl = (n == 2) ? (((r2 > cl) ? cl : r2) - r1 + 1) : (cl - r1);
10349 mg_snprintf(conn,
10350 NULL, /* range buffer is big enough */
10351 range,
10352 sizeof(range),
10353 "bytes "
10354 "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT,
10355 r1,
10356 r1 + cl - 1,
10357 filep->stat.size);
10358
10359#if defined(USE_ZLIB)
10360 /* Do not compress ranges. */
10361 allow_on_the_fly_compression = 0;
10362#endif
10363 }
10364
10365 /* Do not compress small files. Small files do not benefit from file
10366 * compression, but there is still some overhead. */
10367#if defined(USE_ZLIB)
10369 /* File is below the size limit. */
10370 allow_on_the_fly_compression = 0;
10371 }
10372#endif
10373
10374 /* Prepare Etag, and Last-Modified headers. */
10375 gmt_time_string(lm, sizeof(lm), &filep->stat.last_modified);
10376 construct_etag(etag, sizeof(etag), &filep->stat);
10377
10378 /* Create 2xx (200, 206) response */
10382 send_cors_header(conn);
10384 "Content-Type",
10385 mime_vec.ptr,
10386 (int)mime_vec.len);
10387 mg_response_header_add(conn, "Last-Modified", lm, -1);
10388 mg_response_header_add(conn, "Etag", etag, -1);
10389
10390#if defined(USE_ZLIB)
10391 /* On the fly compression allowed */
10392 if (allow_on_the_fly_compression) {
10393 /* For on the fly compression, we don't know the content size in
10394 * advance, so we have to use chunked encoding */
10395 encoding = "gzip";
10396 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
10397 /* HTTP/2 is always using "chunks" (frames) */
10398 mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
10399 }
10400
10401 } else
10402#endif
10403 {
10404 /* Without on-the-fly compression, we know the content-length
10405 * and we can use ranges (with on-the-fly compression we cannot).
10406 * So we send these response headers only in this case. */
10407 char len[32];
10408 int trunc = 0;
10409 mg_snprintf(conn, &trunc, len, sizeof(len), "%" INT64_FMT, cl);
10410
10411 if (!trunc) {
10412 mg_response_header_add(conn, "Content-Length", len, -1);
10413 }
10414
10415 mg_response_header_add(conn, "Accept-Ranges", "bytes", -1);
10416 }
10417
10418 if (encoding) {
10419 mg_response_header_add(conn, "Content-Encoding", encoding, -1);
10420 }
10421 if (range[0] != 0) {
10422 mg_response_header_add(conn, "Content-Range", range, -1);
10423 }
10424
10425 /* The code above does not add any header starting with X- to make
10426 * sure no one of the additional_headers is included twice */
10427 if ((additional_headers != NULL) && (*additional_headers != 0)) {
10428 mg_response_header_add_lines(conn, additional_headers);
10429 }
10430
10431 /* Send all headers */
10433
10434 if (!is_head_request) {
10435#if defined(USE_ZLIB)
10436 if (allow_on_the_fly_compression) {
10437 /* Compress and send */
10438 send_compressed_data(conn, filep);
10439 } else
10440#endif
10441 {
10442 /* Send file directly */
10443 send_file_data(conn, filep, r1, cl, 0); /* send static file */
10444 }
10445 }
10446 (void)mg_fclose(&filep->access); /* ignore error on read only file */
10447}
const char * mime_type
Definition civetweb.c:8224
#define MG_FILE_COMPRESSION_SIZE_LIMIT
Definition civetweb.c:478
static int parse_range_header(const char *header, int64_t *a, int64_t *b)
Definition civetweb.c:10169
static void get_mime_type(struct mg_connection *conn, const char *path, struct vec *vec)
Definition civetweb.c:8354
CIVETWEB_API int mg_response_header_add_lines(struct mg_connection *conn, const char *http1_headers)

References mg_connection::accept_gzip, mg_file::access, construct_etag(), mg_connection::dom_ctx, ERRNO, fclose_on_exec(), get_mime_type(), gmt_time_string(), INT64_FMT, INT64_MAX, mg_file_stat::is_directory, mg_file_stat::is_gzipped, mg_file_stat::last_modified, vec::len, mg_fclose(), MG_FILE_COMPRESSION_SIZE_LIMIT, mg_fopen(), MG_FOPEN_MODE_READ, mg_get_header(), mg_response_header_add(), mg_response_header_add_lines(), mg_response_header_send(), mg_response_header_start(), mg_send_http_error(), mg_snprintf(), mg_stat(), mime_type, NULL, parse_range_header(), mg_connection::protocol_type, PROTOCOL_TYPE_HTTP1, vec::ptr, mg_connection::request_info, mg_request_info::request_method, send_additional_header(), send_cors_header(), send_file_data(), send_static_cache_header(), mg_file_stat::size, mg_file::stat, mg_connection::status_code, and UTF8_PATH_MAX.

Referenced by handle_file_based_request(), handle_request(), and mg_send_mime_file2().

◆ header_has_option()

static int header_has_option ( const char * header,
const char * option )
static

Definition at line 3956 of file civetweb.c.

3957{
3958 struct vec opt_vec;
3959 struct vec eq_vec;
3960
3961 DEBUG_ASSERT(option != NULL);
3962 DEBUG_ASSERT(option[0] != '\0');
3963
3964 while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) {
3965 if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0)
3966 return 1;
3967 }
3968
3969 return 0;
3970}

References DEBUG_ASSERT, vec::len, mg_strncasecmp(), next_option(), NULL, and vec::ptr.

Referenced by should_keep_alive().

◆ header_val()

static const char * header_val ( const struct mg_connection * conn,
const char * header )
static

Definition at line 16046 of file civetweb.c.

16047{
16048 const char *header_value;
16049
16050 if ((header_value = mg_get_header(conn, header)) == NULL) {
16051 return "-";
16052 } else {
16053 return header_value;
16054 }
16055}

References mg_get_header(), and NULL.

Referenced by log_access().

◆ hexdump2string()

static int hexdump2string ( void * mem,
int memlen,
char * buf,
int buflen )
static

Definition at line 16594 of file civetweb.c.

16595{
16596 int i;
16597 const char hexdigit[] = "0123456789abcdef";
16598
16599 if ((memlen <= 0) || (buflen <= 0)) {
16600 return 0;
16601 }
16602 if (buflen < (3 * memlen)) {
16603 return 0;
16604 }
16605
16606 for (i = 0; i < memlen; i++) {
16607 if (i > 0) {
16608 buf[3 * i - 1] = ' ';
16609 }
16610 buf[3 * i] = hexdigit[(((uint8_t *)mem)[i] >> 4) & 0xF];
16611 buf[3 * i + 1] = hexdigit[((uint8_t *)mem)[i] & 0xF];
16612 }
16613 buf[3 * memlen - 1] = 0;
16614
16615 return 1;
16616}

Referenced by ssl_get_client_cert_info().

◆ init_connection()

static void init_connection ( struct mg_connection * conn)
static

Definition at line 19329 of file civetweb.c.

19330{
19331 /* Is keep alive allowed by the server */
19332 int keep_alive_enabled =
19334
19335 if (!keep_alive_enabled) {
19336 conn->must_close = 1;
19337 }
19338
19339 /* Important: on new connection, reset the receiving buffer. Credit
19340 * goes to crule42. */
19341 conn->data_len = 0;
19342 conn->handled_requests = 0;
19346
19347#if defined(USE_SERVER_STATS)
19348 conn->conn_state = 2; /* init */
19349#endif
19350
19351 /* call the init_connection callback if assigned */
19352 if (conn->phys_ctx->callbacks.init_connection != NULL) {
19353 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
19354 void *conn_data = NULL;
19355 conn->phys_ctx->callbacks.init_connection(conn, &conn_data);
19356 mg_set_user_connection_data(conn, conn_data);
19357 }
19358 }
19359}
int(* init_connection)(const struct mg_connection *conn, void **conn_data)
Definition civetweb.h:417
int handled_requests
Definition civetweb.c:2554
const char * acceptedWebSocketSubprotocol
Definition civetweb.h:184

References mg_request_info::acceptedWebSocketSubprotocol, mg_context::callbacks, mg_domain_context::config, mg_connection::connection_type, CONNECTION_TYPE_INVALID, CONTEXT_SERVER, mg_context::context_type, mg_connection::data_len, mg_connection::dom_ctx, ENABLE_KEEP_ALIVE, mg_connection::handled_requests, mg_callbacks::init_connection, mg_set_user_connection_data(), mg_strcasecmp(), mg_connection::must_close, NULL, mg_connection::phys_ctx, and mg_connection::request_info.

Referenced by worker_thread_run().

◆ init_ssl_ctx()

static int init_ssl_ctx ( struct mg_context * phys_ctx,
struct mg_domain_context * dom_ctx )
static

Definition at line 17485 of file civetweb.c.

17486{
17487 void *ssl_ctx = 0;
17488 int callback_ret;
17489 const char *pem;
17490 const char *chain;
17491 char ebuf[128];
17492
17493 if (!phys_ctx) {
17494 return 0;
17495 }
17496
17497 if (!dom_ctx) {
17498 dom_ctx = &(phys_ctx->dd);
17499 }
17500
17501 if (!is_ssl_port_used(dom_ctx->config[LISTENING_PORTS])) {
17502 /* No SSL port is set. No need to setup SSL. */
17503 return 1;
17504 }
17505
17506 /* Check for external SSL_CTX */
17507 callback_ret =
17508 (phys_ctx->callbacks.external_ssl_ctx == NULL)
17509 ? 0
17510 : (phys_ctx->callbacks.external_ssl_ctx(&ssl_ctx,
17511 phys_ctx->user_data));
17512
17513 if (callback_ret < 0) {
17514 /* Callback exists and returns <0: Initializing failed. */
17515 mg_cry_ctx_internal(phys_ctx,
17516 "external_ssl_ctx callback returned error: %i",
17517 callback_ret);
17518 return 0;
17519 } else if (callback_ret > 0) {
17520 /* Callback exists and returns >0: Initializing complete,
17521 * civetweb should not modify the SSL context. */
17522 dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
17523 if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17524 mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17525 return 0;
17526 }
17527 return 1;
17528 }
17529 /* If the callback does not exist or return 0, civetweb must initialize
17530 * the SSL context. Handle "domain" callback next. */
17531
17532 /* Check for external domain SSL_CTX callback. */
17533 callback_ret = (phys_ctx->callbacks.external_ssl_ctx_domain == NULL)
17534 ? 0
17535 : (phys_ctx->callbacks.external_ssl_ctx_domain(
17536 dom_ctx->config[AUTHENTICATION_DOMAIN],
17537 &ssl_ctx,
17538 phys_ctx->user_data));
17539
17540 if (callback_ret < 0) {
17541 /* Callback < 0: Error. Abort init. */
17543 phys_ctx,
17544 "external_ssl_ctx_domain callback returned error: %i",
17545 callback_ret);
17546 return 0;
17547 } else if (callback_ret > 0) {
17548 /* Callback > 0: Consider init done. */
17549 dom_ctx->ssl_ctx = (SSL_CTX *)ssl_ctx;
17550 if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17551 mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17552 return 0;
17553 }
17554 return 1;
17555 }
17556 /* else: external_ssl_ctx/external_ssl_ctx_domain do not exist or return
17557 * 0, CivetWeb should continue initializing SSL */
17558
17559 /* If PEM file is not specified and the init_ssl callbacks
17560 * are not specified, setup will fail. */
17561 if (((pem = dom_ctx->config[SSL_CERTIFICATE]) == NULL)
17562 && (phys_ctx->callbacks.init_ssl == NULL)
17563 && (phys_ctx->callbacks.init_ssl_domain == NULL)) {
17564 /* No certificate and no init_ssl callbacks:
17565 * Essential data to set up TLS is missing.
17566 */
17567 mg_cry_ctx_internal(phys_ctx,
17568 "Initializing SSL failed: -%s is not set",
17570 return 0;
17571 }
17572
17573 /* If a certificate chain is configured, use it. */
17574 chain = dom_ctx->config[SSL_CERTIFICATE_CHAIN];
17575 if (chain == NULL) {
17576 /* Default: certificate chain in PEM file */
17577 chain = pem;
17578 }
17579 if ((chain != NULL) && (*chain == 0)) {
17580 /* If the chain is an empty string, don't use it. */
17581 chain = NULL;
17582 }
17583
17584 if (!initialize_openssl(ebuf, sizeof(ebuf))) {
17585 mg_cry_ctx_internal(phys_ctx, "%s", ebuf);
17586 return 0;
17587 }
17588
17589 return init_ssl_ctx_impl(phys_ctx, dom_ctx, pem, chain);
17590}
static int init_ssl_ctx_impl(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx, const char *pem, const char *chain)
Definition civetweb.c:17239
static int is_ssl_port_used(const char *ports)
Definition civetweb.c:15679
static int initialize_openssl(char *ebuf, size_t ebuf_len)
Definition civetweb.c:16821
int(* init_ssl)(void *ssl_ctx, void *user_data)
Definition civetweb.h:254
int(* init_ssl_domain)(const char *server_domain, void *ssl_ctx, void *user_data)
Definition civetweb.h:265
int(* external_ssl_ctx_domain)(const char *server_domain, void **ssl_ctx, void *user_data)
Definition civetweb.h:290

References AUTHENTICATION_DOMAIN, mg_context::callbacks, mg_domain_context::config, config_options, mg_context::dd, mg_callbacks::external_ssl_ctx, mg_callbacks::external_ssl_ctx_domain, mg_callbacks::init_ssl, init_ssl_ctx_impl(), mg_callbacks::init_ssl_domain, initialize_openssl(), is_ssl_port_used(), LISTENING_PORTS, mg_cry_ctx_internal, name, NULL, SSL_CERTIFICATE, SSL_CERTIFICATE_CHAIN, mg_domain_context::ssl_ctx, and mg_context::user_data.

Referenced by mg_start2(), and mg_start_domain2().

◆ init_ssl_ctx_impl()

static int init_ssl_ctx_impl ( struct mg_context * phys_ctx,
struct mg_domain_context * dom_ctx,
const char * pem,
const char * chain )
static

Definition at line 17239 of file civetweb.c.

17243{
17244 int callback_ret;
17245 int should_verify_peer;
17246 int peer_certificate_optional;
17247 const char *ca_path;
17248 const char *ca_file;
17249 int use_default_verify_paths;
17250 int verify_depth;
17251 struct timespec now_mt;
17252 md5_byte_t ssl_context_id[16];
17253 md5_state_t md5state;
17254 int protocol_ver;
17255 int ssl_cache_timeout;
17256
17257#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \
17258 && !defined(NO_SSL_DL)
17259 if ((dom_ctx->ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL) {
17260 mg_cry_ctx_internal(phys_ctx,
17261 "SSL_CTX_new (server) error: %s",
17262 ssl_error());
17263 return 0;
17264 }
17265#else
17266 if ((dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
17267 mg_cry_ctx_internal(phys_ctx,
17268 "SSL_CTX_new (server) error: %s",
17269 ssl_error());
17270 return 0;
17271 }
17272#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
17273
17274#if defined(SSL_OP_NO_TLSv1_3)
17275 SSL_CTX_clear_options(dom_ctx->ssl_ctx,
17276 SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1
17277 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2
17278 | SSL_OP_NO_TLSv1_3);
17279#else
17280 SSL_CTX_clear_options(dom_ctx->ssl_ctx,
17281 SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1
17282 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);
17283#endif
17284
17285 protocol_ver = atoi(dom_ctx->config[SSL_PROTOCOL_VERSION]);
17286 SSL_CTX_set_options(dom_ctx->ssl_ctx, ssl_get_protocol(protocol_ver));
17287 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_SINGLE_DH_USE);
17288 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
17289 SSL_CTX_set_options(dom_ctx->ssl_ctx,
17290 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
17291 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_NO_COMPRESSION);
17292
17293#if defined(SSL_OP_NO_RENEGOTIATION)
17294 SSL_CTX_set_options(dom_ctx->ssl_ctx, SSL_OP_NO_RENEGOTIATION);
17295#endif
17296
17297#if !defined(NO_SSL_DL)
17298 SSL_CTX_set_ecdh_auto(dom_ctx->ssl_ctx, 1);
17299#endif /* NO_SSL_DL */
17300
17301 /* In SSL documentation examples callback defined without const
17302 * specifier 'void (*)(SSL *, int, int)' See:
17303 * https://www.openssl.org/docs/man1.0.2/ssl/ssl.html
17304 * https://www.openssl.org/docs/man1.1.0/ssl/ssl.html
17305 * But in the source code const SSL is used:
17306 * 'void (*)(const SSL *, int, int)' See:
17307 * https://github.com/openssl/openssl/blob/1d97c8435171a7af575f73c526d79e1ef0ee5960/ssl/ssl.h#L1173
17308 * Problem about wrong documentation described, but not resolved:
17309 * https://bugs.launchpad.net/ubuntu/+source/openssl/+bug/1147526
17310 * Wrong const cast ignored on C or can be suppressed by compiler flags.
17311 * But when compiled with modern C++ compiler, correct const should be
17312 * provided
17313 */
17314 SSL_CTX_set_info_callback(dom_ctx->ssl_ctx, ssl_info_callback);
17315
17316 SSL_CTX_set_tlsext_servername_callback(dom_ctx->ssl_ctx,
17318
17319 /* If a callback has been specified, call it. */
17320 callback_ret = (phys_ctx->callbacks.init_ssl == NULL)
17321 ? 0
17322 : (phys_ctx->callbacks.init_ssl(dom_ctx->ssl_ctx,
17323 phys_ctx->user_data));
17324
17325 /* If callback returns 0, civetweb sets up the SSL certificate.
17326 * If it returns 1, civetweb assumes the callback already did this.
17327 * If it returns -1, initializing ssl fails. */
17328 if (callback_ret < 0) {
17329 mg_cry_ctx_internal(phys_ctx,
17330 "SSL callback returned error: %i",
17331 callback_ret);
17332 return 0;
17333 }
17334 if (callback_ret > 0) {
17335 /* Callback did everything. */
17336 return 1;
17337 }
17338
17339 /* If a domain callback has been specified, call it. */
17340 callback_ret = (phys_ctx->callbacks.init_ssl_domain == NULL)
17341 ? 0
17342 : (phys_ctx->callbacks.init_ssl_domain(
17343 dom_ctx->config[AUTHENTICATION_DOMAIN],
17344 dom_ctx->ssl_ctx,
17345 phys_ctx->user_data));
17346
17347 /* If domain callback returns 0, civetweb sets up the SSL certificate.
17348 * If it returns 1, civetweb assumes the callback already did this.
17349 * If it returns -1, initializing ssl fails. */
17350 if (callback_ret < 0) {
17351 mg_cry_ctx_internal(phys_ctx,
17352 "Domain SSL callback returned error: %i",
17353 callback_ret);
17354 return 0;
17355 }
17356 if (callback_ret > 0) {
17357 /* Domain callback did everything. */
17358 return 1;
17359 }
17360
17361 /* Use some combination of start time, domain and port as a SSL
17362 * context ID. This should be unique on the current machine. */
17363 md5_init(&md5state);
17364 clock_gettime(CLOCK_MONOTONIC, &now_mt);
17365 md5_append(&md5state, (const md5_byte_t *)&now_mt, sizeof(now_mt));
17366 md5_append(&md5state,
17367 (const md5_byte_t *)phys_ctx->dd.config[LISTENING_PORTS],
17368 strlen(phys_ctx->dd.config[LISTENING_PORTS]));
17369 md5_append(&md5state,
17370 (const md5_byte_t *)dom_ctx->config[AUTHENTICATION_DOMAIN],
17371 strlen(dom_ctx->config[AUTHENTICATION_DOMAIN]));
17372 md5_append(&md5state, (const md5_byte_t *)phys_ctx, sizeof(*phys_ctx));
17373 md5_append(&md5state, (const md5_byte_t *)dom_ctx, sizeof(*dom_ctx));
17374 md5_finish(&md5state, ssl_context_id);
17375
17376 SSL_CTX_set_session_id_context(dom_ctx->ssl_ctx,
17377 (unsigned char *)ssl_context_id,
17378 sizeof(ssl_context_id));
17379
17380 if (pem != NULL) {
17381 if (!ssl_use_pem_file(phys_ctx, dom_ctx, pem, chain)) {
17382 return 0;
17383 }
17384 }
17385
17386 /* Should we support client certificates? */
17387 /* Default is "no". */
17388 should_verify_peer = 0;
17389 peer_certificate_optional = 0;
17390 if (dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
17391 if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER], "yes") == 0) {
17392 /* Yes, they are mandatory */
17393 should_verify_peer = 1;
17394 } else if (mg_strcasecmp(dom_ctx->config[SSL_DO_VERIFY_PEER],
17395 "optional")
17396 == 0) {
17397 /* Yes, they are optional */
17398 should_verify_peer = 1;
17399 peer_certificate_optional = 1;
17400 }
17401 }
17402
17403 use_default_verify_paths =
17404 (dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS] != NULL)
17405 && (mg_strcasecmp(dom_ctx->config[SSL_DEFAULT_VERIFY_PATHS], "yes")
17406 == 0);
17407
17408 if (should_verify_peer) {
17409 ca_path = dom_ctx->config[SSL_CA_PATH];
17410 ca_file = dom_ctx->config[SSL_CA_FILE];
17411 if (SSL_CTX_load_verify_locations(dom_ctx->ssl_ctx, ca_file, ca_path)
17412 != 1) {
17413 mg_cry_ctx_internal(phys_ctx,
17414 "SSL_CTX_load_verify_locations error: %s "
17415 "ssl_verify_peer requires setting "
17416 "either ssl_ca_path or ssl_ca_file. "
17417 "Is any of them present in the "
17418 ".conf file?",
17419 ssl_error());
17420 return 0;
17421 }
17422
17423 if (peer_certificate_optional) {
17424 SSL_CTX_set_verify(dom_ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
17425 } else {
17426 SSL_CTX_set_verify(dom_ctx->ssl_ctx,
17427 SSL_VERIFY_PEER
17428 | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
17429 NULL);
17430 }
17431
17432 if (use_default_verify_paths
17433 && (SSL_CTX_set_default_verify_paths(dom_ctx->ssl_ctx) != 1)) {
17434 mg_cry_ctx_internal(phys_ctx,
17435 "SSL_CTX_set_default_verify_paths error: %s",
17436 ssl_error());
17437 return 0;
17438 }
17439
17440 if (dom_ctx->config[SSL_VERIFY_DEPTH]) {
17441 verify_depth = atoi(dom_ctx->config[SSL_VERIFY_DEPTH]);
17442 SSL_CTX_set_verify_depth(dom_ctx->ssl_ctx, verify_depth);
17443 }
17444 }
17445
17446 if (dom_ctx->config[SSL_CIPHER_LIST] != NULL) {
17447 if (SSL_CTX_set_cipher_list(dom_ctx->ssl_ctx,
17448 dom_ctx->config[SSL_CIPHER_LIST])
17449 != 1) {
17450 mg_cry_ctx_internal(phys_ctx,
17451 "SSL_CTX_set_cipher_list error: %s",
17452 ssl_error());
17453 }
17454 }
17455
17456 /* SSL session caching */
17457 ssl_cache_timeout = ((dom_ctx->config[SSL_CACHE_TIMEOUT] != NULL)
17458 ? atoi(dom_ctx->config[SSL_CACHE_TIMEOUT])
17459 : 0);
17460 if (ssl_cache_timeout > 0) {
17461 SSL_CTX_set_session_cache_mode(dom_ctx->ssl_ctx, SSL_SESS_CACHE_BOTH);
17462 /* SSL_CTX_sess_set_cache_size(dom_ctx->ssl_ctx, 10000); ... use
17463 * default */
17464 SSL_CTX_set_timeout(dom_ctx->ssl_ctx, (long)ssl_cache_timeout);
17465 }
17466
17467#if defined(USE_ALPN)
17468 /* Initialize ALPN only of TLS library (OpenSSL version) supports ALPN */
17469#if !defined(NO_SSL_DL)
17470 if (!tls_feature_missing[TLS_ALPN])
17471#endif
17472 {
17473 init_alpn(phys_ctx, dom_ctx);
17474 }
17475#endif
17476
17477 return 1;
17478}
static int ssl_use_pem_file(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx, const char *pem, const char *chain)
Definition civetweb.c:16940
static const char * ssl_error(void)
Definition civetweb.c:16585
static long ssl_get_protocol(int version_id)
Definition civetweb.c:17017
static int ssl_servername_callback(SSL *ssl, int *ad, void *arg)
Definition civetweb.c:17066
static void ssl_info_callback(const SSL *ssl, int what, int ret)
Definition civetweb.c:17050

References AUTHENTICATION_DOMAIN, mg_context::callbacks, mg_domain_context::config, mg_context::dd, mg_callbacks::init_ssl, mg_callbacks::init_ssl_domain, LISTENING_PORTS, mg_cry_ctx_internal, mg_strcasecmp(), NULL, SSL_CA_FILE, SSL_CA_PATH, SSL_CACHE_TIMEOUT, SSL_CIPHER_LIST, mg_domain_context::ssl_ctx, SSL_DEFAULT_VERIFY_PATHS, SSL_DO_VERIFY_PEER, ssl_error(), ssl_get_protocol(), ssl_info_callback(), SSL_PROTOCOL_VERSION, ssl_servername_callback(), ssl_use_pem_file(), SSL_VERIFY_DEPTH, and mg_context::user_data.

Referenced by init_ssl_ctx().

◆ initialize_openssl()

static int initialize_openssl ( char * ebuf,
size_t ebuf_len )
static

Definition at line 16821 of file civetweb.c.

16822{
16823#if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0)
16824 int i, num_locks;
16825 size_t size;
16826#endif
16827
16828 if (ebuf_len > 0) {
16829 ebuf[0] = 0;
16830 }
16831
16832#if !defined(NO_SSL_DL)
16833 if (!cryptolib_dll_handle) {
16834 memset(tls_feature_missing, 0, sizeof(tls_feature_missing));
16836 ebuf, ebuf_len, CRYPTO_LIB, crypto_sw, tls_feature_missing);
16837 if (!cryptolib_dll_handle) {
16839 NULL, /* No truncation check for ebuf */
16840 ebuf,
16841 ebuf_len,
16842 "%s: error loading library %s",
16843 __func__,
16844 CRYPTO_LIB);
16845 DEBUG_TRACE("%s", ebuf);
16846 return 0;
16847 }
16848 }
16849#endif /* NO_SSL_DL */
16850
16851 if (mg_atomic_inc(&cryptolib_users) > 1) {
16852 return 1;
16853 }
16854
16855#if !defined(OPENSSL_API_1_1) && !defined(OPENSSL_API_3_0)
16856 /* Initialize locking callbacks, needed for thread safety.
16857 * http://www.openssl.org/support/faq.html#PROG1
16858 */
16859 num_locks = CRYPTO_num_locks();
16860 if (num_locks < 0) {
16861 num_locks = 0;
16862 }
16863 size = sizeof(pthread_mutex_t) * ((size_t)(num_locks));
16864
16865 /* allocate mutex array, if required */
16866 if (num_locks == 0) {
16867 /* No mutex array required */
16868 ssl_mutexes = NULL;
16869 } else {
16870 /* Mutex array required - allocate it */
16871 ssl_mutexes = (pthread_mutex_t *)mg_malloc(size);
16872
16873 /* Check OOM */
16874 if (ssl_mutexes == NULL) {
16876 NULL, /* No truncation check for ebuf */
16877 ebuf,
16878 ebuf_len,
16879 "%s: cannot allocate mutexes: %s",
16880 __func__,
16881 ssl_error());
16882 DEBUG_TRACE("%s", ebuf);
16883 return 0;
16884 }
16885
16886 /* initialize mutex array */
16887 for (i = 0; i < num_locks; i++) {
16888 if (0 != pthread_mutex_init(&ssl_mutexes[i], &pthread_mutex_attr)) {
16890 NULL, /* No truncation check for ebuf */
16891 ebuf,
16892 ebuf_len,
16893 "%s: error initializing mutex %i of %i",
16894 __func__,
16895 i,
16896 num_locks);
16897 DEBUG_TRACE("%s", ebuf);
16899 return 0;
16900 }
16901 }
16902 }
16903
16904 CRYPTO_set_locking_callback(&ssl_locking_callback);
16905 CRYPTO_set_id_callback(&mg_current_thread_id);
16906#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
16907
16908#if !defined(NO_SSL_DL)
16909 if (!ssllib_dll_handle) {
16911 load_tls_dll(ebuf, ebuf_len, SSL_LIB, ssl_sw, tls_feature_missing);
16912 if (!ssllib_dll_handle) {
16913#if !defined(OPENSSL_API_1_1)
16915#endif
16916 DEBUG_TRACE("%s", ebuf);
16917 return 0;
16918 }
16919 }
16920#endif /* NO_SSL_DL */
16921
16922#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \
16923 && !defined(NO_SSL_DL)
16924 /* Initialize SSL library */
16925 OPENSSL_init_ssl(0, NULL);
16926 OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS
16927 | OPENSSL_INIT_LOAD_CRYPTO_STRINGS,
16928 NULL);
16929#else
16930 /* Initialize SSL library */
16931 SSL_library_init();
16932 SSL_load_error_strings();
16933#endif
16934
16935 return 1;
16936}
static void ssl_locking_callback(int mode, int mutex_num, const char *file, int line)
Definition civetweb.c:16697
static FUNCTION_MAY_BE_UNUSED unsigned long mg_current_thread_id(void)
Definition civetweb.c:1626
static pthread_mutex_t * ssl_mutexes
Definition civetweb.c:16451
static volatile ptrdiff_t cryptolib_users
Definition civetweb.c:16815
#define CRYPTO_LIB
Definition civetweb.c:911
static pthread_mutexattr_t pthread_mutex_attr
Definition civetweb.c:1074
static void * load_tls_dll(char *ebuf, size_t ebuf_len, const char *dll_name, struct ssl_func *sw, int *feature_missing)
Definition civetweb.c:16715
#define SSL_LIB
Definition civetweb.c:908
static FUNCTION_MAY_BE_UNUSED ptrdiff_t mg_atomic_inc(volatile ptrdiff_t *addr)
Definition civetweb.c:1121
static void * cryptolib_dll_handle
Definition civetweb.c:16806
static void * ssllib_dll_handle
Definition civetweb.c:16805

References CRYPTO_LIB, cryptolib_dll_handle, cryptolib_users, DEBUG_TRACE, load_tls_dll(), mg_atomic_inc(), mg_current_thread_id(), mg_free(), mg_malloc(), mg_snprintf(), NULL, pthread_mutex_attr, ssl_error(), SSL_LIB, ssl_locking_callback(), ssl_mutexes, and ssllib_dll_handle.

Referenced by init_ssl_ctx(), and mg_init_library().

◆ interpret_uri()

static void interpret_uri ( struct mg_connection * conn,
char * filename,
size_t filename_buf_len,
struct mg_file_stat * filestat,
int * is_found,
int * is_script_resource,
int * is_websocket_request,
int * is_put_or_delete_request,
int * is_webdav_request,
int * is_template_text )
static

Definition at line 7622 of file civetweb.c.

7633{
7634 char const *accept_encoding;
7635
7636#if !defined(NO_FILES)
7637 const char *uri = conn->request_info.local_uri;
7638 const char *root = conn->dom_ctx->config[DOCUMENT_ROOT];
7639 const char *rewrite;
7640 struct vec a, b;
7641 ptrdiff_t match_len;
7642 char gz_path[UTF8_PATH_MAX];
7643 int truncated;
7644#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
7645 char *tmp_str;
7646 size_t tmp_str_len, sep_pos;
7647 int allow_substitute_script_subresources;
7648#endif
7649#else
7650 (void)filename_buf_len; /* unused if NO_FILES is defined */
7651#endif
7652
7653 /* Step 1: Set all initially unknown outputs to zero */
7654 memset(filestat, 0, sizeof(*filestat));
7655 *filename = 0;
7656 *is_found = 0;
7657 *is_script_resource = 0;
7658 *is_template_text = 0;
7659
7660 /* Step 2: Classify the request method */
7661 /* Step 2a: Check if the request attempts to modify the file system */
7662 *is_put_or_delete_request = is_put_or_delete_method(conn);
7663 /* Step 2b: Check if the request uses WebDav method that requires special
7664 * handling */
7665 *is_webdav_request = is_civetweb_webdav_method(conn);
7666
7667 /* Step 3: Check if it is a websocket request, and modify the document
7668 * root if required */
7669#if defined(USE_WEBSOCKET)
7670 *is_websocket_request = (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET);
7671#if !defined(NO_FILES)
7672 if ((*is_websocket_request) && conn->dom_ctx->config[WEBSOCKET_ROOT]) {
7673 root = conn->dom_ctx->config[WEBSOCKET_ROOT];
7674 }
7675#endif /* !NO_FILES */
7676#else /* USE_WEBSOCKET */
7677 *is_websocket_request = 0;
7678#endif /* USE_WEBSOCKET */
7679
7680 /* Step 4: Check if gzip encoded response is allowed */
7681 conn->accept_gzip = 0;
7682 if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) {
7683 if (strstr(accept_encoding, "gzip") != NULL) {
7684 conn->accept_gzip = 1;
7685 }
7686 }
7687
7688#if !defined(NO_FILES)
7689 /* Step 5: If there is no root directory, don't look for files. */
7690 /* Note that root == NULL is a regular use case here. This occurs,
7691 * if all requests are handled by callbacks, so the WEBSOCKET_ROOT
7692 * config is not required. */
7693 if (root == NULL) {
7694 /* all file related outputs have already been set to 0, just return
7695 */
7696 return;
7697 }
7698
7699 /* Step 6: Determine the local file path from the root path and the
7700 * request uri. */
7701 /* Using filename_buf_len - 1 because memmove() for PATH_INFO may shift
7702 * part of the path one byte on the right. */
7703 truncated = 0;
7705 conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri);
7706
7707 if (truncated) {
7708 goto interpret_cleanup;
7709 }
7710
7711 /* Step 7: URI rewriting */
7712 rewrite = conn->dom_ctx->config[URL_REWRITE_PATTERN];
7713 while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
7714 if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
7715 mg_snprintf(conn,
7716 &truncated,
7717 filename,
7718 filename_buf_len - 1,
7719 "%.*s%s",
7720 (int)b.len,
7721 b.ptr,
7722 uri + match_len);
7723 break;
7724 }
7725 }
7726
7727 if (truncated) {
7728 goto interpret_cleanup;
7729 }
7730
7731 /* Step 8: Check if the file exists at the server */
7732 /* Local file path and name, corresponding to requested URI
7733 * is now stored in "filename" variable. */
7734 if (mg_stat(conn, filename, filestat)) {
7735 int uri_len = (int)strlen(uri);
7736 int is_uri_end_slash = (uri_len > 0) && (uri[uri_len - 1] == '/');
7737
7738 /* 8.1: File exists. */
7739 *is_found = 1;
7740
7741 /* 8.2: Check if it is a script type. */
7742 if (extention_matches_script(conn, filename)) {
7743 /* The request addresses a CGI resource, Lua script or
7744 * server-side javascript.
7745 * The URI corresponds to the script itself (like
7746 * /path/script.cgi), and there is no additional resource
7747 * path (like /path/script.cgi/something).
7748 * Requests that modify (replace or delete) a resource, like
7749 * PUT and DELETE requests, should replace/delete the script
7750 * file.
7751 * Requests that read or write from/to a resource, like GET and
7752 * POST requests, should call the script and return the
7753 * generated response. */
7754 *is_script_resource = (!*is_put_or_delete_request);
7755 }
7756
7757 /* 8.3: Check for SSI and LSP files */
7758 if (extention_matches_template_text(conn, filename)) {
7759 /* Same as above, but for *.lsp and *.shtml files. */
7760 /* A "template text" is a file delivered directly to the client,
7761 * but with some text tags replaced by dynamic content.
7762 * E.g. a Server Side Include (SSI) or Lua Page/Lua Server Page
7763 * (LP, LSP) file. */
7764 *is_template_text = (!*is_put_or_delete_request);
7765 }
7766
7767 /* 8.4: If the request target is a directory, there could be
7768 * a substitute file (index.html, index.cgi, ...). */
7769 /* But do not substitute a directory for a WebDav request */
7770 if (filestat->is_directory && is_uri_end_slash
7771 && (!*is_webdav_request)) {
7772 /* Use a local copy here, since substitute_index_file will
7773 * change the content of the file status */
7774 struct mg_file_stat tmp_filestat;
7775 memset(&tmp_filestat, 0, sizeof(tmp_filestat));
7776
7778 conn, filename, filename_buf_len, &tmp_filestat)) {
7779
7780 /* Substitute file found. Copy stat to the output, then
7781 * check if the file is a script file */
7782 *filestat = tmp_filestat;
7783
7784 if (extention_matches_script(conn, filename)) {
7785 /* Substitute file is a script file */
7786 *is_script_resource = 1;
7787 } else if (extention_matches_template_text(conn, filename)) {
7788 /* Substitute file is a LSP or SSI file */
7789 *is_template_text = 1;
7790 } else {
7791 /* Substitute file is a regular file */
7792 *is_script_resource = 0;
7793 *is_found = (mg_stat(conn, filename, filestat) ? 1 : 0);
7794 }
7795 }
7796 /* If there is no substitute file, the server could return
7797 * a directory listing in a later step */
7798 }
7799 return;
7800 }
7801
7802 /* Step 9: Check for zipped files: */
7803 /* If we can't find the actual file, look for the file
7804 * with the same name but a .gz extension. If we find it,
7805 * use that and set the gzipped flag in the file struct
7806 * to indicate that the response need to have the content-
7807 * encoding: gzip header.
7808 * We can only do this if the browser declares support. */
7809 if (conn->accept_gzip) {
7811 conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename);
7812
7813 if (truncated) {
7814 goto interpret_cleanup;
7815 }
7816
7817 if (mg_stat(conn, gz_path, filestat)) {
7818 if (filestat) {
7819 filestat->is_gzipped = 1;
7820 *is_found = 1;
7821 }
7822 /* Currently gz files can not be scripts. */
7823 return;
7824 }
7825 }
7826
7827#if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE)
7828 /* Step 10: Script resources may handle sub-resources */
7829 /* Support PATH_INFO for CGI scripts. */
7830 tmp_str_len = strlen(filename);
7831 tmp_str =
7832 (char *)mg_malloc_ctx(tmp_str_len + UTF8_PATH_MAX + 1, conn->phys_ctx);
7833 if (!tmp_str) {
7834 /* Out of memory */
7835 goto interpret_cleanup;
7836 }
7837 memcpy(tmp_str, filename, tmp_str_len + 1);
7838
7839 /* Check config, if index scripts may have sub-resources */
7840 allow_substitute_script_subresources =
7842 "yes");
7843 if (*is_webdav_request) {
7844 /* TO BE DEFINED: Should scripts handle special WebDAV methods lile
7845 * PROPFIND for their subresources? */
7846 /* allow_substitute_script_subresources = 0; */
7847 }
7848
7849 sep_pos = tmp_str_len;
7850 while (sep_pos > 0) {
7851 sep_pos--;
7852 if (tmp_str[sep_pos] == '/') {
7853 int is_script = 0, does_exist = 0;
7854
7855 tmp_str[sep_pos] = 0;
7856 if (tmp_str[0]) {
7857 is_script = extention_matches_script(conn, tmp_str);
7858 does_exist = mg_stat(conn, tmp_str, filestat);
7859 }
7860
7861 if (does_exist && is_script) {
7862 filename[sep_pos] = 0;
7863 memmove(filename + sep_pos + 2,
7864 filename + sep_pos + 1,
7865 strlen(filename + sep_pos + 1) + 1);
7866 conn->path_info = filename + sep_pos + 1;
7867 filename[sep_pos + 1] = '/';
7868 *is_script_resource = 1;
7869 *is_found = 1;
7870 break;
7871 }
7872
7873 if (allow_substitute_script_subresources) {
7875 conn, tmp_str, tmp_str_len + UTF8_PATH_MAX, filestat)) {
7876
7877 /* some intermediate directory has an index file */
7878 if (extention_matches_script(conn, tmp_str)) {
7879
7880 size_t script_name_len = strlen(tmp_str);
7881
7882 /* subres_name read before this memory locatio will be
7883 overwritten */
7884 char *subres_name = filename + sep_pos;
7885 size_t subres_name_len = strlen(subres_name);
7886
7887 DEBUG_TRACE("Substitute script %s serving path %s",
7888 tmp_str,
7889 filename);
7890
7891 /* this index file is a script */
7892 if ((script_name_len + subres_name_len + 2)
7893 >= filename_buf_len) {
7894 mg_free(tmp_str);
7895 goto interpret_cleanup;
7896 }
7897
7898 conn->path_info =
7899 filename + script_name_len + 1; /* new target */
7900 memmove(conn->path_info, subres_name, subres_name_len);
7901 conn->path_info[subres_name_len] = 0;
7902 memcpy(filename, tmp_str, script_name_len + 1);
7903
7904 *is_script_resource = 1;
7905 *is_found = 1;
7906 break;
7907
7908 } else {
7909
7910 DEBUG_TRACE("Substitute file %s serving path %s",
7911 tmp_str,
7912 filename);
7913
7914 /* non-script files will not have sub-resources */
7915 filename[sep_pos] = 0;
7916 conn->path_info = 0;
7917 *is_script_resource = 0;
7918 *is_found = 0;
7919 break;
7920 }
7921 }
7922 }
7923
7924 tmp_str[sep_pos] = '/';
7925 }
7926 }
7927
7928 mg_free(tmp_str);
7929
7930#endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */
7931#endif /* !defined(NO_FILES) */
7932 return;
7933
7934#if !defined(NO_FILES)
7935/* Reset all outputs */
7936interpret_cleanup:
7937 memset(filestat, 0, sizeof(*filestat));
7938 *filename = 0;
7939 *is_found = 0;
7940 *is_script_resource = 0;
7941 *is_websocket_request = 0;
7942 *is_put_or_delete_request = 0;
7943#endif /* !defined(NO_FILES) */
7944}
static int is_civetweb_webdav_method(const struct mg_connection *conn)
Definition civetweb.c:7482
static int extention_matches_script(struct mg_connection *conn, const char *filename)
Definition civetweb.c:7504
static int extention_matches_template_text(struct mg_connection *conn, const char *filename)
Definition civetweb.c:7550
static int substitute_index_file(struct mg_connection *conn, char *path, size_t path_len, struct mg_file_stat *filestat)
Definition civetweb.c:7574
char * path_info
Definition civetweb.c:2533

References mg_connection::accept_gzip, ALLOW_INDEX_SCRIPT_SUB_RES, mg_domain_context::config, DEBUG_TRACE, DOCUMENT_ROOT, mg_connection::dom_ctx, extention_matches_script(), extention_matches_template_text(), is_civetweb_webdav_method(), mg_file_stat::is_directory, mg_file_stat::is_gzipped, is_put_or_delete_method(), vec::len, mg_request_info::local_uri, mg_free(), mg_get_header(), mg_malloc_ctx, mg_snprintf(), mg_stat(), mg_strcasecmp(), next_option(), NULL, mg_connection::path_info, mg_connection::phys_ctx, mg_connection::protocol_type, PROTOCOL_TYPE_WEBSOCKET, vec::ptr, mg_connection::request_info, substitute_index_file(), URL_REWRITE_PATTERN, and UTF8_PATH_MAX.

Referenced by handle_request().

◆ is_authorized_for_put()

static int is_authorized_for_put ( struct mg_connection * conn)
static

Definition at line 9035 of file civetweb.c.

9036{
9037 int ret = 0;
9038
9039 if (conn) {
9040 struct mg_file file = STRUCT_FILE_INITIALIZER;
9041 const char *passfile = conn->dom_ctx->config[PUT_DELETE_PASSWORDS_FILE];
9042
9043 if (passfile != NULL
9044 && mg_fopen(conn, passfile, MG_FOPEN_MODE_READ, &file)) {
9045 ret = authorize(conn, &file, NULL);
9046 (void)mg_fclose(&file.access); /* ignore error on read only file */
9047 }
9048 }
9049
9050 DEBUG_TRACE("file write authorization: %i", ret);
9051 return ret;
9052}

References mg_file::access, authorize(), mg_domain_context::config, DEBUG_TRACE, mg_connection::dom_ctx, mg_fclose(), mg_fopen(), MG_FOPEN_MODE_READ, NULL, PUT_DELETE_PASSWORDS_FILE, and STRUCT_FILE_INITIALIZER.

Referenced by handle_request().

◆ is_civetweb_webdav_method()

static int is_civetweb_webdav_method ( const struct mg_connection * conn)
static

Definition at line 7482 of file civetweb.c.

7483{
7484 /* Note: Here we only have to identify the WebDav methods that need special
7485 * handling in the CivetWeb code - not all methods used in WebDav. In
7486 * particular, methods used on directories (when using Windows Explorer as
7487 * WebDav client).
7488 */
7489 if (conn) {
7490 const char *s = conn->request_info.request_method;
7491 if (s != NULL) {
7492 /* These are the civetweb builtin DAV methods */
7493 return (!strcmp(s, "PROPFIND") || !strcmp(s, "PROPPATCH")
7494 || !strcmp(s, "LOCK") || !strcmp(s, "UNLOCK")
7495 || !strcmp(s, "MOVE") || !strcmp(s, "COPY"));
7496 }
7497 }
7498 return 0;
7499}

References NULL, mg_connection::request_info, mg_request_info::request_method, and s.

Referenced by interpret_uri().

◆ is_file_opened()

static int is_file_opened ( const struct mg_file_access * fileacc)
static

Definition at line 2853 of file civetweb.c.

2854{
2855 if (!fileacc) {
2856 return 0;
2857 }
2858
2859 return (fileacc->fp != NULL);
2860}

References mg_file_access::fp, and NULL.

Referenced by check_authorization().

◆ is_in_script_path()

static int is_in_script_path ( const struct mg_connection * conn,
const char * path )
static

Definition at line 14645 of file civetweb.c.

14646{
14647 /* TODO (Feature): Add config value for allowed script path.
14648 * Default: All allowed. */
14649 (void)conn;
14650 (void)path;
14651 return 1;
14652}

Referenced by handle_file_based_request(), and handle_request().

◆ is_not_modified()

static int is_not_modified ( const struct mg_connection * conn,
const struct mg_file_stat * filestat )
static

Definition at line 10468 of file civetweb.c.

10470{
10471 char etag[64];
10472 const char *ims = mg_get_header(conn, "If-Modified-Since");
10473 const char *inm = mg_get_header(conn, "If-None-Match");
10474 construct_etag(etag, sizeof(etag), filestat);
10475
10476 return ((inm != NULL) && !mg_strcasecmp(etag, inm))
10477 || ((ims != NULL)
10478 && (filestat->last_modified <= parse_date_string(ims)));
10479}
static time_t parse_date_string(const char *datetime)
Definition civetweb.c:8005

References construct_etag(), mg_file_stat::last_modified, mg_get_header(), mg_strcasecmp(), NULL, and parse_date_string().

Referenced by handle_file_based_request(), handle_request(), and mg_send_mime_file2().

◆ is_put_or_delete_method()

static int is_put_or_delete_method ( const struct mg_connection * conn)
static

Definition at line 7464 of file civetweb.c.

7465{
7466 if (conn) {
7467 const char *s = conn->request_info.request_method;
7468 if (s != NULL) {
7469 /* PUT, DELETE, MKCOL, PATCH, LOCK, UNLOCK, PROPPATCH, MOVE, COPY */
7470 return (!strcmp(s, "PUT") || !strcmp(s, "DELETE")
7471 || !strcmp(s, "MKCOL") || !strcmp(s, "PATCH")
7472 || !strcmp(s, "LOCK") || !strcmp(s, "UNLOCK")
7473 || !strcmp(s, "PROPPATCH") || !strcmp(s, "MOVE")
7474 || !strcmp(s, "COPY"));
7475 }
7476 }
7477 return 0;
7478}

References NULL, mg_connection::request_info, mg_request_info::request_method, and s.

Referenced by handle_request(), and interpret_uri().

◆ is_ssl_port_used()

static int is_ssl_port_used ( const char * ports)
static

Definition at line 15679 of file civetweb.c.

15680{
15681 if (ports) {
15682 /* There are several different allowed syntax variants:
15683 * - "80" for a single port using every network interface
15684 * - "localhost:80" for a single port using only localhost
15685 * - "80,localhost:8080" for two ports, one bound to localhost
15686 * - "80,127.0.0.1:8084,[::1]:8086" for three ports, one bound
15687 * to IPv4 localhost, one to IPv6 localhost
15688 * - "+80" use port 80 for IPv4 and IPv6
15689 * - "+80r,+443s" port 80 (HTTP) is a redirect to port 443 (HTTPS),
15690 * for both: IPv4 and IPv4
15691 * - "+443s,localhost:8080" port 443 (HTTPS) for every interface,
15692 * additionally port 8080 bound to localhost connections
15693 *
15694 * If we just look for 's' anywhere in the string, "localhost:80"
15695 * will be detected as SSL (false positive).
15696 * Looking for 's' after a digit may cause false positives in
15697 * "my24service:8080".
15698 * Looking from 's' backward if there are only ':' and numbers
15699 * before will not work for "24service:8080" (non SSL, port 8080)
15700 * or "24s" (SSL, port 24).
15701 *
15702 * Remark: Initially hostnames were not allowed to start with a
15703 * digit (according to RFC 952), this was allowed later (RFC 1123,
15704 * Section 2.1).
15705 *
15706 * To get this correct, the entire string must be parsed as a whole,
15707 * reading it as a list element for element and parsing with an
15708 * algorithm equivalent to parse_port_string.
15709 *
15710 * In fact, we use local interface names here, not arbitrary
15711 * hostnames, so in most cases the only name will be "localhost".
15712 *
15713 * So, for now, we use this simple algorithm, that may still return
15714 * a false positive in bizarre cases.
15715 */
15716 int i;
15717 int portslen = (int)strlen(ports);
15718 char prevIsNumber = 0;
15719
15720 for (i = 0; i < portslen; i++) {
15721 if (prevIsNumber && (ports[i] == 's' || ports[i] == 'r')) {
15722 return 1;
15723 }
15724 if (ports[i] >= '0' && ports[i] <= '9') {
15725 prevIsNumber = 1;
15726 } else {
15727 prevIsNumber = 0;
15728 }
15729 }
15730 }
15731 return 0;
15732}

Referenced by init_ssl_ctx(), and legacy_init().

◆ is_valid_http_method()

static int is_valid_http_method ( const char * method)
static

Definition at line 10899 of file civetweb.c.

10900{
10901 return (get_http_method_info(method) != NULL);
10902}
static const struct mg_http_method_info * get_http_method_info(const char *method)
Definition civetweb.c:10880

References get_http_method_info(), and NULL.

Referenced by parse_http_request().

◆ is_valid_port()

static int is_valid_port ( unsigned long port)
static

Definition at line 9239 of file civetweb.c.

9240{
9241 return (port <= 0xffff);
9242}

Referenced by connect_socket(), get_rel_url_at_current_server(), get_uri_type(), and parse_port_string().

◆ legacy_init()

static void legacy_init ( const char ** options)
static

Definition at line 20436 of file civetweb.c.

20437{
20438 const char *ports_option = config_options[LISTENING_PORTS].default_value;
20439
20440 if (options) {
20441 const char **run_options = options;
20442 const char *optname = config_options[LISTENING_PORTS].name;
20443
20444 /* Try to find the "listening_ports" option */
20445 while (*run_options) {
20446 if (!strcmp(*run_options, optname)) {
20447 ports_option = run_options[1];
20448 }
20449 run_options += 2;
20450 }
20451 }
20452
20453 if (is_ssl_port_used(ports_option)) {
20454 /* Initialize with SSL support */
20456 } else {
20457 /* Initialize without SSL support */
20459 }
20460}
CIVETWEB_API unsigned mg_init_library(unsigned features)
Definition civetweb.c:22347
@ MG_FEATURES_DEFAULT
Definition civetweb.h:57
@ MG_FEATURES_TLS
Definition civetweb.h:66
const char * default_value
Definition civetweb.h:688

References config_options, mg_option::default_value, is_ssl_port_used(), LISTENING_PORTS, MG_FEATURES_DEFAULT, MG_FEATURES_TLS, mg_init_library(), and mg_option::name.

Referenced by mg_start2().

◆ load_tls_dll()

static void * load_tls_dll ( char * ebuf,
size_t ebuf_len,
const char * dll_name,
struct ssl_func * sw,
int * feature_missing )
static

Definition at line 16715 of file civetweb.c.

16720{
16721 union {
16722 void *p;
16723 void (*fp)(void);
16724 } u;
16725 void *dll_handle;
16726 struct ssl_func *fp;
16727 int ok;
16728 int truncated = 0;
16729
16730 if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
16732 NULL, /* No truncation check for ebuf */
16733 ebuf,
16734 ebuf_len,
16735 "%s: cannot load %s",
16736 __func__,
16737 dll_name);
16738 return NULL;
16739 }
16740
16741 ok = 1;
16742 for (fp = sw; fp->name != NULL; fp++) {
16743#if defined(_WIN32)
16744 /* GetProcAddress() returns pointer to function */
16745 u.fp = (void (*)(void))dlsym(dll_handle, fp->name);
16746#else
16747 /* dlsym() on UNIX returns void *. ISO C forbids casts of data
16748 * pointers to function pointers. We need to use a union to make a
16749 * cast. */
16750 u.p = dlsym(dll_handle, fp->name);
16751#endif /* _WIN32 */
16752
16753 /* Set pointer (might be NULL) */
16754 fp->ptr = u.fp;
16755
16756 if (u.fp == NULL) {
16757 DEBUG_TRACE("Missing function: %s\n", fp->name);
16758 if (feature_missing) {
16759 feature_missing[fp->required]++;
16760 }
16761 if (fp->required == TLS_Mandatory) {
16762 /* Mandatory function is missing */
16763 if (ok) {
16764 /* This is the first missing function.
16765 * Create a new error message. */
16767 &truncated,
16768 ebuf,
16769 ebuf_len,
16770 "%s: %s: cannot find %s",
16771 __func__,
16772 dll_name,
16773 fp->name);
16774 ok = 0;
16775 } else {
16776 /* This is yet anothermissing function.
16777 * Append existing error message. */
16778 size_t cur_len = strlen(ebuf);
16779 if (!truncated && ((ebuf_len - cur_len) > 3)) {
16781 &truncated,
16782 ebuf + cur_len,
16783 ebuf_len - cur_len - 3,
16784 ", %s",
16785 fp->name);
16786 if (truncated) {
16787 /* If truncated, add "..." */
16788 strcat(ebuf, "...");
16789 }
16790 }
16791 }
16792 }
16793 }
16794 }
16795
16796 if (!ok) {
16797 (void)dlclose(dll_handle);
16798 return NULL;
16799 }
16800
16801 return dll_handle;
16802}

References DEBUG_TRACE, mg_snprintf(), and NULL.

Referenced by initialize_openssl().

◆ log_access()

static void log_access ( const struct mg_connection * conn)
static

Definition at line 16063 of file civetweb.c.

16064{
16065 const struct mg_request_info *ri;
16066 struct mg_file fi;
16067 char date[64], src_addr[IP_ADDR_STR_LEN];
16068#if defined(REENTRANT_TIME)
16069 struct tm _tm;
16070 struct tm *tm = &_tm;
16071#else
16072 struct tm *tm;
16073#endif
16074
16075 const char *referer;
16076 const char *user_agent;
16077
16078 char log_buf[4096];
16079
16080 if (!conn || !conn->dom_ctx) {
16081 return;
16082 }
16083
16084 /* Set log message to "empty" */
16085 log_buf[0] = 0;
16086
16087#if defined(USE_LUA)
16088 if (conn->phys_ctx->lua_bg_log_available) {
16089 int ret;
16090 struct mg_context *ctx = conn->phys_ctx;
16091 lua_State *lstate = (lua_State *)ctx->lua_background_state;
16092 pthread_mutex_lock(&ctx->lua_bg_mutex);
16093 /* call "log()" in Lua */
16094 lua_getglobal(lstate, "log");
16095 prepare_lua_request_info_inner(conn, lstate);
16096 push_lua_response_log_data(conn, lstate);
16097
16098 ret = lua_pcall(lstate, /* args */ 2, /* results */ 1, 0);
16099 if (ret == 0) {
16100 int t = lua_type(lstate, -1);
16101 if (t == LUA_TBOOLEAN) {
16102 if (lua_toboolean(lstate, -1) == 0) {
16103 /* log() returned false: do not log */
16104 pthread_mutex_unlock(&ctx->lua_bg_mutex);
16105 return;
16106 }
16107 /* log returned true: continue logging */
16108 } else if (t == LUA_TSTRING) {
16109 size_t len;
16110 const char *txt = lua_tolstring(lstate, -1, &len);
16111 if ((len == 0) || (*txt == 0)) {
16112 /* log() returned empty string: do not log */
16113 pthread_mutex_unlock(&ctx->lua_bg_mutex);
16114 return;
16115 }
16116 /* Copy test from Lua into log_buf */
16117 if (len >= sizeof(log_buf)) {
16118 len = sizeof(log_buf) - 1;
16119 }
16120 memcpy(log_buf, txt, len);
16121 log_buf[len] = 0;
16122 }
16123 } else {
16124 lua_cry(conn, ret, lstate, "lua_background_script", "log");
16125 }
16126 pthread_mutex_unlock(&ctx->lua_bg_mutex);
16127 }
16128#endif
16129
16130 if (conn->dom_ctx->config[ACCESS_LOG_FILE] != NULL) {
16131 if (mg_fopen(conn,
16134 &fi)
16135 == 0) {
16136 fi.access.fp = NULL;
16137 }
16138 } else {
16139 fi.access.fp = NULL;
16140 }
16141
16142 /* Log is written to a file and/or a callback. If both are not set,
16143 * executing the rest of the function is pointless. */
16144 if ((fi.access.fp == NULL)
16145 && (conn->phys_ctx->callbacks.log_access == NULL)) {
16146 return;
16147 }
16148
16149 /* If we did not get a log message from Lua, create it here. */
16150 if (!log_buf[0]) {
16151#if defined(REENTRANT_TIME)
16152 localtime_r(&conn->conn_birth_time, tm);
16153#else
16154 tm = localtime(&conn->conn_birth_time);
16155#endif
16156 if (tm != NULL) {
16157 strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm);
16158 } else {
16159 mg_strlcpy(date, "01/Jan/1970:00:00:00 +0000", sizeof(date));
16160 }
16161
16162 ri = &conn->request_info;
16163
16164 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
16165 referer = header_val(conn, "Referer");
16166 user_agent = header_val(conn, "User-Agent");
16167
16168 mg_snprintf(conn,
16169 NULL, /* Ignore truncation in access log */
16170 log_buf,
16171 sizeof(log_buf),
16172 "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT
16173 " %s %s",
16174 src_addr,
16175 (ri->remote_user == NULL) ? "-" : ri->remote_user,
16176 date,
16177 ri->request_method ? ri->request_method : "-",
16178 ri->request_uri ? ri->request_uri : "-",
16179 ri->query_string ? "?" : "",
16180 ri->query_string ? ri->query_string : "",
16181 ri->http_version,
16182 conn->status_code,
16183 conn->num_bytes_sent,
16184 referer,
16185 user_agent);
16186 }
16187
16188 /* Here we have a log message in log_buf. Call the callback */
16189 if (conn->phys_ctx->callbacks.log_access) {
16190 if (conn->phys_ctx->callbacks.log_access(conn, log_buf)) {
16191 /* do not log if callback returns non-zero */
16192 if (fi.access.fp) {
16193 mg_fclose(&fi.access);
16194 }
16195 return;
16196 }
16197 }
16198
16199 /* Store in file */
16200 if (fi.access.fp) {
16201 int ok = 1;
16202 flockfile(fi.access.fp);
16203 if (fprintf(fi.access.fp, "%s\n", log_buf) < 1) {
16204 ok = 0;
16205 }
16206 if (fflush(fi.access.fp) != 0) {
16207 ok = 0;
16208 }
16209 funlockfile(fi.access.fp);
16210 if (mg_fclose(&fi.access) != 0) {
16211 ok = 0;
16212 }
16213 if (!ok) {
16214 mg_cry_internal(conn,
16215 "Error writing log file %s",
16217 }
16218 }
16219}
#define MG_FOPEN_MODE_APPEND
Definition civetweb.c:2849
static const char * header_val(const struct mg_connection *conn, const char *header)
Definition civetweb.c:16046
LUA_API int lua_toboolean(lua_State *L, int idx)
LUA_API const char * lua_tolstring(lua_State *L, int idx, size_t *len)
LUA_API int lua_type(lua_State *L, int idx)
#define LUA_TSTRING
#define LUA_TBOOLEAN
#define lua_getglobal(L, s)
#define lua_pcall(L, n, r, f)
int(* log_access)(const struct mg_connection *, const char *message)
Definition civetweb.h:244
time_t conn_birth_time
Definition civetweb.c:2508

References mg_file::access, ACCESS_LOG_FILE, mg_context::callbacks, mg_connection::client, mg_domain_context::config, mg_connection::conn_birth_time, mg_connection::dom_ctx, mg_file_access::fp, header_val(), mg_request_info::http_version, INT64_FMT, IP_ADDR_STR_LEN, mg_callbacks::log_access, lua_getglobal, lua_pcall, LUA_TBOOLEAN, lua_toboolean(), lua_tolstring(), LUA_TSTRING, lua_type(), mg_cry_internal, mg_fclose(), mg_fopen(), MG_FOPEN_MODE_APPEND, mg_snprintf(), mg_strlcpy(), NULL, mg_connection::num_bytes_sent, mg_connection::phys_ctx, mg_request_info::query_string, mg_request_info::remote_user, mg_connection::request_info, mg_request_info::request_method, mg_request_info::request_uri, socket::rsa, sockaddr_to_string(), and mg_connection::status_code.

Referenced by handle_request_stat_log().

◆ lowercase()

static int lowercase ( const char * s)
static

Definition at line 3012 of file civetweb.c.

3013{
3014 return tolower((unsigned char)*s);
3015}

References s.

Referenced by mg_strcasecmp(), mg_strncasecmp(), and set_throttle().

◆ master_thread()

static void * master_thread ( void * thread_func_param)
static

Definition at line 20231 of file civetweb.c.

20232{
20233#if !defined(__ZEPHYR__)
20234 struct sigaction sa;
20235
20236 /* Ignore SIGPIPE */
20237 memset(&sa, 0, sizeof(sa));
20238 sa.sa_handler = SIG_IGN;
20239 sigaction(SIGPIPE, &sa, NULL);
20240#endif
20241
20242 master_thread_run((struct mg_context *)thread_func_param);
20243 return NULL;
20244}
static void master_thread_run(struct mg_context *ctx)
Definition civetweb.c:20044

References master_thread_run(), and NULL.

Referenced by mg_start2().

◆ master_thread_run()

static void master_thread_run ( struct mg_context * ctx)
static

Definition at line 20044 of file civetweb.c.

20045{
20046 struct mg_workerTLS tls;
20047 struct mg_pollfd *pfd;
20048 unsigned int i;
20049 unsigned int workerthreadcount;
20050
20051 if (!ctx) {
20052 return;
20053 }
20054
20055 mg_set_thread_name("master");
20056
20057 /* Increase priority of the master thread */
20058#if defined(_WIN32)
20059 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
20060#elif defined(USE_MASTER_THREAD_PRIORITY)
20061 int min_prio = sched_get_priority_min(SCHED_RR);
20062 int max_prio = sched_get_priority_max(SCHED_RR);
20063 if ((min_prio >= 0) && (max_prio >= 0)
20064 && ((USE_MASTER_THREAD_PRIORITY) <= max_prio)
20065 && ((USE_MASTER_THREAD_PRIORITY) >= min_prio)) {
20066 struct sched_param sched_param = {0};
20067 sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY);
20068 pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
20069 }
20070#endif
20071
20072 /* Initialize thread local storage */
20073#if defined(_WIN32)
20074 tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
20075#endif
20076 tls.is_master = 1;
20077 pthread_setspecific(sTlsKey, &tls);
20078
20079 if (ctx->callbacks.init_thread) {
20080 /* Callback for the master thread (type 0) */
20081 tls.user_ptr = ctx->callbacks.init_thread(ctx, 0);
20082 } else {
20083 tls.user_ptr = NULL;
20084 }
20085
20086 /* Lua background script "start" event */
20087#if defined(USE_LUA)
20088 if (ctx->lua_background_state) {
20089 lua_State *lstate = (lua_State *)ctx->lua_background_state;
20090 pthread_mutex_lock(&ctx->lua_bg_mutex);
20091
20092 /* call "start()" in Lua */
20093 lua_getglobal(lstate, "start");
20094 if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20095 int ret = lua_pcall(lstate, /* args */ 0, /* results */ 0, 0);
20096 if (ret != 0) {
20097 struct mg_connection fc;
20098 lua_cry(fake_connection(&fc, ctx),
20099 ret,
20100 lstate,
20101 "lua_background_script",
20102 "start");
20103 }
20104 } else {
20105 lua_pop(lstate, 1);
20106 }
20107
20108 /* determine if there is a "log()" function in Lua background script */
20109 lua_getglobal(lstate, "log");
20110 if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20111 ctx->lua_bg_log_available = 1;
20112 }
20113 lua_pop(lstate, 1);
20114
20115 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20116 }
20117#endif
20118
20119 /* Server starts *now* */
20120 ctx->start_time = time(NULL);
20121
20122 /* Server accept loop */
20123 pfd = ctx->listening_socket_fds;
20124 while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
20125 for (i = 0; i < ctx->num_listening_sockets; i++) {
20126 pfd[i].fd = ctx->listening_sockets[i].sock;
20127 pfd[i].events = POLLIN;
20128 }
20129
20130 if (mg_poll(pfd,
20133 &(ctx->stop_flag))
20134 > 0) {
20135 for (i = 0; i < ctx->num_listening_sockets; i++) {
20136 /* NOTE(lsm): on QNX, poll() returns POLLRDNORM after the
20137 * successful poll, and POLLIN is defined as
20138 * (POLLRDNORM | POLLRDBAND)
20139 * Therefore, we're checking pfd[i].revents & POLLIN, not
20140 * pfd[i].revents == POLLIN. */
20141 if (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
20142 && (pfd[i].revents & POLLIN)) {
20144 }
20145 }
20146 }
20147 }
20148
20149 /* Here stop_flag is 1 - Initiate shutdown. */
20150 DEBUG_TRACE("%s", "stopping workers");
20151
20152 /* Stop signal received: somebody called mg_stop. Quit. */
20154
20155 /* Wakeup workers that are waiting for connections to handle. */
20156#if defined(ALTERNATIVE_QUEUE)
20157 for (i = 0; i < ctx->cfg_worker_threads; i++) {
20158 event_signal(ctx->client_wait_events[i]);
20159 }
20160#else
20161 (void)pthread_mutex_lock(&ctx->thread_mutex);
20162 pthread_cond_broadcast(&ctx->sq_full);
20163 (void)pthread_mutex_unlock(&ctx->thread_mutex);
20164#endif
20165
20166 /* Join all worker threads to avoid leaking threads. */
20167 workerthreadcount = ctx->cfg_worker_threads;
20168 for (i = 0; i < workerthreadcount; i++) {
20169 if (ctx->worker_threadids[i] != 0) {
20171 }
20172 }
20173
20174#if defined(USE_LUA)
20175 /* Free Lua state of lua background task */
20176 if (ctx->lua_background_state) {
20177 lua_State *lstate = (lua_State *)ctx->lua_background_state;
20178 ctx->lua_bg_log_available = 0;
20179
20180 /* call "stop()" in Lua */
20181 pthread_mutex_lock(&ctx->lua_bg_mutex);
20182 lua_getglobal(lstate, "stop");
20183 if (lua_type(lstate, -1) == LUA_TFUNCTION) {
20184 int ret = lua_pcall(lstate, /* args */ 0, /* results */ 0, 0);
20185 if (ret != 0) {
20186 struct mg_connection fc;
20187 lua_cry(fake_connection(&fc, ctx),
20188 ret,
20189 lstate,
20190 "lua_background_script",
20191 "stop");
20192 }
20193 }
20194 DEBUG_TRACE("Close Lua background state %p", lstate);
20195 lua_close(lstate);
20196
20197 ctx->lua_background_state = 0;
20198 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20199 }
20200#endif
20201
20202 DEBUG_TRACE("%s", "exiting");
20203
20204 /* call exit thread callback */
20205 if (ctx->callbacks.exit_thread) {
20206 /* Callback for the master thread (type 0) */
20207 ctx->callbacks.exit_thread(ctx, 0, tls.user_ptr);
20208 }
20209
20210#if defined(_WIN32)
20211 CloseHandle(tls.pthread_cond_helper_mutex);
20212#endif
20213 pthread_setspecific(sTlsKey, NULL);
20214
20215 /* Signal mg_stop() that we're done.
20216 * WARNING: This must be the very last thing this
20217 * thread does, as ctx becomes invalid after this line. */
20218 STOP_FLAG_ASSIGN(&ctx->stop_flag, 2);
20219}
static pthread_key_t sTlsKey
Definition civetweb.c:1580
static void close_all_listening_sockets(struct mg_context *ctx)
Definition civetweb.c:15456
static struct mg_connection * fake_connection(struct mg_connection *fc, struct mg_context *ctx)
Definition civetweb.c:3470
static void static void mg_set_thread_name(const char *name)
Definition civetweb.c:2780
static void accept_new_connection(const struct socket *listener, struct mg_context *ctx)
Definition civetweb.c:19957
#define SOCKET_TIMEOUT_QUANTUM
Definition civetweb.c:473
static int mg_join_thread(pthread_t threadid)
Definition civetweb.c:5757
LUA_API void lua_close(lua_State *L)
#define lua_pop(L, n)
#define LUA_TFUNCTION
void *(* init_thread)(const struct mg_context *ctx, int thread_type)
Definition civetweb.h:393
void(* exit_thread)(const struct mg_context *ctx, int thread_type, void *thread_pointer)
Definition civetweb.h:400
time_t start_time
Definition civetweb.c:2412
unsigned int cfg_worker_threads
Definition civetweb.c:2378

References accept_new_connection(), mg_context::callbacks, mg_context::cfg_worker_threads, close_all_listening_sockets(), DEBUG_TRACE, mg_callbacks::exit_thread, fake_connection(), FALSE, mg_callbacks::init_thread, mg_workerTLS::is_master, mg_context::listening_socket_fds, mg_context::listening_sockets, lua_close(), lua_getglobal, lua_pcall, lua_pop, LUA_TFUNCTION, lua_type(), mg_join_thread(), mg_poll(), mg_pollfd, mg_set_thread_name(), NULL, mg_context::num_listening_sockets, socket::sock, SOCKET_TIMEOUT_QUANTUM, mg_context::sq_full, mg_context::start_time, sTlsKey, mg_context::stop_flag, STOP_FLAG_ASSIGN, STOP_FLAG_IS_ZERO, mg_context::thread_mutex, mg_workerTLS::user_ptr, and mg_context::worker_threadids.

Referenced by master_thread().

◆ mg_atomic_dec()

static FUNCTION_MAY_BE_UNUSED ptrdiff_t mg_atomic_dec ( volatile ptrdiff_t * addr)
static

Definition at line 1144 of file civetweb.c.

1145{
1146 ptrdiff_t ret;
1147
1148#if defined(_WIN64) && !defined(NO_ATOMICS)
1149 ret = InterlockedDecrement64(addr);
1150#elif defined(_WIN32) && !defined(NO_ATOMICS)
1151 ret = InterlockedDecrement(addr);
1152#elif defined(__GNUC__) \
1153 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1154 && !defined(NO_ATOMICS)
1155 ret = __sync_sub_and_fetch(addr, 1);
1156#else
1158 ret = (--(*addr));
1160#endif
1161 return ret;
1162}
static FUNCTION_MAY_BE_UNUSED void mg_global_unlock(void)
Definition civetweb.c:1102
static FUNCTION_MAY_BE_UNUSED void mg_global_lock(void)
Definition civetweb.c:1094

References mg_global_lock(), and mg_global_unlock().

Referenced by abort_cgi_process(), process_new_connection(), and uninitialize_openssl().

◆ mg_atomic_inc()

static FUNCTION_MAY_BE_UNUSED ptrdiff_t mg_atomic_inc ( volatile ptrdiff_t * addr)
static

Definition at line 1121 of file civetweb.c.

1122{
1123 ptrdiff_t ret;
1124
1125#if defined(_WIN64) && !defined(NO_ATOMICS)
1126 ret = InterlockedIncrement64(addr);
1127#elif defined(_WIN32) && !defined(NO_ATOMICS)
1128 ret = InterlockedIncrement(addr);
1129#elif defined(__GNUC__) \
1130 && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
1131 && !defined(NO_ATOMICS)
1132 ret = __sync_add_and_fetch(addr, 1);
1133#else
1135 ret = (++(*addr));
1137#endif
1138 return ret;
1139}

References mg_global_lock(), and mg_global_unlock().

Referenced by initialize_openssl(), mg_current_thread_id(), mg_start2(), process_new_connection(), and worker_thread_run().

◆ mg_base64_decode()

CIVETWEB_API int mg_base64_decode ( const char * src,
size_t src_len,
unsigned char * dst,
size_t * dst_len )

Definition at line 7383 of file civetweb.c.

7387{
7388 size_t i;
7389 unsigned char a, b, c, d;
7390 size_t dst_len_limit = (size_t)-1;
7391 size_t dst_len_used = 0;
7392
7393 if (dst_len != NULL) {
7394 dst_len_limit = *dst_len;
7395 *dst_len = 0;
7396 }
7397
7398 for (i = 0; i < src_len; i += 4) {
7399 /* Read 4 characters from BASE64 string */
7400 a = b64reverse(src[i]);
7401 if (a >= 254) {
7402 return (int)i;
7403 }
7404
7405 b = b64reverse(((i + 1) >= src_len) ? 0 : src[i + 1]);
7406 if (b >= 254) {
7407 return (int)i + 1;
7408 }
7409
7410 c = b64reverse(((i + 2) >= src_len) ? 0 : src[i + 2]);
7411 if (c == 254) {
7412 return (int)i + 2;
7413 }
7414
7415 d = b64reverse(((i + 3) >= src_len) ? 0 : src[i + 3]);
7416 if (d == 254) {
7417 return (int)i + 3;
7418 }
7419
7420 /* Add first (of 3) decoded character */
7421 if (dst_len_used < dst_len_limit) {
7422 dst[dst_len_used] = (unsigned char)((unsigned char)(a << 2)
7423 + (unsigned char)(b >> 4));
7424 }
7425 dst_len_used++;
7426
7427 if (c != 255) {
7428 if (dst_len_used < dst_len_limit) {
7429
7430 dst[dst_len_used] = (unsigned char)((unsigned char)(b << 4)
7431 + (unsigned char)(c >> 2));
7432 }
7433 dst_len_used++;
7434 if (d != 255) {
7435 if (dst_len_used < dst_len_limit) {
7436 dst[dst_len_used] =
7437 (unsigned char)((unsigned char)(c << 6) + d);
7438 }
7439 dst_len_used++;
7440 }
7441 }
7442 }
7443
7444 /* Add terminating zero */
7445 if (dst_len_used < dst_len_limit) {
7446 dst[dst_len_used] = '\0';
7447 }
7448 dst_len_used++;
7449 if (dst_len != NULL) {
7450 *dst_len = dst_len_used;
7451 }
7452
7453 if (dst_len_used > dst_len_limit) {
7454 /* Out of memory */
7455 return 0;
7456 }
7457
7458 /* Return -1 for "OK" */
7459 return -1;
7460}
static unsigned char b64reverse(char letter)
Definition civetweb.c:7358

References b64reverse(), and NULL.

Referenced by parse_auth_header().

◆ mg_base64_encode()

CIVETWEB_API int mg_base64_encode ( const unsigned char * src,
size_t src_len,
char * dst,
size_t * dst_len )

Definition at line 7305 of file civetweb.c.

7309{
7310 static const char *b64 =
7311 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
7312 size_t i, j;
7313 int a, b, c;
7314
7315 if (dst_len != NULL) {
7316 /* Expected length including 0 termination: */
7317 /* IN 1 -> OUT 5, IN 2 -> OUT 5, IN 3 -> OUT 5, IN 4 -> OUT 9,
7318 * IN 5 -> OUT 9, IN 6 -> OUT 9, IN 7 -> OUT 13, etc. */
7319 size_t expected_len = ((src_len + 2) / 3) * 4 + 1;
7320 if (*dst_len < expected_len) {
7321 if (*dst_len > 0) {
7322 dst[0] = '\0';
7323 }
7324 *dst_len = expected_len;
7325 return 0;
7326 }
7327 }
7328
7329 for (i = j = 0; i < src_len; i += 3) {
7330 a = src[i];
7331 b = ((i + 1) >= src_len) ? 0 : src[i + 1];
7332 c = ((i + 2) >= src_len) ? 0 : src[i + 2];
7333
7334 dst[j++] = b64[a >> 2];
7335 dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
7336 if (i + 1 < src_len) {
7337 dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
7338 }
7339 if (i + 2 < src_len) {
7340 dst[j++] = b64[c & 63];
7341 }
7342 }
7343 while (j % 4 != 0) {
7344 dst[j++] = '=';
7345 }
7346 dst[j++] = '\0';
7347
7348 if (dst_len != NULL) {
7349 *dst_len = (size_t)j;
7350 }
7351
7352 /* Return -1 for "OK" */
7353 return -1;
7354}

References NULL.

◆ mg_calloc()

static __inline void * mg_calloc ( size_t a,
size_t b )
static

Definition at line 1480 of file civetweb.c.

1481{
1482 return calloc(a, b);
1483}
#define calloc
Definition civetweb.c:1540

References calloc.

Referenced by mg_connect_client_impl(), mg_modify_passwords_file_ha1(), and mg_start2().

◆ mg_check_digest_access_authentication()

CIVETWEB_API int mg_check_digest_access_authentication ( struct mg_connection * conn,
const char * realm,
const char * filename )

Definition at line 8893 of file civetweb.c.

8896{
8897 struct mg_file file = STRUCT_FILE_INITIALIZER;
8898 int auth;
8899
8900 if (!conn || !filename) {
8901 return -1;
8902 }
8903 if (!mg_fopen(conn, filename, MG_FOPEN_MODE_READ, &file)) {
8904 return -2;
8905 }
8906
8907 auth = authorize(conn, &file, realm);
8908
8909 mg_fclose(&file.access);
8910
8911 return auth;
8912}

References mg_file::access, authorize(), mg_fclose(), mg_fopen(), MG_FOPEN_MODE_READ, and STRUCT_FILE_INITIALIZER.

◆ mg_check_feature()

CIVETWEB_API unsigned mg_check_feature ( unsigned feature)

Definition at line 21430 of file civetweb.c.

21431{
21432 static const unsigned feature_set = 0
21433 /* Set bits for available features according to API documentation.
21434 * This bit mask is created at compile time, according to the active
21435 * preprocessor defines. It is a single const value at runtime. */
21436#if !defined(NO_FILES)
21438#endif
21439#if !defined(NO_SSL) || defined(USE_MBEDTLS)
21441#endif
21442#if !defined(NO_CGI)
21444#endif
21445#if defined(USE_IPV6)
21447#endif
21448#if defined(USE_WEBSOCKET)
21450#endif
21451#if defined(USE_LUA)
21453#endif
21454#if defined(USE_DUKTAPE)
21456#endif
21457#if !defined(NO_CACHING)
21459#endif
21460#if defined(USE_SERVER_STATS)
21462#endif
21463#if defined(USE_ZLIB)
21465#endif
21466#if defined(USE_HTTP2)
21468#endif
21469#if defined(USE_X_DOM_SOCKET)
21471#endif
21472
21473 /* Set some extra bits not defined in the API documentation.
21474 * These bits may change without further notice. */
21475#if defined(MG_LEGACY_INTERFACE)
21476 | 0x80000000u
21477#endif
21478#if defined(MG_EXPERIMENTAL_INTERFACES)
21479 | 0x40000000u
21480#endif
21481#if !defined(NO_RESPONSE_BUFFERING)
21482 | 0x20000000u
21483#endif
21484#if defined(MEMORY_DEBUGGING)
21485 | 0x10000000u
21486#endif
21487 ;
21488 return (feature & feature_set);
21489}
@ MG_FEATURES_HTTP2
Definition civetweb.h:102
@ MG_FEATURES_STATS
Definition civetweb.h:95
@ MG_FEATURES_CACHE
Definition civetweb.h:91
@ MG_FEATURES_FILES
Definition civetweb.h:61
@ MG_FEATURES_CGI
Definition civetweb.h:71
@ MG_FEATURES_IPV6
Definition civetweb.h:75
@ MG_FEATURES_X_DOMAIN_SOCKET
Definition civetweb.h:105
@ MG_FEATURES_SSL
Definition civetweb.h:67
@ MG_FEATURES_LUA
Definition civetweb.h:83
@ MG_FEATURES_WEBSOCKET
Definition civetweb.h:79
@ MG_FEATURES_SSJS
Definition civetweb.h:87
@ MG_FEATURES_COMPRESSION
Definition civetweb.h:99

References MG_FEATURES_CACHE, MG_FEATURES_CGI, MG_FEATURES_COMPRESSION, MG_FEATURES_FILES, MG_FEATURES_HTTP2, MG_FEATURES_IPV6, MG_FEATURES_LUA, MG_FEATURES_SSJS, MG_FEATURES_SSL, MG_FEATURES_STATS, MG_FEATURES_WEBSOCKET, and MG_FEATURES_X_DOMAIN_SOCKET.

Referenced by mg_get_system_info(), and mg_init_library().

◆ mg_close_connection()

CIVETWEB_API void mg_close_connection ( struct mg_connection * conn)

Definition at line 17938 of file civetweb.c.

17939{
17940 if ((conn == NULL) || (conn->phys_ctx == NULL)) {
17941 return;
17942 }
17943
17944#if defined(USE_WEBSOCKET)
17945 if (conn->phys_ctx->context_type == CONTEXT_SERVER) {
17946 if (conn->in_websocket_handling) {
17947 /* Set close flag, so the server thread can exit. */
17948 conn->must_close = 1;
17949 return;
17950 }
17951 }
17952 if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) {
17953
17954 unsigned int i;
17955
17956 /* client context: loops must end */
17958 conn->must_close = 1;
17959
17960 /* We need to get the client thread out of the select/recv call
17961 * here. */
17962 /* Since we use a sleep quantum of some seconds to check for recv
17963 * timeouts, we will just wait a few seconds in mg_join_thread. */
17964
17965 /* join worker thread */
17966 for (i = 0; i < conn->phys_ctx->cfg_worker_threads; i++) {
17968 }
17969 }
17970#endif /* defined(USE_WEBSOCKET) */
17971
17972 close_connection(conn);
17973
17974#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
17977 && (conn->phys_ctx->dd.ssl_ctx != NULL)) {
17978 SSL_CTX_free(conn->phys_ctx->dd.ssl_ctx);
17979 }
17980#endif
17981
17982#if defined(USE_WEBSOCKET)
17983 if (conn->phys_ctx->context_type == CONTEXT_WS_CLIENT) {
17985 (void)pthread_mutex_destroy(&conn->mutex);
17986 mg_free(conn);
17987 } else if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) {
17988 (void)pthread_mutex_destroy(&conn->mutex);
17989 mg_free(conn);
17990 }
17991#else
17992 if (conn->phys_ctx->context_type == CONTEXT_HTTP_CLIENT) { /* Client */
17993 (void)pthread_mutex_destroy(&conn->mutex);
17994 mg_free(conn);
17995 }
17996#endif /* defined(USE_WEBSOCKET) */
17997}
static void close_connection(struct mg_connection *conn)
Definition civetweb.c:17864
pthread_mutex_t mutex
Definition civetweb.c:2565

References mg_context::cfg_worker_threads, close_connection(), CONTEXT_HTTP_CLIENT, CONTEXT_SERVER, mg_context::context_type, CONTEXT_WS_CLIENT, mg_context::dd, mg_free(), mg_join_thread(), mg_connection::must_close, mg_connection::mutex, NULL, mg_connection::phys_ctx, mg_domain_context::ssl_ctx, mg_context::stop_flag, STOP_FLAG_ASSIGN, and mg_context::worker_threadids.

Referenced by mg_connect_websocket_client_impl(), mg_download(), and run_client().

◆ mg_connect_client()

CIVETWEB_API struct mg_connection * mg_connect_client ( const char * host,
int port,
int use_ssl,
char * error_buffer,
size_t error_buffer_size )

Definition at line 18253 of file civetweb.c.

18258{
18259 struct mg_client_options opts;
18260 struct mg_init_data init;
18261 struct mg_error_data error;
18262
18263 memset(&init, 0, sizeof(init));
18264
18265 memset(&error, 0, sizeof(error));
18266 error.text_buffer_size = error_buffer_size;
18267 error.text = error_buffer;
18268
18269 memset(&opts, 0, sizeof(opts));
18270 opts.host = host;
18271 opts.port = port;
18272 if (use_ssl) {
18273 opts.host_name = host;
18274 }
18275
18276 return mg_connect_client_impl(&opts, use_ssl, &init, &error);
18277}
static struct mg_connection * mg_connect_client_impl(const struct mg_client_options *client_options, int use_ssl, struct mg_init_data *init, struct mg_error_data *error)
Definition civetweb.c:18001

References error(), mg_client_options::host, mg_client_options::host_name, mg_connect_client_impl(), and mg_client_options::port.

Referenced by mg_download(), and run_client().

◆ mg_connect_client_impl()

static struct mg_connection * mg_connect_client_impl ( const struct mg_client_options * client_options,
int use_ssl,
struct mg_init_data * init,
struct mg_error_data * error )
static

Definition at line 18001 of file civetweb.c.

18005{
18006 struct mg_connection *conn = NULL;
18007 SOCKET sock;
18008 union usa sa;
18009 struct sockaddr *psa;
18010 socklen_t len;
18011
18012 unsigned max_req_size =
18013 (unsigned)atoi(config_options[MAX_REQUEST_SIZE].default_value);
18014
18015 /* Size of structures, aligned to 8 bytes */
18016 size_t conn_size = ((sizeof(struct mg_connection) + 7) >> 3) << 3;
18017 size_t ctx_size = ((sizeof(struct mg_context) + 7) >> 3) << 3;
18018 size_t alloc_size = conn_size + ctx_size + max_req_size;
18019
18020 (void)init; /* TODO: Implement required options */
18021
18022 conn = (struct mg_connection *)mg_calloc(1, alloc_size);
18023
18024 if (error != NULL) {
18026 error->code_sub = 0;
18027 if (error->text_buffer_size > 0) {
18028 error->text[0] = 0;
18029 }
18030 }
18031
18032 if (conn == NULL) {
18033 if (error != NULL) {
18035 error->code_sub = (unsigned)alloc_size;
18037 NULL, /* No truncation check for ebuf */
18038 error->text,
18039 error->text_buffer_size,
18040 "calloc(): %s",
18041 strerror(ERRNO));
18042 }
18043 return NULL;
18044 }
18045
18046#if defined(GCC_DIAGNOSTIC)
18047#pragma GCC diagnostic push
18048#pragma GCC diagnostic ignored "-Wcast-align"
18049#endif /* defined(GCC_DIAGNOSTIC) */
18050 /* conn_size is aligned to 8 bytes */
18051
18052 conn->phys_ctx = (struct mg_context *)(((char *)conn) + conn_size);
18053
18054#if defined(GCC_DIAGNOSTIC)
18055#pragma GCC diagnostic pop
18056#endif /* defined(GCC_DIAGNOSTIC) */
18057
18058 conn->buf = (((char *)conn) + conn_size + ctx_size);
18059 conn->buf_size = (int)max_req_size;
18060 conn->phys_ctx->context_type = CONTEXT_HTTP_CLIENT;
18061 conn->dom_ctx = &(conn->phys_ctx->dd);
18062
18063 if (!connect_socket(conn->phys_ctx,
18064 client_options->host,
18065 client_options->port,
18066 use_ssl,
18067 error,
18068 &sock,
18069 &sa)) {
18070 /* "error" will be set by connect_socket. */
18071 /* free all memory and return NULL; */
18072 mg_free(conn);
18073 return NULL;
18074 }
18075
18076#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
18077#if (defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)) \
18078 && !defined(NO_SSL_DL)
18079
18080 if (use_ssl
18081 && (conn->dom_ctx->ssl_ctx = SSL_CTX_new(TLS_client_method()))
18082 == NULL) {
18083 if (error != NULL) {
18086 NULL, /* No truncation check for ebuf */
18087 error->text,
18088 error->text_buffer_size,
18089 "SSL_CTX_new error: %s",
18090 ssl_error());
18091 }
18092
18093 closesocket(sock);
18094 mg_free(conn);
18095 return NULL;
18096 }
18097
18098#else
18099
18100 if (use_ssl
18101 && (conn->dom_ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method()))
18102 == NULL) {
18103 if (error != NULL) {
18106 NULL, /* No truncation check for ebuf */
18107 error->text,
18108 error->text_buffer_size,
18109 "SSL_CTX_new error: %s",
18110 ssl_error());
18111 }
18112
18113 closesocket(sock);
18114 mg_free(conn);
18115 return NULL;
18116 }
18117
18118#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
18119#endif /* NO_SSL */
18120
18121#if defined(USE_IPV6)
18122 len = (sa.sa.sa_family == AF_INET) ? sizeof(conn->client.rsa.sin)
18123 : sizeof(conn->client.rsa.sin6);
18124 psa = (sa.sa.sa_family == AF_INET)
18125 ? (struct sockaddr *)&(conn->client.rsa.sin)
18126 : (struct sockaddr *)&(conn->client.rsa.sin6);
18127#else
18128 len = sizeof(conn->client.rsa.sin);
18129 psa = (struct sockaddr *)&(conn->client.rsa.sin);
18130#endif
18131
18132 conn->client.sock = sock;
18133 conn->client.lsa = sa;
18134
18135 if (getsockname(sock, psa, &len) != 0) {
18136 mg_cry_internal(conn,
18137 "%s: getsockname() failed: %s",
18138 __func__,
18139 strerror(ERRNO));
18140 }
18141
18142 conn->client.is_ssl = use_ssl ? 1 : 0;
18143 if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
18144 if (error != NULL) {
18146 error->code_sub = (unsigned)ERRNO;
18148 NULL, /* No truncation check for ebuf */
18149 error->text,
18150 error->text_buffer_size,
18151 "Can not create mutex");
18152 }
18153#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
18154 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18155#endif
18156 closesocket(sock);
18157 mg_free(conn);
18158 return NULL;
18159 }
18160
18161#if !defined(NO_SSL) && !defined(USE_MBEDTLS) // TODO: mbedTLS client
18162 if (use_ssl) {
18163 /* TODO: Check ssl_verify_peer and ssl_ca_path here.
18164 * SSL_CTX_set_verify call is needed to switch off server
18165 * certificate checking, which is off by default in OpenSSL and
18166 * on in yaSSL. */
18167 /* TODO: SSL_CTX_set_verify(conn->dom_ctx,
18168 * SSL_VERIFY_PEER, verify_ssl_server); */
18169
18170 if (client_options->client_cert) {
18171 if (!ssl_use_pem_file(conn->phys_ctx,
18172 conn->dom_ctx,
18173 client_options->client_cert,
18174 NULL)) {
18175 if (error != NULL) {
18178 NULL, /* No truncation check for ebuf */
18179 error->text,
18180 error->text_buffer_size,
18181 "Can not use SSL client certificate");
18182 }
18183
18184 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18185 closesocket(sock);
18186 mg_free(conn);
18187 return NULL;
18188 }
18189 }
18190
18191 if (client_options->server_cert) {
18192 if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx,
18193 client_options->server_cert,
18194 NULL)
18195 != 1) {
18196 if (error != NULL) {
18199 NULL, /* No truncation check for ebuf */
18200 error->text,
18201 error->text_buffer_size,
18202 "SSL_CTX_load_verify_locations error: %s",
18203 ssl_error());
18204 }
18205 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18206 closesocket(sock);
18207 mg_free(conn);
18208 return NULL;
18209 }
18210 SSL_CTX_set_verify(conn->dom_ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
18211 } else {
18212 SSL_CTX_set_verify(conn->dom_ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
18213 }
18214
18215 if (!sslize(conn, SSL_connect, client_options)) {
18216 if (error != NULL) {
18219 NULL, /* No truncation check for ebuf */
18220 error->text,
18221 error->text_buffer_size,
18222 "SSL connection error");
18223 }
18224 SSL_CTX_free(conn->dom_ctx->ssl_ctx);
18225 closesocket(sock);
18226 mg_free(conn);
18227 return NULL;
18228 }
18229 }
18230#endif
18231
18232 return conn;
18233}
static int connect_socket(struct mg_context *ctx, const char *host, int port, int use_ssl, struct mg_error_data *error, SOCKET *sock, union usa *sa)
Definition civetweb.c:9287
int SOCKET
Definition civetweb.c:928
static int sslize(struct mg_connection *conn, int(*func)(SSL *), const struct mg_client_options *client_options)
Definition civetweb.c:16455
static __inline void * mg_calloc(size_t a, size_t b)
Definition civetweb.c:1480
@ MG_ERROR_DATA_CODE_TLS_CONNECT_ERROR
Definition civetweb.h:1794
@ MG_ERROR_DATA_CODE_OK
Definition civetweb.h:1734
@ MG_ERROR_DATA_CODE_TLS_SERVER_CERT_ERROR
Definition civetweb.h:1791
@ MG_ERROR_DATA_CODE_OUT_OF_MEMORY
Definition civetweb.h:1752
@ MG_ERROR_DATA_CODE_INIT_TLS_FAILED
Definition civetweb.h:1743
@ MG_ERROR_DATA_CODE_TLS_CLIENT_CERT_ERROR
Definition civetweb.h:1788
const char * client_cert
Definition civetweb.h:1486
const char * server_cert
Definition civetweb.h:1487
const char * host
Definition civetweb.h:1484
struct sockaddr sa
Definition civetweb.c:1833

References mg_connection::buf, mg_connection::buf_size, mg_connection::client, mg_client_options::client_cert, closesocket, config_options, connect_socket(), CONTEXT_HTTP_CLIENT, mg_context::context_type, mg_context::dd, mg_connection::dom_ctx, ERRNO, error(), mg_client_options::host, socket::is_ssl, socket::lsa, MAX_REQUEST_SIZE, mg_calloc(), mg_cry_internal, MG_ERROR_DATA_CODE_INIT_TLS_FAILED, MG_ERROR_DATA_CODE_OK, MG_ERROR_DATA_CODE_OS_ERROR, MG_ERROR_DATA_CODE_OUT_OF_MEMORY, MG_ERROR_DATA_CODE_TLS_CLIENT_CERT_ERROR, MG_ERROR_DATA_CODE_TLS_CONNECT_ERROR, MG_ERROR_DATA_CODE_TLS_SERVER_CERT_ERROR, mg_free(), mg_snprintf(), mg_connection::mutex, NULL, mg_connection::phys_ctx, mg_client_options::port, pthread_mutex_attr, socket::rsa, usa::sa, mg_client_options::server_cert, usa::sin, socket::sock, mg_domain_context::ssl_ctx, ssl_error(), ssl_use_pem_file(), and sslize().

Referenced by mg_connect_client(), mg_connect_client_secure(), and mg_connect_websocket_client_impl().

◆ mg_connect_client_secure()

CIVETWEB_API struct mg_connection * mg_connect_client_secure ( const struct mg_client_options * client_options,
char * error_buffer,
size_t error_buffer_size )

Definition at line 18237 of file civetweb.c.

18240{
18241 struct mg_init_data init;
18242 struct mg_error_data error;
18243
18244 memset(&init, 0, sizeof(init));
18245 memset(&error, 0, sizeof(error));
18246 error.text_buffer_size = error_buffer_size;
18247 error.text = error_buffer;
18248 return mg_connect_client_impl(client_options, 1, &init, &error);
18249}

References error(), and mg_connect_client_impl().

◆ mg_connect_websocket_client()

CIVETWEB_API struct mg_connection * mg_connect_websocket_client ( const char * host,
int port,
int use_ssl,
char * error_buffer,
size_t error_buffer_size,
const char * path,
const char * origin,
mg_websocket_data_handler data_func,
mg_websocket_close_handler close_func,
void * user_data )

Definition at line 19212 of file civetweb.c.

19222{
19223 struct mg_client_options client_options;
19224 memset(&client_options, 0, sizeof(client_options));
19225 client_options.host = host;
19226 client_options.port = port;
19227
19228 return mg_connect_websocket_client_impl(&client_options,
19229 use_ssl,
19230 error_buffer,
19231 error_buffer_size,
19232 path,
19233 origin,
19234 NULL,
19235 data_func,
19236 close_func,
19237 user_data);
19238}
static struct mg_connection * mg_connect_websocket_client_impl(const struct mg_client_options *client_options, int use_ssl, char *error_buffer, size_t error_buffer_size, const char *path, const char *origin, const char *extensions, mg_websocket_data_handler data_func, mg_websocket_close_handler close_func, void *user_data)
Definition civetweb.c:18997
static void close_func(LexState *ls)

References close_func(), mg_client_options::host, mg_connect_websocket_client_impl(), NULL, and mg_client_options::port.

◆ mg_connect_websocket_client_extensions()

CIVETWEB_API struct mg_connection * mg_connect_websocket_client_extensions ( const char * host,
int port,
int use_ssl,
char * error_buffer,
size_t error_buffer_size,
const char * path,
const char * origin,
const char * extensions,
mg_websocket_data_handler data_func,
mg_websocket_close_handler close_func,
void * user_data )

Definition at line 19269 of file civetweb.c.

19280{
19281 struct mg_client_options client_options;
19282 memset(&client_options, 0, sizeof(client_options));
19283 client_options.host = host;
19284 client_options.port = port;
19285
19286 return mg_connect_websocket_client_impl(&client_options,
19287 use_ssl,
19288 error_buffer,
19289 error_buffer_size,
19290 path,
19291 origin,
19292 extensions,
19293 data_func,
19294 close_func,
19295 user_data);
19296}

References close_func(), mg_client_options::host, mg_connect_websocket_client_impl(), and mg_client_options::port.

◆ mg_connect_websocket_client_impl()

static struct mg_connection * mg_connect_websocket_client_impl ( const struct mg_client_options * client_options,
int use_ssl,
char * error_buffer,
size_t error_buffer_size,
const char * path,
const char * origin,
const char * extensions,
mg_websocket_data_handler data_func,
mg_websocket_close_handler close_func,
void * user_data )
static

Definition at line 18997 of file civetweb.c.

19007{
19008 struct mg_connection *conn = NULL;
19009
19010#if defined(USE_WEBSOCKET)
19011 struct websocket_client_thread_data *thread_data;
19012 static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw==";
19013
19014 const char *host = client_options->host;
19015 int i;
19016
19017 struct mg_init_data init;
19018 struct mg_error_data error;
19019
19020 memset(&init, 0, sizeof(init));
19021 memset(&error, 0, sizeof(error));
19022 error.text_buffer_size = error_buffer_size;
19023 error.text = error_buffer;
19024
19025#if defined(__clang__)
19026#pragma clang diagnostic push
19027#pragma clang diagnostic ignored "-Wformat-nonliteral"
19028#endif
19029
19030 /* Establish the client connection and request upgrade */
19031 conn = mg_connect_client_impl(client_options, use_ssl, &init, &error);
19032
19033 /* Connection object will be null if something goes wrong */
19034 if (conn == NULL) {
19035 /* error_buffer should be already filled ... */
19036 if (!error_buffer[0]) {
19037 /* ... if not add an error message */
19038 mg_snprintf(conn,
19039 NULL, /* No truncation check for ebuf */
19040 error_buffer,
19041 error_buffer_size,
19042 "Unexpected error");
19043 }
19044 return NULL;
19045 }
19046
19047 if (origin != NULL) {
19048 if (extensions != NULL) {
19049 i = mg_printf(conn,
19050 "GET %s HTTP/1.1\r\n"
19051 "Host: %s\r\n"
19052 "Upgrade: websocket\r\n"
19053 "Connection: Upgrade\r\n"
19054 "Sec-WebSocket-Key: %s\r\n"
19055 "Sec-WebSocket-Version: 13\r\n"
19056 "Sec-WebSocket-Extensions: %s\r\n"
19057 "Origin: %s\r\n"
19058 "\r\n",
19059 path,
19060 host,
19061 magic,
19062 extensions,
19063 origin);
19064 } else {
19065 i = mg_printf(conn,
19066 "GET %s HTTP/1.1\r\n"
19067 "Host: %s\r\n"
19068 "Upgrade: websocket\r\n"
19069 "Connection: Upgrade\r\n"
19070 "Sec-WebSocket-Key: %s\r\n"
19071 "Sec-WebSocket-Version: 13\r\n"
19072 "Origin: %s\r\n"
19073 "\r\n",
19074 path,
19075 host,
19076 magic,
19077 origin);
19078 }
19079 } else {
19080
19081 if (extensions != NULL) {
19082 i = mg_printf(conn,
19083 "GET %s HTTP/1.1\r\n"
19084 "Host: %s\r\n"
19085 "Upgrade: websocket\r\n"
19086 "Connection: Upgrade\r\n"
19087 "Sec-WebSocket-Key: %s\r\n"
19088 "Sec-WebSocket-Version: 13\r\n"
19089 "Sec-WebSocket-Extensions: %s\r\n"
19090 "\r\n",
19091 path,
19092 host,
19093 magic,
19094 extensions);
19095 } else {
19096 i = mg_printf(conn,
19097 "GET %s HTTP/1.1\r\n"
19098 "Host: %s\r\n"
19099 "Upgrade: websocket\r\n"
19100 "Connection: Upgrade\r\n"
19101 "Sec-WebSocket-Key: %s\r\n"
19102 "Sec-WebSocket-Version: 13\r\n"
19103 "\r\n",
19104 path,
19105 host,
19106 magic);
19107 }
19108 }
19109 if (i <= 0) {
19110 mg_snprintf(conn,
19111 NULL, /* No truncation check for ebuf */
19112 error_buffer,
19113 error_buffer_size,
19114 "%s",
19115 "Error sending request");
19116 mg_close_connection(conn);
19117 return NULL;
19118 }
19119
19120 conn->data_len = 0;
19121 if (!get_response(conn, error_buffer, error_buffer_size, &i)) {
19122 mg_close_connection(conn);
19123 return NULL;
19124 }
19127
19128#if defined(__clang__)
19129#pragma clang diagnostic pop
19130#endif
19131
19132 if (conn->response_info.status_code != 101) {
19133 /* We sent an "upgrade" request. For a correct websocket
19134 * protocol handshake, we expect a "101 Continue" response.
19135 * Otherwise it is a protocol violation. Maybe the HTTP
19136 * Server does not know websockets. */
19137 if (!*error_buffer) {
19138 /* set an error, if not yet set */
19139 mg_snprintf(conn,
19140 NULL, /* No truncation check for ebuf */
19141 error_buffer,
19142 error_buffer_size,
19143 "Unexpected server reply");
19144 }
19145
19146 DEBUG_TRACE("Websocket client connect error: %s\r\n", error_buffer);
19147 mg_close_connection(conn);
19148 return NULL;
19149 }
19150
19151 thread_data = (struct websocket_client_thread_data *)mg_calloc_ctx(
19152 1, sizeof(struct websocket_client_thread_data), conn->phys_ctx);
19153 if (!thread_data) {
19154 DEBUG_TRACE("%s\r\n", "Out of memory");
19156 return NULL;
19157 }
19158
19159 thread_data->conn = conn;
19160 thread_data->data_handler = data_func;
19161 thread_data->close_handler = close_func;
19162 thread_data->callback_data = user_data;
19163
19165 (pthread_t *)mg_calloc_ctx(1, sizeof(pthread_t), conn->phys_ctx);
19167 DEBUG_TRACE("%s\r\n", "Out of memory");
19168 mg_free(thread_data);
19170 return NULL;
19171 }
19172
19173 /* Now upgrade to ws/wss client context */
19174 conn->phys_ctx->user_data = user_data;
19176 conn->phys_ctx->cfg_worker_threads = 1; /* one worker thread */
19177
19178 /* Start a thread to read the websocket client connection
19179 * This thread will automatically stop when mg_disconnect is
19180 * called on the client connection */
19181 if (mg_start_thread_with_id(websocket_client_thread,
19182 thread_data,
19184 != 0) {
19186 mg_free(thread_data);
19188 conn = NULL;
19189 DEBUG_TRACE("%s",
19190 "Websocket client connect thread could not be started\r\n");
19191 }
19192
19193#else
19194 /* Appease "unused parameter" warnings */
19195 (void)client_options;
19196 (void)use_ssl;
19197 (void)error_buffer;
19198 (void)error_buffer_size;
19199 (void)path;
19200 (void)origin;
19201 (void)extensions;
19202 (void)user_data;
19203 (void)data_func;
19204 (void)close_func;
19205#endif
19206
19207 return conn;
19208}
#define mg_calloc_ctx(a, b, c)
Definition civetweb.c:1498
static int get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
Definition civetweb.c:18737
static int mg_start_thread_with_id(mg_thread_func_t func, void *param, pthread_t *threadidptr)
Definition civetweb.c:5726
CIVETWEB_API void mg_close_connection(struct mg_connection *conn)
Definition civetweb.c:17938
mg_websocket_close_handler close_handler
Definition civetweb.c:18929
mg_websocket_data_handler data_handler
Definition civetweb.c:18928
struct mg_connection * conn
Definition civetweb.c:18927

References websocket_client_thread_data::callback_data, mg_context::cfg_worker_threads, close_func(), websocket_client_thread_data::close_handler, websocket_client_thread_data::conn, mg_context::context_type, CONTEXT_WS_CLIENT, websocket_client_thread_data::data_handler, mg_connection::data_len, DEBUG_TRACE, error(), get_response(), mg_client_options::host, mg_request_info::local_uri, mg_request_info::local_uri_raw, mg_calloc_ctx, mg_close_connection(), mg_connect_client_impl(), mg_free(), mg_printf(), mg_snprintf(), mg_start_thread_with_id(), NULL, mg_connection::phys_ctx, mg_connection::request_info, mg_request_info::request_uri, mg_connection::response_info, mg_response_info::status_code, mg_context::user_data, and mg_context::worker_threadids.

Referenced by mg_connect_websocket_client(), mg_connect_websocket_client_extensions(), mg_connect_websocket_client_secure(), and mg_connect_websocket_client_secure_extensions().

◆ mg_connect_websocket_client_secure()

CIVETWEB_API struct mg_connection * mg_connect_websocket_client_secure ( const struct mg_client_options * client_options,
char * error_buffer,
size_t error_buffer_size,
const char * path,
const char * origin,
mg_websocket_data_handler data_func,
mg_websocket_close_handler close_func,
void * user_data )

Definition at line 19242 of file civetweb.c.

19251{
19252 if (!client_options) {
19253 return NULL;
19254 }
19255 return mg_connect_websocket_client_impl(client_options,
19256 1,
19257 error_buffer,
19258 error_buffer_size,
19259 path,
19260 origin,
19261 NULL,
19262 data_func,
19263 close_func,
19264 user_data);
19265}

References close_func(), mg_connect_websocket_client_impl(), and NULL.

◆ mg_connect_websocket_client_secure_extensions()

CIVETWEB_API struct mg_connection * mg_connect_websocket_client_secure_extensions ( const struct mg_client_options * client_options,
char * error_buffer,
size_t error_buffer_size,
const char * path,
const char * origin,
const char * extensions,
mg_websocket_data_handler data_func,
mg_websocket_close_handler close_func,
void * user_data )

Definition at line 19300 of file civetweb.c.

19310{
19311 if (!client_options) {
19312 return NULL;
19313 }
19314 return mg_connect_websocket_client_impl(client_options,
19315 1,
19316 error_buffer,
19317 error_buffer_size,
19318 path,
19319 origin,
19320 extensions,
19321 data_func,
19322 close_func,
19323 user_data);
19324}

References close_func(), mg_connect_websocket_client_impl(), and NULL.

◆ mg_construct_local_link()

static int mg_construct_local_link ( const struct mg_connection * conn,
char * buf,
size_t buflen,
const char * define_proto,
int define_port,
const char * define_uri )
static

Definition at line 3601 of file civetweb.c.

3607{
3608 if ((buflen < 1) || (buf == 0) || (conn == 0)) {
3609 return -1;
3610 } else {
3611 int i, j;
3612 int truncated = 0;
3613 const struct mg_request_info *ri = &conn->request_info;
3614
3615 const char *proto =
3616 (define_proto != NULL) ? define_proto : get_proto_name(conn);
3617 const char *uri =
3618 (define_uri != NULL)
3619 ? define_uri
3620 : ((ri->request_uri != NULL) ? ri->request_uri : ri->local_uri);
3621 int port = (define_port > 0) ? define_port : ri->server_port;
3622 int default_port = 80;
3623 char *uri_encoded;
3624 size_t uri_encoded_len;
3625
3626 if (uri == NULL) {
3627 return -1;
3628 }
3629
3630 uri_encoded_len = strlen(uri) * 3 + 1;
3631 uri_encoded = (char *)mg_malloc_ctx(uri_encoded_len, conn->phys_ctx);
3632 if (uri_encoded == NULL) {
3633 return -1;
3634 }
3635 mg_url_encode(uri, uri_encoded, uri_encoded_len);
3636
3637 /* Directory separator should be preserved. */
3638 for (i = j = 0; uri_encoded[i]; j++) {
3639 if (!strncmp(uri_encoded + i, "%2f", 3)) {
3640 uri_encoded[j] = '/';
3641 i += 3;
3642 } else {
3643 uri_encoded[j] = uri_encoded[i++];
3644 }
3645 }
3646 uri_encoded[j] = '\0';
3647
3648#if defined(USE_X_DOM_SOCKET)
3649 if (conn->client.lsa.sa.sa_family == AF_UNIX) {
3650 /* TODO: Define and document a link for UNIX domain sockets. */
3651 /* There seems to be no official standard for this.
3652 * Common uses seem to be "httpunix://", "http.unix://" or
3653 * "http+unix://" as a protocol definition string, followed by
3654 * "localhost" or "127.0.0.1" or "/tmp/unix/path" or
3655 * "%2Ftmp%2Funix%2Fpath" (url % encoded) or
3656 * "localhost:%2Ftmp%2Funix%2Fpath" (domain socket path as port) or
3657 * "" (completely skipping the server name part). In any case, the
3658 * last part is the server local path. */
3659 const char *server_name = UNIX_DOMAIN_SOCKET_SERVER_NAME;
3660 mg_snprintf(conn,
3661 &truncated,
3662 buf,
3663 buflen,
3664 "%s.unix://%s%s",
3665 proto,
3666 server_name,
3667 ri->local_uri);
3668 default_port = 0;
3669 mg_free(uri_encoded);
3670 return 0;
3671 }
3672#endif
3673
3674 if (define_proto) {
3675 /* If we got a protocol name, use the default port accordingly. */
3676 if ((0 == strcmp(define_proto, "https"))
3677 || (0 == strcmp(define_proto, "wss"))) {
3678 default_port = 443;
3679 }
3680 } else if (ri->is_ssl) {
3681 /* If we did not get a protocol name, use TLS as default if it is
3682 * already used. */
3683 default_port = 443;
3684 }
3685
3686 {
3687#if defined(USE_IPV6)
3688 int is_ipv6 = (conn->client.lsa.sa.sa_family == AF_INET6);
3689#endif
3690 int auth_domain_check_enabled =
3692 && (!mg_strcasecmp(
3693 conn->dom_ctx->config[ENABLE_AUTH_DOMAIN_CHECK], "yes"));
3694
3695 const char *server_domain =
3697
3698 char portstr[16];
3699 char server_ip[48];
3700
3701 if (port != default_port) {
3702 sprintf(portstr, ":%u", (unsigned)port);
3703 } else {
3704 portstr[0] = 0;
3705 }
3706
3707 if (!auth_domain_check_enabled || !server_domain) {
3708
3709 sockaddr_to_string(server_ip,
3710 sizeof(server_ip),
3711 &conn->client.lsa);
3712
3713 server_domain = server_ip;
3714 }
3715
3716 mg_snprintf(conn,
3717 &truncated,
3718 buf,
3719 buflen,
3720#if defined(USE_IPV6)
3721 "%s://%s%s%s%s%s",
3722 proto,
3723 (is_ipv6 && (server_domain == server_ip)) ? "[" : "",
3724 server_domain,
3725 (is_ipv6 && (server_domain == server_ip)) ? "]" : "",
3726#else
3727 "%s://%s%s%s",
3728 proto,
3729 server_domain,
3730#endif
3731 portstr,
3732 uri_encoded);
3733
3734 mg_free(uri_encoded);
3735 if (truncated) {
3736 return -1;
3737 }
3738 return 0;
3739 }
3740 }
3741}
unsigned default_port
Definition civetweb.c:18380
static const char * get_proto_name(const struct mg_connection *conn)
Definition civetweb.c:3574
CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len)
Definition civetweb.c:9581

References AUTHENTICATION_DOMAIN, mg_connection::client, mg_domain_context::config, default_port, mg_connection::dom_ctx, ENABLE_AUTH_DOMAIN_CHECK, get_proto_name(), mg_request_info::is_ssl, mg_request_info::local_uri, socket::lsa, mg_free(), mg_malloc_ctx, mg_snprintf(), mg_strcasecmp(), mg_url_encode(), NULL, mg_connection::phys_ctx, proto, mg_connection::request_info, mg_request_info::request_uri, usa::sa, mg_request_info::server_port, and sockaddr_to_string().

Referenced by mg_get_request_link(), print_props(), and redirect_to_https_port().

◆ mg_cry()

CIVETWEB_API void mg_cry ( const struct mg_connection * conn,
const char * fmt,
... )

Definition at line 3501 of file civetweb.c.

3502{
3503 va_list ap;
3504 va_start(ap, fmt);
3505 mg_cry_internal_impl(conn, "user", 0, fmt, ap);
3506 va_end(ap);
3507}
static void mg_cry_internal_impl(const struct mg_connection *conn, const char *func, unsigned line, const char *fmt, va_list ap)
Definition civetweb.c:3382

References mg_cry_internal_impl().

◆ mg_cry_internal_impl()

static void mg_cry_internal_impl ( const struct mg_connection * conn,
const char * func,
unsigned line,
const char * fmt,
va_list ap )
static

Definition at line 3382 of file civetweb.c.

3387{
3388 char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
3389 struct mg_file fi;
3390 time_t timestamp;
3391
3392 /* Unused, in the RELEASE build */
3393 (void)func;
3394 (void)line;
3395
3396#if defined(GCC_DIAGNOSTIC)
3397#pragma GCC diagnostic push
3398#pragma GCC diagnostic ignored "-Wformat-nonliteral"
3399#endif
3400
3401 IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap));
3402
3403#if defined(GCC_DIAGNOSTIC)
3404#pragma GCC diagnostic pop
3405#endif
3406
3407 buf[sizeof(buf) - 1] = 0;
3408
3409 DEBUG_TRACE("mg_cry called from %s:%u: %s", func, line, buf);
3410
3411 if (!conn) {
3412 puts(buf);
3413 return;
3414 }
3415
3416 /* Do not lock when getting the callback value, here and below.
3417 * I suppose this is fine, since function cannot disappear in the
3418 * same way string option can. */
3419 if ((conn->phys_ctx->callbacks.log_message == NULL)
3420 || (conn->phys_ctx->callbacks.log_message(conn, buf) == 0)) {
3421
3422 if (conn->dom_ctx->config[ERROR_LOG_FILE] != NULL) {
3423 if (mg_fopen(conn,
3426 &fi)
3427 == 0) {
3428 fi.access.fp = NULL;
3429 }
3430 } else {
3431 fi.access.fp = NULL;
3432 }
3433
3434 if (fi.access.fp != NULL) {
3435 flockfile(fi.access.fp);
3436 timestamp = time(NULL);
3437
3438 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
3439 fprintf(fi.access.fp,
3440 "[%010lu] [error] [client %s] ",
3441 (unsigned long)timestamp,
3442 src_addr);
3443
3444 if (conn->request_info.request_method != NULL) {
3445 fprintf(fi.access.fp,
3446 "%s %s: ",
3450 : "");
3451 }
3452
3453 fprintf(fi.access.fp, "%s", buf);
3454 fputc('\n', fi.access.fp);
3455 fflush(fi.access.fp);
3456 funlockfile(fi.access.fp);
3457 (void)mg_fclose(&fi.access); /* Ignore errors. We can't call
3458 * mg_cry here anyway ;-) */
3459 }
3460 }
3461}
int(* log_message)(const struct mg_connection *, const char *message)
Definition civetweb.h:240

References mg_file::access, mg_context::callbacks, mg_connection::client, mg_domain_context::config, DEBUG_TRACE, mg_connection::dom_ctx, ERROR_LOG_FILE, mg_file_access::fp, IGNORE_UNUSED_RESULT, IP_ADDR_STR_LEN, mg_callbacks::log_message, MG_BUF_LEN, mg_fclose(), mg_fopen(), MG_FOPEN_MODE_APPEND, NULL, mg_connection::phys_ctx, mg_connection::request_info, mg_request_info::request_method, mg_request_info::request_uri, socket::rsa, sockaddr_to_string(), and vsnprintf_impl.

Referenced by mg_cry(), and mg_cry_internal_wrap().

◆ mg_cry_internal_wrap()

static void mg_cry_internal_wrap ( const struct mg_connection * conn,
struct mg_context * ctx,
const char * func,
unsigned line,
const char * fmt,
... )
static

Definition at line 3481 of file civetweb.c.

3487{
3488 va_list ap;
3489 va_start(ap, fmt);
3490 if (!conn && ctx) {
3491 struct mg_connection fc;
3492 mg_cry_internal_impl(fake_connection(&fc, ctx), func, line, fmt, ap);
3493 } else {
3494 mg_cry_internal_impl(conn, func, line, fmt, ap);
3495 }
3496 va_end(ap);
3497}

References fake_connection(), and mg_cry_internal_impl().

◆ mg_current_thread_id()

static FUNCTION_MAY_BE_UNUSED unsigned long mg_current_thread_id ( void )
static

Definition at line 1626 of file civetweb.c.

1627{
1628#if defined(_WIN32)
1629 return GetCurrentThreadId();
1630#else
1631
1632#if defined(__clang__)
1633#pragma clang diagnostic push
1634#pragma clang diagnostic ignored "-Wunreachable-code"
1635 /* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)"
1636 * or not, so one of the two conditions will be unreachable by construction.
1637 * Unfortunately the C standard does not define a way to check this at
1638 * compile time, since the #if preprocessor conditions can not use the
1639 * sizeof operator as an argument. */
1640#endif
1641
1642 if (sizeof(pthread_t) > sizeof(unsigned long)) {
1643 /* This is the problematic case for CRYPTO_set_id_callback:
1644 * The OS pthread_t can not be cast to unsigned long. */
1645 struct mg_workerTLS *tls =
1646 (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
1647 if (tls == NULL) {
1648 /* SSL called from an unknown thread: Create some thread index.
1649 */
1650 tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS));
1651 tls->is_master = -2; /* -2 means "3rd party thread" */
1652 tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
1653 pthread_setspecific(sTlsKey, tls);
1654 }
1655 return tls->thread_idx;
1656 } else {
1657 /* pthread_t may be any data type, so a simple cast to unsigned long
1658 * can rise a warning/error, depending on the platform.
1659 * Here memcpy is used as an anything-to-anything cast. */
1660 unsigned long ret = 0;
1661 pthread_t t = pthread_self();
1662 memcpy(&ret, &t, sizeof(pthread_t));
1663 return ret;
1664 }
1665
1666#if defined(__clang__)
1667#pragma clang diagnostic pop
1668#endif
1669
1670#endif
1671}
static volatile ptrdiff_t thread_idx_max
Definition civetweb.c:1581
unsigned long thread_idx
Definition civetweb.c:1589

References mg_workerTLS::is_master, mg_atomic_inc(), mg_malloc(), NULL, sTlsKey, mg_workerTLS::thread_idx, and thread_idx_max.

Referenced by initialize_openssl().

◆ mg_difftimespec()

static double mg_difftimespec ( const struct timespec * ts_now,
const struct timespec * ts_before )
static

Definition at line 3364 of file civetweb.c.

3365{
3366 return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9
3367 + (double)(ts_now->tv_sec - ts_before->tv_sec);
3368}

Referenced by handle_request_stat_log(), and read_message().

◆ mg_disable_connection_keep_alive()

CIVETWEB_API void mg_disable_connection_keep_alive ( struct mg_connection * conn)

Definition at line 22042 of file civetweb.c.

22043{
22044 /* https://github.com/civetweb/civetweb/issues/727 */
22045 if (conn != NULL) {
22046 conn->must_close = 1;
22047 }
22048}

References mg_connection::must_close, and NULL.

◆ mg_download()

CIVETWEB_API 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 18874 of file civetweb.c.

18881{
18882 struct mg_connection *conn;
18883 va_list ap;
18884 int i;
18885 int reqerr;
18886
18887 if (ebuf_len > 0) {
18888 ebuf[0] = '\0';
18889 }
18890
18891 va_start(ap, fmt);
18892
18893 /* open a connection */
18894 conn = mg_connect_client(host, port, use_ssl, ebuf, ebuf_len);
18895
18896 if (conn != NULL) {
18897 i = mg_vprintf(conn, fmt, ap);
18898 if (i <= 0) {
18899 mg_snprintf(conn,
18900 NULL, /* No truncation check for ebuf */
18901 ebuf,
18902 ebuf_len,
18903 "%s",
18904 "Error sending request");
18905 } else {
18906 /* make sure the buffer is clear */
18907 conn->data_len = 0;
18908 get_response(conn, ebuf, ebuf_len, &reqerr);
18909
18910 /* TODO: here, the URI is the http response code */
18912 }
18913 }
18914
18915 /* if an error occurred, close the connection */
18916 if ((ebuf[0] != '\0') && (conn != NULL)) {
18917 mg_close_connection(conn);
18918 conn = NULL;
18919 }
18920
18921 va_end(ap);
18922 return conn;
18923}
static int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap)
Definition civetweb.c:7016
CIVETWEB_API struct mg_connection * mg_connect_client(const char *host, int port, int use_ssl, char *error_buffer, size_t error_buffer_size)
Definition civetweb.c:18253

References mg_connection::data_len, get_response(), mg_request_info::local_uri, mg_close_connection(), mg_connect_client(), mg_snprintf(), mg_vprintf(), NULL, mg_connection::request_info, and mg_request_info::request_uri.

◆ mg_exit_library()

CIVETWEB_API unsigned mg_exit_library ( void )

Definition at line 22477 of file civetweb.c.

22478{
22479 if (mg_init_library_called <= 0) {
22480 return 0;
22481 }
22482
22484
22486 if (mg_init_library_called == 0) {
22487#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1)) && !defined(NO_SSL)
22488 if (mg_openssl_initialized) {
22490 mg_openssl_initialized = 0;
22491 }
22492#endif
22493
22494#if defined(_WIN32)
22495 (void)WSACleanup();
22496 (void)pthread_mutex_destroy(&global_log_file_lock);
22497#else
22498 (void)pthread_mutexattr_destroy(&pthread_mutex_attr);
22499#endif
22500
22501 (void)pthread_key_delete(sTlsKey);
22502
22503#if defined(USE_LUA)
22504 lua_exit_optional_libraries();
22505#endif
22507 all_methods = NULL;
22508
22510 (void)pthread_mutex_destroy(&global_lock_mutex);
22511 return 1;
22512 }
22513
22515 return 1;
22516}
static pthread_mutex_t global_lock_mutex
Definition civetweb.c:1089
static int mg_init_library_called
Definition civetweb.c:1552
static char * all_methods
Definition civetweb.c:10876
static void uninitialize_openssl(void)
Definition civetweb.c:17594

References all_methods, global_lock_mutex, mg_free(), mg_global_lock(), mg_global_unlock(), mg_init_library_called, NULL, pthread_mutex_attr, sTlsKey, and uninitialize_openssl().

Referenced by run_client().

◆ mg_fclose()

static int mg_fclose ( struct mg_file_access * fileacc)
static

Definition at line 2986 of file civetweb.c.

2987{
2988 int ret = -1;
2989 if (fileacc != NULL) {
2990 if (fileacc->fp != NULL) {
2991 ret = fclose(fileacc->fp);
2992 }
2993 /* reset all members of fileacc */
2994 memset(fileacc, 0, sizeof(*fileacc));
2995 }
2996 return ret;
2997}

References mg_file_access::fp, and NULL.

Referenced by check_authorization(), do_ssi_include(), handle_ssi_file_request(), handle_static_file_request(), is_authorized_for_put(), log_access(), mg_check_digest_access_authentication(), mg_cry_internal_impl(), mg_send_file_body(), mg_store_body(), put_file(), and read_auth_file().

◆ mg_fgetc()

static int mg_fgetc ( struct mg_file * filep)
static

Definition at line 12467 of file civetweb.c.

12468{
12469 if (filep == NULL) {
12470 return EOF;
12471 }
12472
12473 if (filep->access.fp != NULL) {
12474 return fgetc(filep->access.fp);
12475 } else {
12476 return EOF;
12477 }
12478}

References mg_file::access, mg_file_access::fp, and NULL.

Referenced by send_ssi_file().

◆ mg_fgets()

static const char * mg_fgets ( char * buf,
size_t size,
struct mg_file * filep )
static

Definition at line 8696 of file civetweb.c.

8697{
8698 if (!filep) {
8699 return NULL;
8700 }
8701
8702 if (filep->access.fp != NULL) {
8703 return fgets(buf, (int)size, filep->access.fp);
8704 } else {
8705 return NULL;
8706 }
8707}

References mg_file::access, mg_file_access::fp, and NULL.

Referenced by read_auth_file().

◆ mg_fopen()

static int mg_fopen ( const struct mg_connection * conn,
const char * path,
int mode,
struct mg_file * filep )
static

Definition at line 2915 of file civetweb.c.

2919{
2920 int found;
2921
2922 if (!filep) {
2923 return 0;
2924 }
2925 filep->access.fp = NULL;
2926
2927 if (mg_path_suspicious(conn, path)) {
2928 return 0;
2929 }
2930
2931 /* filep is initialized in mg_stat: all fields with memset to,
2932 * some fields like size and modification date with values */
2933 found = mg_stat(conn, path, &(filep->stat));
2934
2935 if ((mode == MG_FOPEN_MODE_READ) && (!found)) {
2936 /* file does not exist and will not be created */
2937 return 0;
2938 }
2939
2940#if defined(_WIN32)
2941 {
2942 wchar_t wbuf[UTF16_PATH_MAX];
2943 path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
2944 switch (mode) {
2945 case MG_FOPEN_MODE_READ:
2946 filep->access.fp = _wfopen(wbuf, L"rb");
2947 break;
2949 filep->access.fp = _wfopen(wbuf, L"wb");
2950 break;
2952 filep->access.fp = _wfopen(wbuf, L"ab");
2953 break;
2954 }
2955 }
2956#else
2957 /* Linux et al already use unicode. No need to convert. */
2958 switch (mode) {
2959 case MG_FOPEN_MODE_READ:
2960 filep->access.fp = fopen(path, "r");
2961 break;
2963 filep->access.fp = fopen(path, "w");
2964 break;
2966 filep->access.fp = fopen(path, "a");
2967 break;
2968 }
2969
2970#endif
2971 if (!found) {
2972 /* File did not exist before fopen was called.
2973 * Maybe it has been created now. Get stat info
2974 * like creation time now. */
2975 found = mg_stat(conn, path, &(filep->stat));
2976 (void)found;
2977 }
2978
2979 /* return OK if file is opened */
2980 return (filep->access.fp != NULL);
2981}
static int mg_path_suspicious(const struct mg_connection *conn, const char *path)
Definition civetweb.c:2871
#define MG_FOPEN_MODE_WRITE
Definition civetweb.c:2846

References mg_file::access, ARRAY_SIZE, mg_file_access::fp, MG_FOPEN_MODE_APPEND, MG_FOPEN_MODE_READ, MG_FOPEN_MODE_WRITE, mg_path_suspicious(), mg_stat(), NULL, and mg_file::stat.

Referenced by check_authorization(), do_ssi_include(), handle_ssi_file_request(), handle_static_file_request(), is_authorized_for_put(), log_access(), mg_check_digest_access_authentication(), mg_cry_internal_impl(), mg_send_file_body(), mg_store_body(), open_auth_file(), put_file(), and read_auth_file().

◆ mg_free()

◆ mg_get_builtin_mime_type()

CIVETWEB_API const char * mg_get_builtin_mime_type ( const char * path)

Definition at line 8332 of file civetweb.c.

8333{
8334 const char *ext;
8335 size_t i, path_len;
8336
8337 path_len = strlen(path);
8338
8339 for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
8340 ext = path + (path_len - builtin_mime_types[i].ext_len);
8341 if ((path_len > builtin_mime_types[i].ext_len)
8342 && (mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0)) {
8343 return builtin_mime_types[i].mime_type;
8344 }
8345 }
8346
8347 return "text/plain";
8348}
size_t ext_len
Definition civetweb.c:8223
const char * extension
Definition civetweb.c:8222
static const struct @5 builtin_mime_types[]

References builtin_mime_types, ext_len, extension, mg_strcasecmp(), and NULL.

Referenced by get_mime_type().

◆ mg_get_context()

CIVETWEB_API struct mg_context * mg_get_context ( const struct mg_connection * conn)

Definition at line 3188 of file civetweb.c.

3189{
3190 return (conn == NULL) ? (struct mg_context *)NULL : (conn->phys_ctx);
3191}

References NULL, and mg_connection::phys_ctx.

Referenced by mg_get_user_context_data().

◆ mg_get_context_info()

CIVETWEB_API int mg_get_context_info ( const struct mg_context * ctx,
char * buffer,
int buflen )

Definition at line 21843 of file civetweb.c.

21844{
21845#if defined(USE_SERVER_STATS)
21846 char *end, *append_eoobj = NULL, block[256];
21847 size_t context_info_length = 0;
21848
21849#if defined(_WIN32)
21850 static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
21851#else
21852 static const char eol[] = "\n", eoobj[] = "\n}\n";
21853#endif
21854 struct mg_memory_stat *ms = get_memory_stat((struct mg_context *)ctx);
21855
21856 if ((buffer == NULL) || (buflen < 1)) {
21857 buflen = 0;
21858 end = buffer;
21859 } else {
21860 *buffer = 0;
21861 end = buffer + buflen;
21862 }
21863 if (buflen > (int)(sizeof(eoobj) - 1)) {
21864 /* has enough space to append eoobj */
21865 append_eoobj = buffer;
21866 end -= sizeof(eoobj) - 1;
21867 }
21868
21869 context_info_length += mg_str_append(&buffer, end, "{");
21870
21871 if (ms) { /* <-- should be always true */
21872 /* Memory information */
21873 int blockCount = (int)ms->blockCount;
21874 int64_t totalMemUsed = ms->totalMemUsed;
21875 int64_t maxMemUsed = ms->maxMemUsed;
21876 if (totalMemUsed > maxMemUsed) {
21877 maxMemUsed = totalMemUsed;
21878 }
21879
21881 NULL,
21882 block,
21883 sizeof(block),
21884 "%s\"memory\" : {%s"
21885 "\"blocks\" : %i,%s"
21886 "\"used\" : %" INT64_FMT ",%s"
21887 "\"maxUsed\" : %" INT64_FMT "%s"
21888 "}",
21889 eol,
21890 eol,
21891 blockCount,
21892 eol,
21893 totalMemUsed,
21894 eol,
21895 maxMemUsed,
21896 eol);
21897 context_info_length += mg_str_append(&buffer, end, block);
21898 }
21899
21900 if (ctx) {
21901 /* Declare all variables at begin of the block, to comply
21902 * with old C standards. */
21903 char start_time_str[64] = {0};
21904 char now_str[64] = {0};
21905 time_t start_time = ctx->start_time;
21906 time_t now = time(NULL);
21907 int64_t total_data_read, total_data_written;
21908 int active_connections = (int)ctx->active_connections;
21909 int max_active_connections = (int)ctx->max_active_connections;
21910 int total_connections = (int)ctx->total_connections;
21911 if (active_connections > max_active_connections) {
21912 max_active_connections = active_connections;
21913 }
21914 if (active_connections > total_connections) {
21915 total_connections = active_connections;
21916 }
21917
21918 /* Connections information */
21920 NULL,
21921 block,
21922 sizeof(block),
21923 ",%s\"connections\" : {%s"
21924 "\"active\" : %i,%s"
21925 "\"maxActive\" : %i,%s"
21926 "\"total\" : %i%s"
21927 "}",
21928 eol,
21929 eol,
21930 active_connections,
21931 eol,
21932 max_active_connections,
21933 eol,
21934 total_connections,
21935 eol);
21936 context_info_length += mg_str_append(&buffer, end, block);
21937
21938 /* Queue information */
21939#if !defined(ALTERNATIVE_QUEUE)
21941 NULL,
21942 block,
21943 sizeof(block),
21944 ",%s\"queue\" : {%s"
21945 "\"length\" : %i,%s"
21946 "\"filled\" : %i,%s"
21947 "\"maxFilled\" : %i,%s"
21948 "\"full\" : %s%s"
21949 "}",
21950 eol,
21951 eol,
21952 ctx->sq_size,
21953 eol,
21954 ctx->sq_head - ctx->sq_tail,
21955 eol,
21956 ctx->sq_max_fill,
21957 eol,
21958 (ctx->sq_blocked ? "true" : "false"),
21959 eol);
21960 context_info_length += mg_str_append(&buffer, end, block);
21961#endif
21962
21963 /* Requests information */
21965 NULL,
21966 block,
21967 sizeof(block),
21968 ",%s\"requests\" : {%s"
21969 "\"total\" : %lu%s"
21970 "}",
21971 eol,
21972 eol,
21973 (unsigned long)ctx->total_requests,
21974 eol);
21975 context_info_length += mg_str_append(&buffer, end, block);
21976
21977 /* Data information */
21978 total_data_read =
21979 mg_atomic_add64((volatile int64_t *)&ctx->total_data_read, 0);
21980 total_data_written =
21981 mg_atomic_add64((volatile int64_t *)&ctx->total_data_written, 0);
21983 NULL,
21984 block,
21985 sizeof(block),
21986 ",%s\"data\" : {%s"
21987 "\"read\" : %" INT64_FMT ",%s"
21988 "\"written\" : %" INT64_FMT "%s"
21989 "}",
21990 eol,
21991 eol,
21992 total_data_read,
21993 eol,
21994 total_data_written,
21995 eol);
21996 context_info_length += mg_str_append(&buffer, end, block);
21997
21998 /* Execution time information */
21999 gmt_time_string(start_time_str,
22000 sizeof(start_time_str) - 1,
22001 &start_time);
22002 gmt_time_string(now_str, sizeof(now_str) - 1, &now);
22003
22005 NULL,
22006 block,
22007 sizeof(block),
22008 ",%s\"time\" : {%s"
22009 "\"uptime\" : %.0f,%s"
22010 "\"start\" : \"%s\",%s"
22011 "\"now\" : \"%s\"%s"
22012 "}",
22013 eol,
22014 eol,
22015 difftime(now, start_time),
22016 eol,
22017 start_time_str,
22018 eol,
22019 now_str,
22020 eol);
22021 context_info_length += mg_str_append(&buffer, end, block);
22022 }
22023
22024 /* Terminate string */
22025 if (append_eoobj) {
22026 strcat(append_eoobj, eoobj);
22027 }
22028 context_info_length += sizeof(eoobj) - 1;
22029
22030 return (int)context_info_length;
22031#else
22032 (void)ctx;
22033 if ((buffer != NULL) && (buflen > 0)) {
22034 *buffer = 0;
22035 }
22036 return 0;
22037#endif
22038}
static size_t mg_str_append(char **dst, char *end, const char *src)
Definition civetweb.c:21493
static void block(LexState *ls)
volatile int sq_blocked
Definition civetweb.c:2393

References block(), gmt_time_string(), INT64_FMT, mg_snprintf(), mg_str_append(), NULL, mg_context::sq_blocked, mg_context::sq_head, mg_context::sq_size, mg_context::sq_tail, and mg_context::start_time.

◆ mg_get_cookie()

CIVETWEB_API int mg_get_cookie ( const char * cookie_header,
const char * var_name,
char * dst,
size_t dst_size )

Definition at line 7256 of file civetweb.c.

7260{
7261 const char *s, *p, *end;
7262 int name_len, len = -1;
7263
7264 if ((dst == NULL) || (dst_size == 0)) {
7265 return -2;
7266 }
7267
7268 dst[0] = '\0';
7269 if ((var_name == NULL) || ((s = cookie_header) == NULL)) {
7270 return -1;
7271 }
7272
7273 name_len = (int)strlen(var_name);
7274 end = s + strlen(s);
7275 for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
7276 if (s[name_len] == '=') {
7277 /* HCP24: now check is it a substring or a full cookie name */
7278 if ((s == cookie_header) || (s[-1] == ' ')) {
7279 s += name_len + 1;
7280 if ((p = strchr(s, ' ')) == NULL) {
7281 p = end;
7282 }
7283 if (p[-1] == ';') {
7284 p--;
7285 }
7286 if ((*s == '"') && (p[-1] == '"') && (p > s + 1)) {
7287 s++;
7288 p--;
7289 }
7290 if ((size_t)(p - s) < dst_size) {
7291 len = (int)(p - s);
7292 mg_strlcpy(dst, s, (size_t)len + 1);
7293 } else {
7294 len = -3;
7295 }
7296 break;
7297 }
7298 }
7299 }
7300 return len;
7301}
static const char * mg_strcasestr(const char *big_str, const char *small_str)
Definition civetweb.c:3075

References mg_strcasestr(), mg_strlcpy(), NULL, and s.

◆ mg_get_current_time_ns()

static FUNCTION_MAY_BE_UNUSED uint64_t mg_get_current_time_ns ( void )
static

Definition at line 1676 of file civetweb.c.

1677{
1678 struct timespec tsnow;
1679 clock_gettime(CLOCK_REALTIME, &tsnow);
1680 return (((uint64_t)tsnow.tv_sec) * 1000000000) + (uint64_t)tsnow.tv_nsec;
1681}

Referenced by dav_lock_file(), get_random(), pull_all(), and push_inner().

◆ mg_get_header()

◆ mg_get_option()

CIVETWEB_API const char * mg_get_option ( const struct mg_context * ctx,
const char * name )

Definition at line 3173 of file civetweb.c.

3174{
3175 int i;
3176 if ((i = get_option_index(name)) == -1) {
3177 return NULL;
3178 } else if (!ctx || ctx->dd.config[i] == NULL) {
3179 return "";
3180 } else {
3181 return ctx->dd.config[i];
3182 }
3183}
static int get_option_index(const char *name)
Definition civetweb.c:3159

References mg_domain_context::config, mg_context::dd, get_option_index(), name, and NULL.

◆ mg_get_request_info()

CIVETWEB_API const struct mg_request_info * mg_get_request_info ( const struct mg_connection * conn)

Definition at line 3521 of file civetweb.c.

3522{
3523 if (!conn) {
3524 return NULL;
3525 }
3526#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
3528 char txt[16];
3529 struct mg_workerTLS *tls =
3530 (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
3531
3532 sprintf(txt, "%03i", conn->response_info.status_code);
3533 if (strlen(txt) == 3) {
3534 memcpy(tls->txtbuf, txt, 4);
3535 } else {
3536 strcpy(tls->txtbuf, "ERR");
3537 }
3538
3539 ((struct mg_connection *)conn)->request_info.local_uri =
3540 tls->txtbuf; /* use thread safe buffer */
3541 ((struct mg_connection *)conn)->request_info.local_uri_raw =
3542 tls->txtbuf; /* use the same thread safe buffer */
3543 ((struct mg_connection *)conn)->request_info.request_uri =
3544 tls->txtbuf; /* use the same thread safe buffer */
3545
3546 ((struct mg_connection *)conn)->request_info.num_headers =
3548 memcpy(((struct mg_connection *)conn)->request_info.http_headers,
3550 sizeof(conn->response_info.http_headers));
3551 } else
3552#endif
3554 return NULL;
3555 }
3556 return &conn->request_info;
3557}

References mg_connection::connection_type, CONNECTION_TYPE_REQUEST, CONNECTION_TYPE_RESPONSE, mg_request_info::http_headers, mg_response_info::http_headers, NULL, mg_response_info::num_headers, mg_connection::request_info, mg_connection::response_info, mg_response_info::status_code, and sTlsKey.

Referenced by get_request_handler(), and user_handler().

◆ mg_get_request_link()

CIVETWEB_API int mg_get_request_link ( const struct mg_connection * conn,
char * buf,
size_t buflen )

Definition at line 3745 of file civetweb.c.

3746{
3747 return mg_construct_local_link(conn, buf, buflen, NULL, -1, NULL);
3748}
static int mg_construct_local_link(const struct mg_connection *conn, char *buf, size_t buflen, const char *define_proto, int define_port, const char *define_uri)
Definition civetweb.c:3601

References mg_construct_local_link(), and NULL.

Referenced by dav_lock_file(), dav_proppatch(), dav_unlock_file(), and handle_request().

◆ mg_get_response()

CIVETWEB_API int mg_get_response ( struct mg_connection * conn,
char * ebuf,
size_t ebuf_len,
int timeout )

Definition at line 18822 of file civetweb.c.

18826{
18827 int err, ret;
18828 char txt[32]; /* will not overflow */
18829 char *save_timeout;
18830 char *new_timeout;
18831
18832 if (ebuf_len > 0) {
18833 ebuf[0] = '\0';
18834 }
18835
18836 if (!conn) {
18837 mg_snprintf(conn,
18838 NULL, /* No truncation check for ebuf */
18839 ebuf,
18840 ebuf_len,
18841 "%s",
18842 "Parameter error");
18843 return -1;
18844 }
18845
18846 /* Reset the previous responses */
18847 conn->data_len = 0;
18848
18849 /* Implementation of API function for HTTP clients */
18850 save_timeout = conn->dom_ctx->config[REQUEST_TIMEOUT];
18851
18852 if (timeout >= 0) {
18853 mg_snprintf(conn, NULL, txt, sizeof(txt), "%i", timeout);
18854 new_timeout = txt;
18855 } else {
18856 new_timeout = NULL;
18857 }
18858
18859 conn->dom_ctx->config[REQUEST_TIMEOUT] = new_timeout;
18860 ret = get_response(conn, ebuf, ebuf_len, &err);
18861 conn->dom_ctx->config[REQUEST_TIMEOUT] = save_timeout;
18862
18863 /* TODO: here, the URI is the http response code */
18866
18867 /* TODO (mid): Define proper return values - maybe return length?
18868 * For the first test use <0 for error and >0 for OK */
18869 return (ret == 0) ? -1 : +1;
18870}

References mg_domain_context::config, mg_connection::data_len, mg_connection::dom_ctx, get_response(), mg_request_info::local_uri, mg_request_info::local_uri_raw, mg_snprintf(), NULL, mg_connection::request_info, REQUEST_TIMEOUT, and mg_request_info::request_uri.

Referenced by run_client().

◆ mg_get_response_code_text()

CIVETWEB_API const char * mg_get_response_code_text ( const struct mg_connection * conn,
int response_code )

Definition at line 4176 of file civetweb.c.

4177{
4178 /* See IANA HTTP status code assignment:
4179 * http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
4180 */
4181
4182 switch (response_code) {
4183 /* RFC2616 Section 10.1 - Informational 1xx */
4184 case 100:
4185 return "Continue"; /* RFC2616 Section 10.1.1 */
4186 case 101:
4187 return "Switching Protocols"; /* RFC2616 Section 10.1.2 */
4188 case 102:
4189 return "Processing"; /* RFC2518 Section 10.1 */
4190
4191 /* RFC2616 Section 10.2 - Successful 2xx */
4192 case 200:
4193 return "OK"; /* RFC2616 Section 10.2.1 */
4194 case 201:
4195 return "Created"; /* RFC2616 Section 10.2.2 */
4196 case 202:
4197 return "Accepted"; /* RFC2616 Section 10.2.3 */
4198 case 203:
4199 return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */
4200 case 204:
4201 return "No Content"; /* RFC2616 Section 10.2.5 */
4202 case 205:
4203 return "Reset Content"; /* RFC2616 Section 10.2.6 */
4204 case 206:
4205 return "Partial Content"; /* RFC2616 Section 10.2.7 */
4206 case 207:
4207 return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1
4208 */
4209 case 208:
4210 return "Already Reported"; /* RFC5842 Section 7.1 */
4211
4212 case 226:
4213 return "IM used"; /* RFC3229 Section 10.4.1 */
4214
4215 /* RFC2616 Section 10.3 - Redirection 3xx */
4216 case 300:
4217 return "Multiple Choices"; /* RFC2616 Section 10.3.1 */
4218 case 301:
4219 return "Moved Permanently"; /* RFC2616 Section 10.3.2 */
4220 case 302:
4221 return "Found"; /* RFC2616 Section 10.3.3 */
4222 case 303:
4223 return "See Other"; /* RFC2616 Section 10.3.4 */
4224 case 304:
4225 return "Not Modified"; /* RFC2616 Section 10.3.5 */
4226 case 305:
4227 return "Use Proxy"; /* RFC2616 Section 10.3.6 */
4228 case 307:
4229 return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */
4230 case 308:
4231 return "Permanent Redirect"; /* RFC7238 Section 3 */
4232
4233 /* RFC2616 Section 10.4 - Client Error 4xx */
4234 case 400:
4235 return "Bad Request"; /* RFC2616 Section 10.4.1 */
4236 case 401:
4237 return "Unauthorized"; /* RFC2616 Section 10.4.2 */
4238 case 402:
4239 return "Payment Required"; /* RFC2616 Section 10.4.3 */
4240 case 403:
4241 return "Forbidden"; /* RFC2616 Section 10.4.4 */
4242 case 404:
4243 return "Not Found"; /* RFC2616 Section 10.4.5 */
4244 case 405:
4245 return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */
4246 case 406:
4247 return "Not Acceptable"; /* RFC2616 Section 10.4.7 */
4248 case 407:
4249 return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */
4250 case 408:
4251 return "Request Time-out"; /* RFC2616 Section 10.4.9 */
4252 case 409:
4253 return "Conflict"; /* RFC2616 Section 10.4.10 */
4254 case 410:
4255 return "Gone"; /* RFC2616 Section 10.4.11 */
4256 case 411:
4257 return "Length Required"; /* RFC2616 Section 10.4.12 */
4258 case 412:
4259 return "Precondition Failed"; /* RFC2616 Section 10.4.13 */
4260 case 413:
4261 return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */
4262 case 414:
4263 return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */
4264 case 415:
4265 return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */
4266 case 416:
4267 return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17
4268 */
4269 case 417:
4270 return "Expectation Failed"; /* RFC2616 Section 10.4.18 */
4271
4272 case 421:
4273 return "Misdirected Request"; /* RFC7540 Section 9.1.2 */
4274 case 422:
4275 return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918
4276 * Section 11.2 */
4277 case 423:
4278 return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */
4279 case 424:
4280 return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918
4281 * Section 11.4 */
4282
4283 case 426:
4284 return "Upgrade Required"; /* RFC 2817 Section 4 */
4285
4286 case 428:
4287 return "Precondition Required"; /* RFC 6585, Section 3 */
4288 case 429:
4289 return "Too Many Requests"; /* RFC 6585, Section 4 */
4290
4291 case 431:
4292 return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */
4293
4294 case 451:
4295 return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05,
4296 * Section 3 */
4297
4298 /* RFC2616 Section 10.5 - Server Error 5xx */
4299 case 500:
4300 return "Internal Server Error"; /* RFC2616 Section 10.5.1 */
4301 case 501:
4302 return "Not Implemented"; /* RFC2616 Section 10.5.2 */
4303 case 502:
4304 return "Bad Gateway"; /* RFC2616 Section 10.5.3 */
4305 case 503:
4306 return "Service Unavailable"; /* RFC2616 Section 10.5.4 */
4307 case 504:
4308 return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */
4309 case 505:
4310 return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */
4311 case 506:
4312 return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */
4313 case 507:
4314 return "Insufficient Storage"; /* RFC2518 Section 10.6, RFC4918
4315 * Section 11.5 */
4316 case 508:
4317 return "Loop Detected"; /* RFC5842 Section 7.1 */
4318
4319 case 510:
4320 return "Not Extended"; /* RFC 2774, Section 7 */
4321 case 511:
4322 return "Network Authentication Required"; /* RFC 6585, Section 6 */
4323
4324 /* Other status codes, not shown in the IANA HTTP status code
4325 * assignment.
4326 * E.g., "de facto" standards due to common use, ... */
4327 case 418:
4328 return "I am a teapot"; /* RFC2324 Section 2.3.2 */
4329 case 419:
4330 return "Authentication Timeout"; /* common use */
4331 case 420:
4332 return "Enhance Your Calm"; /* common use */
4333 case 440:
4334 return "Login Timeout"; /* common use */
4335 case 509:
4336 return "Bandwidth Limit Exceeded"; /* common use */
4337
4338 default:
4339 /* This error code is unknown. This should not happen. */
4340 if (conn) {
4341 mg_cry_internal(conn,
4342 "Unknown HTTP response code: %u",
4343 response_code);
4344 }
4345
4346 /* Return at least a category according to RFC 2616 Section 10. */
4347 if (response_code >= 100 && response_code < 200) {
4348 /* Unknown informational status code */
4349 return "Information";
4350 }
4351 if (response_code >= 200 && response_code < 300) {
4352 /* Unknown success code */
4353 return "Success";
4354 }
4355 if (response_code >= 300 && response_code < 400) {
4356 /* Unknown redirection code */
4357 return "Redirection";
4358 }
4359 if (response_code >= 400 && response_code < 500) {
4360 /* Unknown request error code */
4361 return "Client Error";
4362 }
4363 if (response_code >= 500 && response_code < 600) {
4364 /* Unknown server error code */
4365 return "Server Error";
4366 }
4367
4368 /* Response code not even within reasonable range */
4369 return "";
4370 }
4371}

References mg_cry_internal.

Referenced by mg_send_http_error_impl().

◆ mg_get_response_info()

CIVETWEB_API const struct mg_response_info * mg_get_response_info ( const struct mg_connection * conn)

Definition at line 3561 of file civetweb.c.

3562{
3563 if (!conn) {
3564 return NULL;
3565 }
3567 return NULL;
3568 }
3569 return &conn->response_info;
3570}

References mg_connection::connection_type, CONNECTION_TYPE_RESPONSE, NULL, and mg_connection::response_info.

Referenced by run_client().

◆ mg_get_server_ports()

CIVETWEB_API int mg_get_server_ports ( const struct mg_context * ctx,
int size,
struct mg_server_port * ports )

Definition at line 3248 of file civetweb.c.

3251{
3252 int i, cnt = 0;
3253
3254 if (size <= 0) {
3255 return -1;
3256 }
3257 memset(ports, 0, sizeof(*ports) * (size_t)size);
3258 if (!ctx) {
3259 return -1;
3260 }
3261 if (!ctx->listening_sockets) {
3262 return -1;
3263 }
3264
3265 for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) {
3266
3267 ports[cnt].port =
3268 ntohs(USA_IN_PORT_UNSAFE(&(ctx->listening_sockets[i].lsa)));
3269 ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
3270 ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
3271
3272 if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
3273 /* IPv4 */
3274 ports[cnt].protocol = 1;
3275 cnt++;
3276 } else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) {
3277 /* IPv6 */
3278 ports[cnt].protocol = 3;
3279 cnt++;
3280 }
3281 }
3282
3283 return cnt;
3284}

References mg_server_port::is_redirect, socket::is_ssl, mg_server_port::is_ssl, mg_context::listening_sockets, socket::lsa, mg_context::num_listening_sockets, mg_server_port::port, mg_server_port::protocol, usa::sa, socket::ssl_redir, and USA_IN_PORT_UNSAFE.

◆ mg_get_system_info()

CIVETWEB_API int mg_get_system_info ( char * buffer,
int buflen )

Definition at line 21512 of file civetweb.c.

21513{
21514 char *end, *append_eoobj = NULL, block[256];
21515 size_t system_info_length = 0;
21516
21517#if defined(_WIN32)
21518 static const char eol[] = "\r\n", eoobj[] = "\r\n}\r\n";
21519#else
21520 static const char eol[] = "\n", eoobj[] = "\n}\n";
21521#endif
21522
21523 if ((buffer == NULL) || (buflen < 1)) {
21524 buflen = 0;
21525 end = buffer;
21526 } else {
21527 *buffer = 0;
21528 end = buffer + buflen;
21529 }
21530 if (buflen > (int)(sizeof(eoobj) - 1)) {
21531 /* has enough space to append eoobj */
21532 append_eoobj = buffer;
21533 if (end) {
21534 end -= sizeof(eoobj) - 1;
21535 }
21536 }
21537
21538 system_info_length += mg_str_append(&buffer, end, "{");
21539
21540 /* Server version */
21541 {
21542 const char *version = mg_version();
21544 NULL,
21545 block,
21546 sizeof(block),
21547 "%s\"version\" : \"%s\"",
21548 eol,
21549 version);
21550 system_info_length += mg_str_append(&buffer, end, block);
21551 }
21552
21553 /* System info */
21554 {
21555#if defined(_WIN32)
21556 DWORD dwVersion = 0;
21557 DWORD dwMajorVersion = 0;
21558 DWORD dwMinorVersion = 0;
21559 SYSTEM_INFO si;
21560
21561 GetSystemInfo(&si);
21562
21563#if defined(_MSC_VER)
21564#pragma warning(push)
21565 /* GetVersion was declared deprecated */
21566#pragma warning(disable : 4996)
21567#endif
21568 dwVersion = GetVersion();
21569#if defined(_MSC_VER)
21570#pragma warning(pop)
21571#endif
21572
21573 dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
21574 dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
21575
21577 NULL,
21578 block,
21579 sizeof(block),
21580 ",%s\"os\" : \"Windows %u.%u\"",
21581 eol,
21582 (unsigned)dwMajorVersion,
21583 (unsigned)dwMinorVersion);
21584 system_info_length += mg_str_append(&buffer, end, block);
21585
21587 NULL,
21588 block,
21589 sizeof(block),
21590 ",%s\"cpu\" : \"type %u, cores %u, mask %x\"",
21591 eol,
21592 (unsigned)si.wProcessorArchitecture,
21593 (unsigned)si.dwNumberOfProcessors,
21594 (unsigned)si.dwActiveProcessorMask);
21595 system_info_length += mg_str_append(&buffer, end, block);
21596#elif defined(__ZEPHYR__)
21598 NULL,
21599 block,
21600 sizeof(block),
21601 ",%s\"os\" : \"%s %s\"",
21602 eol,
21603 "Zephyr OS",
21604 ZEPHYR_VERSION);
21605 system_info_length += mg_str_append(&buffer, end, block);
21606#else
21607 struct utsname name;
21608 memset(&name, 0, sizeof(name));
21609 uname(&name);
21610
21612 NULL,
21613 block,
21614 sizeof(block),
21615 ",%s\"os\" : \"%s %s (%s) - %s\"",
21616 eol,
21617 name.sysname,
21618 name.version,
21619 name.release,
21620 name.machine);
21621 system_info_length += mg_str_append(&buffer, end, block);
21622#endif
21623 }
21624
21625 /* Features */
21626 {
21628 NULL,
21629 block,
21630 sizeof(block),
21631 ",%s\"features\" : %lu"
21632 ",%s\"feature_list\" : \"Server:%s%s%s%s%s%s%s%s%s\"",
21633 eol,
21634 (unsigned long)mg_check_feature(0xFFFFFFFFu),
21635 eol,
21636 mg_check_feature(MG_FEATURES_FILES) ? " Files" : "",
21637 mg_check_feature(MG_FEATURES_SSL) ? " HTTPS" : "",
21638 mg_check_feature(MG_FEATURES_CGI) ? " CGI" : "",
21639 mg_check_feature(MG_FEATURES_IPV6) ? " IPv6" : "",
21641 : "",
21642 mg_check_feature(MG_FEATURES_LUA) ? " Lua" : "",
21643 mg_check_feature(MG_FEATURES_SSJS) ? " JavaScript" : "",
21644 mg_check_feature(MG_FEATURES_CACHE) ? " Cache" : "",
21645 mg_check_feature(MG_FEATURES_STATS) ? " Stats" : "");
21646 system_info_length += mg_str_append(&buffer, end, block);
21647
21648#if defined(USE_LUA)
21650 NULL,
21651 block,
21652 sizeof(block),
21653 ",%s\"lua_version\" : \"%u (%s)\"",
21654 eol,
21655 (unsigned)LUA_VERSION_NUM,
21656 LUA_RELEASE);
21657 system_info_length += mg_str_append(&buffer, end, block);
21658#endif
21659#if defined(USE_DUKTAPE)
21661 NULL,
21662 block,
21663 sizeof(block),
21664 ",%s\"javascript\" : \"Duktape %u.%u.%u\"",
21665 eol,
21666 (unsigned)DUK_VERSION / 10000,
21667 ((unsigned)DUK_VERSION / 100) % 100,
21668 (unsigned)DUK_VERSION % 100);
21669 system_info_length += mg_str_append(&buffer, end, block);
21670#endif
21671 }
21672
21673 /* Build identifier. If BUILD_DATE is not set, __DATE__ will be used. */
21674 {
21675#if defined(BUILD_DATE)
21676 const char *bd = BUILD_DATE;
21677#else
21678#if defined(GCC_DIAGNOSTIC)
21679#if GCC_VERSION >= 40900
21680#pragma GCC diagnostic push
21681 /* Disable idiotic compiler warning -Wdate-time, appeared in gcc5. This
21682 * does not work in some versions. If "BUILD_DATE" is defined to some
21683 * string, it is used instead of __DATE__. */
21684#pragma GCC diagnostic ignored "-Wdate-time"
21685#endif
21686#endif
21687 const char *bd = __DATE__;
21688#if defined(GCC_DIAGNOSTIC)
21689#if GCC_VERSION >= 40900
21690#pragma GCC diagnostic pop
21691#endif
21692#endif
21693#endif
21694
21696 NULL, NULL, block, sizeof(block), ",%s\"build\" : \"%s\"", eol, bd);
21697
21698 system_info_length += mg_str_append(&buffer, end, block);
21699 }
21700
21701 /* Compiler information */
21702 /* http://sourceforge.net/p/predef/wiki/Compilers/ */
21703 {
21704#if defined(_MSC_VER)
21706 NULL,
21707 block,
21708 sizeof(block),
21709 ",%s\"compiler\" : \"MSC: %u (%u)\"",
21710 eol,
21711 (unsigned)_MSC_VER,
21712 (unsigned)_MSC_FULL_VER);
21713 system_info_length += mg_str_append(&buffer, end, block);
21714#elif defined(__MINGW64__)
21716 NULL,
21717 block,
21718 sizeof(block),
21719 ",%s\"compiler\" : \"MinGW64: %u.%u\"",
21720 eol,
21721 (unsigned)__MINGW64_VERSION_MAJOR,
21722 (unsigned)__MINGW64_VERSION_MINOR);
21723 system_info_length += mg_str_append(&buffer, end, block);
21725 NULL,
21726 block,
21727 sizeof(block),
21728 ",%s\"compiler\" : \"MinGW32: %u.%u\"",
21729 eol,
21730 (unsigned)__MINGW32_MAJOR_VERSION,
21731 (unsigned)__MINGW32_MINOR_VERSION);
21732 system_info_length += mg_str_append(&buffer, end, block);
21733#elif defined(__MINGW32__)
21735 NULL,
21736 block,
21737 sizeof(block),
21738 ",%s\"compiler\" : \"MinGW32: %u.%u\"",
21739 eol,
21740 (unsigned)__MINGW32_MAJOR_VERSION,
21741 (unsigned)__MINGW32_MINOR_VERSION);
21742 system_info_length += mg_str_append(&buffer, end, block);
21743#elif defined(__clang__)
21745 NULL,
21746 block,
21747 sizeof(block),
21748 ",%s\"compiler\" : \"clang: %u.%u.%u (%s)\"",
21749 eol,
21750 __clang_major__,
21751 __clang_minor__,
21752 __clang_patchlevel__,
21753 __clang_version__);
21754 system_info_length += mg_str_append(&buffer, end, block);
21755#elif defined(__GNUC__)
21757 NULL,
21758 block,
21759 sizeof(block),
21760 ",%s\"compiler\" : \"gcc: %u.%u.%u\"",
21761 eol,
21762 (unsigned)__GNUC__,
21763 (unsigned)__GNUC_MINOR__,
21764 (unsigned)__GNUC_PATCHLEVEL__);
21765 system_info_length += mg_str_append(&buffer, end, block);
21766#elif defined(__INTEL_COMPILER)
21768 NULL,
21769 block,
21770 sizeof(block),
21771 ",%s\"compiler\" : \"Intel C/C++: %u\"",
21772 eol,
21773 (unsigned)__INTEL_COMPILER);
21774 system_info_length += mg_str_append(&buffer, end, block);
21775#elif defined(__BORLANDC__)
21777 NULL,
21778 block,
21779 sizeof(block),
21780 ",%s\"compiler\" : \"Borland C: 0x%x\"",
21781 eol,
21782 (unsigned)__BORLANDC__);
21783 system_info_length += mg_str_append(&buffer, end, block);
21784#elif defined(__SUNPRO_C)
21786 NULL,
21787 block,
21788 sizeof(block),
21789 ",%s\"compiler\" : \"Solaris: 0x%x\"",
21790 eol,
21791 (unsigned)__SUNPRO_C);
21792 system_info_length += mg_str_append(&buffer, end, block);
21793#else
21795 NULL,
21796 block,
21797 sizeof(block),
21798 ",%s\"compiler\" : \"other\"",
21799 eol);
21800 system_info_length += mg_str_append(&buffer, end, block);
21801#endif
21802 }
21803
21804 /* Determine 32/64 bit data mode.
21805 * see https://en.wikipedia.org/wiki/64-bit_computing */
21806 {
21808 NULL,
21809 block,
21810 sizeof(block),
21811 ",%s\"data_model\" : \"int:%u/%u/%u/%u, float:%u/%u/%u, "
21812 "char:%u/%u, "
21813 "ptr:%u, size:%u, time:%u\"",
21814 eol,
21815 (unsigned)sizeof(short),
21816 (unsigned)sizeof(int),
21817 (unsigned)sizeof(long),
21818 (unsigned)sizeof(long long),
21819 (unsigned)sizeof(float),
21820 (unsigned)sizeof(double),
21821 (unsigned)sizeof(long double),
21822 (unsigned)sizeof(char),
21823 (unsigned)sizeof(wchar_t),
21824 (unsigned)sizeof(void *),
21825 (unsigned)sizeof(size_t),
21826 (unsigned)sizeof(time_t));
21827 system_info_length += mg_str_append(&buffer, end, block);
21828 }
21829
21830 /* Terminate string */
21831 if (append_eoobj) {
21832 strcat(append_eoobj, eoobj);
21833 }
21834 system_info_length += sizeof(eoobj) - 1;
21835
21836 return (int)system_info_length;
21837}
CIVETWEB_API const char * mg_version(void)
Definition civetweb.c:3514
CIVETWEB_API unsigned mg_check_feature(unsigned feature)
Definition civetweb.c:21430
#define LUA_VERSION_NUM
#define LUA_RELEASE

References block(), DUK_VERSION, LUA_RELEASE, LUA_VERSION_NUM, mg_check_feature(), MG_FEATURES_CACHE, MG_FEATURES_CGI, MG_FEATURES_FILES, MG_FEATURES_IPV6, MG_FEATURES_LUA, MG_FEATURES_SSJS, MG_FEATURES_SSL, MG_FEATURES_STATS, MG_FEATURES_WEBSOCKET, mg_snprintf(), mg_str_append(), mg_version(), name, and NULL.

Referenced by init_system_info().

◆ mg_get_thread_pointer()

CIVETWEB_API void * mg_get_thread_pointer ( const struct mg_connection * conn)

Definition at line 3209 of file civetweb.c.

3210{
3211 /* both methods should return the same pointer */
3212 if (conn) {
3213 /* quick access, in case conn is known */
3214 return conn->tls_user_ptr;
3215 } else {
3216 /* otherwise get pointer from thread local storage (TLS) */
3217 struct mg_workerTLS *tls =
3218 (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
3219 return tls->user_ptr;
3220 }
3221}
void * tls_user_ptr
Definition civetweb.c:2571
void * user_ptr
Definition civetweb.c:1590

References sTlsKey, mg_connection::tls_user_ptr, and mg_workerTLS::user_ptr.

◆ mg_get_user_connection_data()

CIVETWEB_API void * mg_get_user_connection_data ( const struct mg_connection * conn)

Definition at line 3238 of file civetweb.c.

3239{
3240 if (conn != NULL) {
3241 return conn->request_info.conn_data;
3242 }
3243 return NULL;
3244}
void * conn_data
Definition civetweb.h:176

References mg_request_info::conn_data, NULL, and mg_connection::request_info.

◆ mg_get_user_context_data()

CIVETWEB_API void * mg_get_user_context_data ( const struct mg_connection * conn)

Definition at line 3202 of file civetweb.c.

3203{
3204 return mg_get_user_data(mg_get_context(conn));
3205}
CIVETWEB_API void * mg_get_user_data(const struct mg_context *ctx)
Definition civetweb.c:3195
CIVETWEB_API struct mg_context * mg_get_context(const struct mg_connection *conn)
Definition civetweb.c:3188

References mg_get_context(), and mg_get_user_data().

◆ mg_get_user_data()

CIVETWEB_API void * mg_get_user_data ( const struct mg_context * ctx)

Definition at line 3195 of file civetweb.c.

3196{
3197 return (ctx == NULL) ? NULL : ctx->user_data;
3198}

References NULL, and mg_context::user_data.

Referenced by mg_get_user_context_data().

◆ mg_get_valid_options()

CIVETWEB_API const struct mg_option * mg_get_valid_options ( void )

Definition at line 2833 of file civetweb.c.

2834{
2835 return config_options;
2836}

References config_options.

Referenced by set_option(), and show_usage_and_exit().

◆ mg_get_var()

CIVETWEB_API int mg_get_var ( const char * data,
size_t data_len,
const char * name,
char * dst,
size_t dst_len )

Definition at line 7088 of file civetweb.c.

7093{
7094 return mg_get_var2(data, data_len, name, dst, dst_len, 0);
7095}
CIVETWEB_API int mg_get_var2(const char *data, size_t data_len, const char *name, char *dst, size_t dst_len, size_t occurrence)
Definition civetweb.c:7099

References mg_get_var2(), and name.

◆ mg_get_var2()

CIVETWEB_API int mg_get_var2 ( const char * data,
size_t data_len,
const char * name,
char * dst,
size_t dst_len,
size_t occurrence )

Definition at line 7099 of file civetweb.c.

7105{
7106 const char *p, *e, *s;
7107 size_t name_len;
7108 int len;
7109
7110 if ((dst == NULL) || (dst_len == 0)) {
7111 len = -2;
7112 } else if ((data == NULL) || (name == NULL) || (data_len == 0)) {
7113 len = -1;
7114 dst[0] = '\0';
7115 } else {
7116 name_len = strlen(name);
7117 e = data + data_len;
7118 len = -1;
7119 dst[0] = '\0';
7120
7121 /* data is "var1=val1&var2=val2...". Find variable first */
7122 for (p = data; p + name_len < e; p++) {
7123 if (((p == data) || (p[-1] == '&')) && (p[name_len] == '=')
7124 && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
7125 /* Point p to variable value */
7126 p += name_len + 1;
7127
7128 /* Point s to the end of the value */
7129 s = (const char *)memchr(p, '&', (size_t)(e - p));
7130 if (s == NULL) {
7131 s = e;
7132 }
7133 DEBUG_ASSERT(s >= p);
7134 if (s < p) {
7135 return -3;
7136 }
7137
7138 /* Decode variable into destination buffer */
7139 len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1);
7140
7141 /* Redirect error code from -1 to -2 (destination buffer too
7142 * small). */
7143 if (len == -1) {
7144 len = -2;
7145 }
7146 break;
7147 }
7148 }
7149 }
7150
7151 return len;
7152}

References DEBUG_ASSERT, mg_strncasecmp(), mg_url_decode(), name, NULL, and s.

Referenced by mg_get_var().

◆ mg_global_lock()

static FUNCTION_MAY_BE_UNUSED void mg_global_lock ( void )
static

Definition at line 1094 of file civetweb.c.

1095{
1096 (void)pthread_mutex_lock(&global_lock_mutex);
1097}

References global_lock_mutex.

Referenced by mg_atomic_dec(), mg_atomic_inc(), mg_exit_library(), and mg_init_library().

◆ mg_global_unlock()

static FUNCTION_MAY_BE_UNUSED void mg_global_unlock ( void )
static

Definition at line 1102 of file civetweb.c.

1103{
1104 (void)pthread_mutex_unlock(&global_lock_mutex);
1105}

References global_lock_mutex.

Referenced by mg_atomic_dec(), mg_atomic_inc(), mg_exit_library(), and mg_init_library().

◆ mg_inet_pton()

static int mg_inet_pton ( int af,
const char * src,
void * dst,
size_t dstlen,
int resolve_src )
static

Definition at line 9246 of file civetweb.c.

9247{
9248 struct addrinfo hints, *res, *ressave;
9249 int func_ret = 0;
9250 int gai_ret;
9251
9252 memset(&hints, 0, sizeof(struct addrinfo));
9253 hints.ai_family = af;
9254 if (!resolve_src) {
9255 hints.ai_flags = AI_NUMERICHOST;
9256 }
9257
9258 gai_ret = getaddrinfo(src, NULL, &hints, &res);
9259 if (gai_ret != 0) {
9260 /* gai_strerror could be used to convert gai_ret to a string */
9261 /* POSIX return values: see
9262 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html
9263 */
9264 /* Windows return values: see
9265 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx
9266 */
9267 return 0;
9268 }
9269
9270 ressave = res;
9271
9272 while (res) {
9273 if ((dstlen >= (size_t)res->ai_addrlen)
9274 && (res->ai_addr->sa_family == af)) {
9275 memcpy(dst, res->ai_addr, res->ai_addrlen);
9276 func_ret = 1;
9277 }
9278 res = res->ai_next;
9279 }
9280
9281 freeaddrinfo(ressave);
9282 return func_ret;
9283}

References NULL.

Referenced by connect_socket(), parse_match_net(), and parse_port_string().

◆ mg_init_library()

CIVETWEB_API unsigned mg_init_library ( unsigned features)

Definition at line 22347 of file civetweb.c.

22348{
22349 unsigned features_to_init = mg_check_feature(features & 0xFFu);
22350 unsigned features_inited = features_to_init;
22351
22352 if (mg_init_library_called <= 0) {
22353 /* Not initialized yet */
22354 if (0 != pthread_mutex_init(&global_lock_mutex, NULL)) {
22355 return 0;
22356 }
22357 }
22358
22360
22361 if (mg_init_library_called <= 0) {
22362 int i;
22363 size_t len;
22364
22365#if defined(_WIN32)
22366 int file_mutex_init = 1;
22367 int wsa = 1;
22368#else
22369 int mutexattr_init = 1;
22370#endif
22371 int failed = 1;
22372 int key_create = pthread_key_create(&sTlsKey, tls_dtor);
22373
22374 if (key_create == 0) {
22375#if defined(_WIN32)
22376 file_mutex_init =
22377 pthread_mutex_init(&global_log_file_lock, &pthread_mutex_attr);
22378 if (file_mutex_init == 0) {
22379 /* Start WinSock */
22380 WSADATA data;
22381 failed = wsa = WSAStartup(MAKEWORD(2, 2), &data);
22382 }
22383#else
22384 mutexattr_init = pthread_mutexattr_init(&pthread_mutex_attr);
22385 if (mutexattr_init == 0) {
22386 failed = pthread_mutexattr_settype(&pthread_mutex_attr,
22387 PTHREAD_MUTEX_RECURSIVE);
22388 }
22389#endif
22390 }
22391
22392 if (failed) {
22393#if defined(_WIN32)
22394 if (wsa == 0) {
22395 (void)WSACleanup();
22396 }
22397 if (file_mutex_init == 0) {
22398 (void)pthread_mutex_destroy(&global_log_file_lock);
22399 }
22400#else
22401 if (mutexattr_init == 0) {
22402 (void)pthread_mutexattr_destroy(&pthread_mutex_attr);
22403 }
22404#endif
22405 if (key_create == 0) {
22406 (void)pthread_key_delete(sTlsKey);
22407 }
22409 (void)pthread_mutex_destroy(&global_lock_mutex);
22410 return 0;
22411 }
22412
22413 len = 1;
22414 for (i = 0; http_methods[i].name != NULL; i++) {
22415 size_t sl = strlen(http_methods[i].name);
22416 len += sl;
22417 if (i > 0) {
22418 len += 2;
22419 }
22420 }
22421 all_methods = (char *)mg_malloc(len);
22422 if (!all_methods) {
22423 /* Must never happen */
22425 (void)pthread_mutex_destroy(&global_lock_mutex);
22426 return 0;
22427 }
22428 all_methods[0] = 0;
22429 for (i = 0; http_methods[i].name != NULL; i++) {
22430 if (i > 0) {
22431 strcat(all_methods, ", ");
22432 strcat(all_methods, http_methods[i].name);
22433 } else {
22434 strcpy(all_methods, http_methods[i].name);
22435 }
22436 }
22437 }
22438
22439#if defined(USE_LUA)
22440 lua_init_optional_libraries();
22441#endif
22442
22443#if (defined(OPENSSL_API_1_0) || defined(OPENSSL_API_1_1) \
22444 || defined(OPENSSL_API_3_0)) \
22445 && !defined(NO_SSL)
22446
22447 if (features_to_init & MG_FEATURES_SSL) {
22448 if (!mg_openssl_initialized) {
22449 char ebuf[128];
22450 if (initialize_openssl(ebuf, sizeof(ebuf))) {
22451 mg_openssl_initialized = 1;
22452 } else {
22453 (void)ebuf;
22454 DEBUG_TRACE("Initializing SSL failed: %s", ebuf);
22455 features_inited &= ~((unsigned)(MG_FEATURES_SSL));
22456 }
22457 } else {
22458 /* ssl already initialized */
22459 }
22460 }
22461
22462#endif
22463
22464 if (mg_init_library_called <= 0) {
22466 } else {
22468 }
22470
22471 return features_inited;
22472}
static void tls_dtor(void *key)
Definition civetweb.c:16321

References all_methods, DEBUG_TRACE, global_lock_mutex, http_methods, initialize_openssl(), mg_check_feature(), MG_FEATURES_SSL, mg_global_lock(), mg_global_unlock(), mg_init_library_called, mg_malloc(), mg_http_method_info::name, name, NULL, pthread_mutex_attr, sTlsKey, and tls_dtor().

Referenced by legacy_init(), and run_client().

◆ mg_join_thread()

static int mg_join_thread ( pthread_t threadid)
static

Definition at line 5757 of file civetweb.c.

5758{
5759 int result;
5760
5761 result = pthread_join(threadid, NULL);
5762 return result;
5763}
#define threadid
Definition sqlite3.c:33865

References NULL, and threadid.

Referenced by master_thread_run(), mg_close_connection(), and mg_stop().

◆ mg_lock_connection()

CIVETWEB_API void mg_lock_connection ( struct mg_connection * conn)

Definition at line 13000 of file civetweb.c.

13001{
13002 if (conn) {
13003 (void)pthread_mutex_lock(&conn->mutex);
13004 }
13005}

References mg_connection::mutex.

Referenced by close_connection().

◆ mg_lock_context()

◆ mg_malloc()

static __inline void * mg_malloc ( size_t a)
static

Definition at line 1474 of file civetweb.c.

1475{
1476 return malloc(a);
1477}
#define malloc
Definition civetweb.c:1539

References malloc.

Referenced by alloc_vprintf(), alloc_vprintf2(), handle_directory_request(), initialize_openssl(), mg_current_thread_id(), mg_init_library(), and print_dir_entry().

◆ mg_md5()

CIVETWEB_API char * mg_md5 ( char buf[33],
... )

Definition at line 8404 of file civetweb.c.

8405{
8406 md5_byte_t hash[16];
8407 const char *p;
8408 va_list ap;
8409 md5_state_t ctx;
8410
8411 md5_init(&ctx);
8412
8413 va_start(ap, buf);
8414 while ((p = va_arg(ap, const char *)) != NULL) {
8415 md5_append(&ctx, (const md5_byte_t *)p, strlen(p));
8416 }
8417 va_end(ap);
8418
8419 md5_finish(&ctx, hash);
8420 bin2str(buf, hash, sizeof(hash));
8421 return buf;
8422}
static void bin2str(char *to, const unsigned char *p, size_t len)
Definition civetweb.c:8389

References bin2str(), and NULL.

Referenced by check_password_digest(), dav_lock_file(), mg_modify_passwords_file(), and read_auth_file().

◆ mg_modify_passwords_file()

CIVETWEB_API int mg_modify_passwords_file ( const char * fname,
const char * domain,
const char * user,
const char * pass )

Definition at line 9220 of file civetweb.c.

9224{
9225 char ha1buf[33];
9226 if ((fname == NULL) || (domain == NULL) || (user == NULL)) {
9227 return 0;
9228 }
9229 if ((pass == NULL) || (pass[0] == 0)) {
9230 return mg_modify_passwords_file_ha1(fname, domain, user, NULL);
9231 }
9232
9233 mg_md5(ha1buf, user, ":", domain, ":", pass, NULL);
9234 return mg_modify_passwords_file_ha1(fname, domain, user, ha1buf);
9235}
CIVETWEB_API int mg_modify_passwords_file_ha1(const char *fname, const char *domain, const char *user, const char *ha1)
Definition civetweb.c:9057

References mg_md5(), mg_modify_passwords_file_ha1(), and NULL.

Referenced by start_civetweb().

◆ mg_modify_passwords_file_ha1()

CIVETWEB_API int mg_modify_passwords_file_ha1 ( const char * fname,
const char * domain,
const char * user,
const char * ha1 )

Definition at line 9057 of file civetweb.c.

9061{
9062 int found = 0, i, result = 1;
9063 char line[512], u[256], d[256], h[256];
9064 struct stat st = {0};
9065 FILE *fp = NULL;
9066 char *temp_file = NULL;
9067 int temp_file_offs = 0;
9068
9069 /* Regard empty password as no password - remove user record. */
9070 if ((ha1 != NULL) && (ha1[0] == '\0')) {
9071 ha1 = NULL;
9072 }
9073
9074 /* Other arguments must not be empty */
9075 if ((fname == NULL) || (domain == NULL) || (user == NULL)) {
9076 return 0;
9077 }
9078
9079 /* Using the given file format, user name and domain must not contain
9080 * the ':' character */
9081 if (strchr(user, ':') != NULL) {
9082 return 0;
9083 }
9084 if (strchr(domain, ':') != NULL) {
9085 return 0;
9086 }
9087
9088 /* Do not allow control characters like newline in user name and domain.
9089 * Do not allow excessively long names either. */
9090 for (i = 0; ((i < 255) && (user[i] != 0)); i++) {
9091 if (iscntrl((unsigned char)user[i])) {
9092 return 0;
9093 }
9094 }
9095 if (user[i]) {
9096 return 0; /* user name too long */
9097 }
9098 for (i = 0; ((i < 255) && (domain[i] != 0)); i++) {
9099 if (iscntrl((unsigned char)domain[i])) {
9100 return 0;
9101 }
9102 }
9103 if (domain[i]) {
9104 return 0; /* domain name too long */
9105 }
9106
9107 /* The maximum length of the path to the password file is limited */
9108 if (strlen(fname) >= UTF8_PATH_MAX) {
9109 return 0;
9110 }
9111
9112 /* Check if the file exists, and get file size */
9113 if (0 == stat(fname, &st)) {
9114 int temp_buf_len;
9115 if (st.st_size > 10485760) {
9116 /* Some funster provided a >10 MB text file */
9117 return 0;
9118 }
9119
9120 /* Add enough space for one more line */
9121 temp_buf_len = (int)st.st_size + 1024;
9122
9123 /* Allocate memory (instead of using a temporary file) */
9124 temp_file = (char *)mg_calloc((size_t)temp_buf_len, 1);
9125 if (!temp_file) {
9126 /* Out of memory */
9127 return 0;
9128 }
9129
9130 /* File exists. Read it into a memory buffer. */
9131 fp = fopen(fname, "r");
9132 if (fp == NULL) {
9133 /* Cannot read file. No permission? */
9134 mg_free(temp_file);
9135 return 0;
9136 }
9137
9138 /* Read content and store in memory */
9139 while ((fgets(line, sizeof(line), fp) != NULL)
9140 && ((temp_file_offs + 600) < temp_buf_len)) {
9141 /* file format is "user:domain:hash\n" */
9142 if (sscanf(line, "%255[^:]:%255[^:]:%255s", u, d, h) != 3) {
9143 continue;
9144 }
9145 u[255] = 0;
9146 d[255] = 0;
9147 h[255] = 0;
9148
9149 if (!strcmp(u, user) && !strcmp(d, domain)) {
9150 /* Found the user: change the password hash or drop the user
9151 */
9152 if ((ha1 != NULL) && (!found)) {
9153 i = sprintf(temp_file + temp_file_offs,
9154 "%s:%s:%s\n",
9155 user,
9156 domain,
9157 ha1);
9158 if (i < 1) {
9159 fclose(fp);
9160 mg_free(temp_file);
9161 return 0;
9162 }
9163 temp_file_offs += i;
9164 }
9165 found = 1;
9166 } else {
9167 /* Copy existing user, including password hash */
9168 i = sprintf(temp_file + temp_file_offs, "%s:%s:%s\n", u, d, h);
9169 if (i < 1) {
9170 fclose(fp);
9171 mg_free(temp_file);
9172 return 0;
9173 }
9174 temp_file_offs += i;
9175 }
9176 }
9177 fclose(fp);
9178 }
9179
9180 /* Create new file */
9181 fp = fopen(fname, "w");
9182 if (!fp) {
9183 mg_free(temp_file);
9184 return 0;
9185 }
9186
9187#if !defined(_WIN32)
9188 /* On Linux & co., restrict file read/write permissions to the owner */
9189 if (fchmod(fileno(fp), S_IRUSR | S_IWUSR) != 0) {
9190 result = 0;
9191 }
9192#endif
9193
9194 if ((temp_file != NULL) && (temp_file_offs > 0)) {
9195 /* Store buffered content of old file */
9196 if (fwrite(temp_file, 1, (size_t)temp_file_offs, fp)
9197 != (size_t)temp_file_offs) {
9198 result = 0;
9199 }
9200 }
9201
9202 /* If new user, just add it */
9203 if ((ha1 != NULL) && (!found)) {
9204 if (fprintf(fp, "%s:%s:%s\n", user, domain, ha1) < 6) {
9205 result = 0;
9206 }
9207 }
9208
9209 /* All data written */
9210 if (fclose(fp) != 0) {
9211 result = 0;
9212 }
9213
9214 mg_free(temp_file);
9215 return result;
9216}
size_t fwrite(const void *, size_t, size_t, FILE *)

References fwrite(), mg_calloc(), mg_free(), NULL, and UTF8_PATH_MAX.

Referenced by mg_modify_passwords_file().

◆ mg_path_suspicious()

static int mg_path_suspicious ( const struct mg_connection * conn,
const char * path )
static

Definition at line 2871 of file civetweb.c.

2872{
2873 const uint8_t *c = (const uint8_t *)path;
2874 (void)conn; /* not used */
2875
2876 if ((c == NULL) || (c[0] == 0)) {
2877 /* Null pointer or empty path --> suspicious */
2878 return 1;
2879 }
2880
2881#if defined(_WIN32)
2882 while (*c) {
2883 if (*c < 32) {
2884 /* Control character */
2885 return 1;
2886 }
2887 if ((*c == '>') || (*c == '<') || (*c == '|')) {
2888 /* stdin/stdout redirection character */
2889 return 1;
2890 }
2891 if ((*c == '*') || (*c == '?')) {
2892 /* Wildcard character */
2893 return 1;
2894 }
2895 if (*c == '"') {
2896 /* Windows quotation */
2897 return 1;
2898 }
2899 c++;
2900 }
2901#endif
2902
2903 /* Nothing suspicious found */
2904 return 0;
2905}

References NULL.

Referenced by mg_fopen(), and mg_stat().

◆ mg_poll()

static int mg_poll ( struct mg_pollfd * pfd,
unsigned int n,
int milliseconds,
const stop_flag_t * stop_flag )
static

Definition at line 5938 of file civetweb.c.

5942{
5943 /* Call poll, but only for a maximum time of a few seconds.
5944 * This will allow to stop the server after some seconds, instead
5945 * of having to wait for a long socket timeout. */
5946 int ms_now = SOCKET_TIMEOUT_QUANTUM; /* Sleep quantum in ms */
5947
5948 int check_pollerr = 0;
5949 if ((n == 1) && ((pfd[0].events & POLLERR) == 0)) {
5950 /* If we wait for only one file descriptor, wait on error as well */
5951 pfd[0].events |= POLLERR;
5952 check_pollerr = 1;
5953 }
5954
5955 do {
5956 int result;
5957
5958 if (!STOP_FLAG_IS_ZERO(&*stop_flag)) {
5959 /* Shut down signal */
5960 return -2;
5961 }
5962
5963 if ((milliseconds >= 0) && (milliseconds < ms_now)) {
5964 ms_now = milliseconds;
5965 }
5966
5967 result = poll(pfd, n, ms_now);
5968 if (result != 0) {
5969 int err = ERRNO;
5970 if ((result == 1) || (!ERROR_TRY_AGAIN(err))) {
5971 /* Poll returned either success (1) or error (-1).
5972 * Forward both to the caller. */
5973 if ((check_pollerr)
5974 && ((pfd[0].revents & (POLLIN | POLLOUT | POLLERR))
5975 == POLLERR)) {
5976 /* One and only file descriptor returned error */
5977 return -1;
5978 }
5979 return result;
5980 }
5981 }
5982
5983 /* Poll returned timeout (0). */
5984 if (milliseconds > 0) {
5985 milliseconds -= ms_now;
5986 }
5987
5988 } while (milliseconds > 0);
5989
5990 /* timeout: return 0 */
5991 return 0;
5992}
#define ERROR_TRY_AGAIN(err)
Definition civetweb.c:447

References ERRNO, ERROR_TRY_AGAIN, SOCKET_TIMEOUT_QUANTUM, and STOP_FLAG_IS_ZERO.

Referenced by connect_socket(), master_thread_run(), pull_inner(), push_inner(), and sslize().

◆ mg_printf()

CIVETWEB_API int mg_printf ( struct mg_connection * conn,
const char * fmt,
... )

Definition at line 7034 of file civetweb.c.

7035{
7036 va_list ap;
7037 int result;
7038
7039 va_start(ap, fmt);
7040 result = mg_vprintf(conn, fmt, ap);
7041 va_end(ap);
7042
7043 return result;
7044}

References mg_vprintf().

Referenced by dav_lock_file(), dav_proppatch(), forward_body_data(), handle_cgi_request(), handle_directory_request(), handle_propfind(), handle_request(), mg_connect_websocket_client_impl(), mg_send_http_error_impl(), print_dir_entry(), print_props(), run_client(), and user_handler().

◆ mg_read()

CIVETWEB_API int mg_read ( struct mg_connection * conn,
void * buf,
size_t len )

Definition at line 6614 of file civetweb.c.

6615{
6616 if (len > INT_MAX) {
6617 len = INT_MAX;
6618 }
6619
6620 if (conn == NULL) {
6621 return 0;
6622 }
6623
6624 if (conn->is_chunked) {
6625 size_t all_read = 0;
6626
6627 while (len > 0) {
6628 if (conn->is_chunked >= 3) {
6629 /* No more data left to read */
6630 return 0;
6631 }
6632 if (conn->is_chunked != 1) {
6633 /* Has error */
6634 return -1;
6635 }
6636
6637 if (conn->consumed_content != conn->content_len) {
6638 /* copy from the current chunk */
6639 int read_ret = mg_read_inner(conn, (char *)buf + all_read, len);
6640
6641 if (read_ret < 1) {
6642 /* read error */
6643 conn->is_chunked = 2;
6644 return -1;
6645 }
6646
6647 all_read += (size_t)read_ret;
6648 len -= (size_t)read_ret;
6649
6650 if (conn->consumed_content == conn->content_len) {
6651 /* Add data bytes in the current chunk have been read,
6652 * so we are expecting \r\n now. */
6653 char x[2];
6654 conn->content_len += 2;
6655 if ((mg_read_inner(conn, x, 2) != 2) || (x[0] != '\r')
6656 || (x[1] != '\n')) {
6657 /* Protocol violation */
6658 conn->is_chunked = 2;
6659 return -1;
6660 }
6661 }
6662
6663 } else {
6664 /* fetch a new chunk */
6665 size_t i;
6666 char lenbuf[64];
6667 char *end = NULL;
6668 unsigned long chunkSize = 0;
6669
6670 for (i = 0; i < (sizeof(lenbuf) - 1); i++) {
6671 conn->content_len++;
6672 if (mg_read_inner(conn, lenbuf + i, 1) != 1) {
6673 lenbuf[i] = 0;
6674 }
6675 if ((i > 0) && (lenbuf[i] == ';')) {
6676 // chunk extension --> skip chars until next CR
6677 //
6678 // RFC 2616, 3.6.1 Chunked Transfer Coding
6679 // (https://www.rfc-editor.org/rfc/rfc2616#page-25)
6680 //
6681 // chunk = chunk-size [ chunk-extension ] CRLF
6682 // chunk-data CRLF
6683 // ...
6684 // chunk-extension= *( ";" chunk-ext-name [ "="
6685 // chunk-ext-val ] )
6686 do
6687 ++conn->content_len;
6688 while (mg_read_inner(conn, lenbuf + i, 1) == 1
6689 && lenbuf[i] != '\r');
6690 }
6691 if ((i > 0) && (lenbuf[i] == '\r')
6692 && (lenbuf[i - 1] != '\r')) {
6693 continue;
6694 }
6695 if ((i > 1) && (lenbuf[i] == '\n')
6696 && (lenbuf[i - 1] == '\r')) {
6697 lenbuf[i + 1] = 0;
6698 chunkSize = strtoul(lenbuf, &end, 16);
6699 if (chunkSize == 0) {
6700 /* regular end of content */
6701 conn->is_chunked = 3;
6702 }
6703 break;
6704 }
6705 if (!isxdigit((unsigned char)lenbuf[i])) {
6706 /* illegal character for chunk length */
6707 conn->is_chunked = 2;
6708 return -1;
6709 }
6710 }
6711 if ((end == NULL) || (*end != '\r')) {
6712 /* chunksize not set correctly */
6713 conn->is_chunked = 2;
6714 return -1;
6715 }
6716 if (conn->is_chunked == 3) {
6717 /* try discarding trailer for keep-alive */
6718
6719 // We found the last chunk (length 0) including the
6720 // CRLF that terminates that chunk. Now follows a possibly
6721 // empty trailer and a final CRLF.
6722 //
6723 // see RFC 2616, 3.6.1 Chunked Transfer Coding
6724 // (https://www.rfc-editor.org/rfc/rfc2616#page-25)
6725 //
6726 // Chunked-Body = *chunk
6727 // last-chunk
6728 // trailer
6729 // CRLF
6730 // ...
6731 // last-chunk = 1*("0") [ chunk-extension ] CRLF
6732 // ...
6733 // trailer = *(entity-header CRLF)
6734
6735 int crlf_count = 2; // one CRLF already determined
6736
6737 while (crlf_count < 4 && conn->is_chunked == 3) {
6738 ++conn->content_len;
6739 if (mg_read_inner(conn, lenbuf, 1) == 1) {
6740 if ((crlf_count == 0 || crlf_count == 2)) {
6741 if (lenbuf[0] == '\r')
6742 ++crlf_count;
6743 else
6744 crlf_count = 0;
6745 } else {
6746 // previous character was a CR
6747 // --> next character must be LF
6748
6749 if (lenbuf[0] == '\n')
6750 ++crlf_count;
6751 else
6752 conn->is_chunked = 2;
6753 }
6754 } else
6755 // premature end of trailer
6756 conn->is_chunked = 2;
6757 }
6758
6759 if (conn->is_chunked == 2)
6760 return -1;
6761 else
6762 conn->is_chunked = 4;
6763
6764 break;
6765 }
6766
6767 /* append a new chunk */
6768 conn->content_len += (int64_t)chunkSize;
6769 }
6770 }
6771
6772 return (int)all_read;
6773 }
6774 return mg_read_inner(conn, buf, len);
6775}
static int mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
Definition civetweb.c:6494

References mg_connection::consumed_content, mg_connection::content_len, mg_connection::is_chunked, mg_read_inner(), and NULL.

Referenced by discard_unread_request_data(), forward_body_data(), mg_store_body(), and run_client().

◆ mg_read_inner()

static int mg_read_inner ( struct mg_connection * conn,
void * buf,
size_t len )
static

Definition at line 6494 of file civetweb.c.

6495{
6496 int64_t content_len, n, buffered_len, nread;
6497 int64_t len64 =
6498 (int64_t)((len > INT_MAX) ? INT_MAX : len); /* since the return value is
6499 * int, we may not read more
6500 * bytes */
6501 const char *body;
6502
6503 if (conn == NULL) {
6504 return 0;
6505 }
6506
6507 /* If Content-Length is not set for a response with body data,
6508 * we do not know in advance how much data should be read. */
6509 content_len = conn->content_len;
6510 if (content_len < 0) {
6511 /* The body data is completed when the connection is closed. */
6512 content_len = INT64_MAX;
6513 }
6514
6515 nread = 0;
6516 if (conn->consumed_content < content_len) {
6517 /* Adjust number of bytes to read. */
6518 int64_t left_to_read = content_len - conn->consumed_content;
6519 if (left_to_read < len64) {
6520 /* Do not read more than the total content length of the
6521 * request.
6522 */
6523 len64 = left_to_read;
6524 }
6525
6526 /* Return buffered data */
6527 buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
6528 - conn->consumed_content;
6529 if (buffered_len > 0) {
6530 if (len64 < buffered_len) {
6531 buffered_len = len64;
6532 }
6533 body = conn->buf + conn->request_len + conn->consumed_content;
6534 memcpy(buf, body, (size_t)buffered_len);
6535 len64 -= buffered_len;
6536 conn->consumed_content += buffered_len;
6537 nread += buffered_len;
6538 buf = (char *)buf + buffered_len;
6539 }
6540
6541 /* We have returned all buffered data. Read new data from the remote
6542 * socket.
6543 */
6544 if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) {
6545 conn->consumed_content += n;
6546 nread += n;
6547 } else {
6548 nread = ((nread > 0) ? nread : n);
6549 }
6550 }
6551 return (int)nread;
6552}
static void body(LexState *ls, expdesc *e, int needself, int line)

References body(), mg_connection::buf, mg_connection::consumed_content, mg_connection::content_len, mg_connection::data_len, INT64_MAX, NULL, pull_all(), and mg_connection::request_len.

Referenced by mg_read().

◆ mg_realloc()

static __inline void * mg_realloc ( void * a,
size_t b )
static

Definition at line 1486 of file civetweb.c.

1487{
1488 return realloc(a, b);
1489}
#define realloc
Definition civetweb.c:1541

References realloc.

Referenced by dir_scan_callback().

◆ mg_send_chunk()

CIVETWEB_API int mg_send_chunk ( struct mg_connection * conn,
const char * chunk,
unsigned int chunk_len )

Definition at line 6860 of file civetweb.c.

6863{
6864 char lenbuf[16];
6865 size_t lenbuf_len;
6866 int ret;
6867 int t;
6868
6869 /* First store the length information in a text buffer. */
6870 sprintf(lenbuf, "%x\r\n", chunk_len);
6871 lenbuf_len = strlen(lenbuf);
6872
6873 /* Then send length information, chunk and terminating \r\n. */
6874 ret = mg_write(conn, lenbuf, lenbuf_len);
6875 if (ret != (int)lenbuf_len) {
6876 return -1;
6877 }
6878 t = ret;
6879
6880 ret = mg_write(conn, chunk, chunk_len);
6881 if (ret != (int)chunk_len) {
6882 return -1;
6883 }
6884 t += ret;
6885
6886 ret = mg_write(conn, "\r\n", 2);
6887 if (ret != 2) {
6888 return -1;
6889 }
6890 t += ret;
6891
6892 return t;
6893}
static void chunk(LexState *ls)

References chunk(), and mg_write().

◆ mg_send_digest_access_authentication_request()

CIVETWEB_API int mg_send_digest_access_authentication_request ( struct mg_connection * conn,
const char * realm )

Definition at line 9022 of file civetweb.c.

9024{
9025 if (conn && conn->dom_ctx) {
9026 send_authorization_request(conn, realm);
9027 return 0;
9028 }
9029 return -1;
9030}

References mg_connection::dom_ctx, and send_authorization_request().

◆ mg_send_file()

CIVETWEB_API void mg_send_file ( struct mg_connection * conn,
const char * path )

Definition at line 10510 of file civetweb.c.

10511{
10512 mg_send_mime_file2(conn, path, NULL, NULL);
10513}
CIVETWEB_API void mg_send_mime_file2(struct mg_connection *conn, const char *path, const char *mime_type, const char *additional_headers)
Definition civetweb.c:10526

References mg_send_mime_file2(), and NULL.

◆ mg_send_file_body()

CIVETWEB_API int mg_send_file_body ( struct mg_connection * conn,
const char * path )

Definition at line 10451 of file civetweb.c.

10452{
10453 struct mg_file file = STRUCT_FILE_INITIALIZER;
10454 if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, &file)) {
10455 return -1;
10456 }
10457 fclose_on_exec(&file.access, conn);
10458 send_file_data(conn, &file, 0, INT64_MAX, 0); /* send static file */
10459 (void)mg_fclose(&file.access); /* Ignore errors for readonly files */
10460 return 0; /* >= 0 for OK */
10461}

References mg_file::access, fclose_on_exec(), INT64_MAX, mg_fclose(), mg_fopen(), MG_FOPEN_MODE_READ, send_file_data(), and STRUCT_FILE_INITIALIZER.

◆ mg_send_http_error()

CIVETWEB_API int mg_send_http_error ( struct mg_connection * conn,
int status,
const char * fmt,
... )

Definition at line 4562 of file civetweb.c.

4563{
4564 va_list ap;
4565 int ret;
4566
4567 va_start(ap, fmt);
4568 ret = mg_send_http_error_impl(conn, status, fmt, ap);
4569 va_end(ap);
4570
4571 return ret;
4572}
static int mg_send_http_error_impl(struct mg_connection *conn, int status, const char *fmt, va_list args)
Definition civetweb.c:4375

References mg_send_http_error_impl().

Referenced by dav_lock_file(), dav_mkcol(), dav_move_file(), dav_unlock_file(), delete_file(), forward_body_data(), handle_cgi_request(), handle_directory_request(), handle_file_based_request(), handle_request(), handle_ssi_file_request(), handle_static_file_request(), mg_send_mime_file2(), process_new_connection(), put_file(), redirect_to_https_port(), and send_file_data().

◆ mg_send_http_error_impl()

static int mg_send_http_error_impl ( struct mg_connection * conn,
int status,
const char * fmt,
va_list args )
static

Definition at line 4375 of file civetweb.c.

4379{
4380 char errmsg_buf[MG_BUF_LEN];
4381 va_list ap;
4382 int has_body;
4383
4384#if !defined(NO_FILESYSTEMS)
4385 char path_buf[UTF8_PATH_MAX];
4386 int len, i, page_handler_found, scope, truncated;
4387 const char *error_handler = NULL;
4388 struct mg_file error_page_file = STRUCT_FILE_INITIALIZER;
4389 const char *error_page_file_ext, *tstr;
4390#endif /* NO_FILESYSTEMS */
4391 int handled_by_callback = 0;
4392
4393 if ((conn == NULL) || (fmt == NULL)) {
4394 return -2;
4395 }
4396
4397 /* Set status (for log) */
4398 conn->status_code = status;
4399
4400 /* Errors 1xx, 204 and 304 MUST NOT send a body */
4401 has_body = ((status > 199) && (status != 204) && (status != 304));
4402
4403 /* Prepare message in buf, if required */
4404 if (has_body
4405 || (!conn->in_error_handler
4406 && (conn->phys_ctx->callbacks.http_error != NULL))) {
4407 /* Store error message in errmsg_buf */
4408 va_copy(ap, args);
4409 mg_vsnprintf(conn, NULL, errmsg_buf, sizeof(errmsg_buf), fmt, ap);
4410 va_end(ap);
4411 /* In a debug build, print all html errors */
4412 DEBUG_TRACE("Error %i - [%s]", status, errmsg_buf);
4413 }
4414
4415 /* If there is a http_error callback, call it.
4416 * But don't do it recursively, if callback calls mg_send_http_error again.
4417 */
4418 if (!conn->in_error_handler
4419 && (conn->phys_ctx->callbacks.http_error != NULL)) {
4420 /* Mark in_error_handler to avoid recursion and call user callback. */
4421 conn->in_error_handler = 1;
4422 handled_by_callback =
4423 (conn->phys_ctx->callbacks.http_error(conn, status, errmsg_buf)
4424 == 0);
4425 conn->in_error_handler = 0;
4426 }
4427
4428 if (!handled_by_callback) {
4429 /* Check for recursion */
4430 if (conn->in_error_handler) {
4432 "Recursion when handling error %u - fall back to default",
4433 status);
4434#if !defined(NO_FILESYSTEMS)
4435 } else {
4436 /* Send user defined error pages, if defined */
4437 error_handler = conn->dom_ctx->config[ERROR_PAGES];
4438 error_page_file_ext = conn->dom_ctx->config[INDEX_FILES];
4439 page_handler_found = 0;
4440
4441 if (error_handler != NULL) {
4442 for (scope = 1; (scope <= 3) && !page_handler_found; scope++) {
4443 switch (scope) {
4444 case 1: /* Handler for specific error, e.g. 404 error */
4445 mg_snprintf(conn,
4446 &truncated,
4447 path_buf,
4448 sizeof(path_buf) - 32,
4449 "%serror%03u.",
4450 error_handler,
4451 status);
4452 break;
4453 case 2: /* Handler for error group, e.g., 5xx error
4454 * handler
4455 * for all server errors (500-599) */
4456 mg_snprintf(conn,
4457 &truncated,
4458 path_buf,
4459 sizeof(path_buf) - 32,
4460 "%serror%01uxx.",
4461 error_handler,
4462 status / 100);
4463 break;
4464 default: /* Handler for all errors */
4465 mg_snprintf(conn,
4466 &truncated,
4467 path_buf,
4468 sizeof(path_buf) - 32,
4469 "%serror.",
4470 error_handler);
4471 break;
4472 }
4473
4474 /* String truncation in buf may only occur if
4475 * error_handler is too long. This string is
4476 * from the config, not from a client. */
4477 (void)truncated;
4478
4479 /* The following code is redundant, but it should avoid
4480 * false positives in static source code analyzers and
4481 * vulnerability scanners.
4482 */
4483 path_buf[sizeof(path_buf) - 32] = 0;
4484 len = (int)strlen(path_buf);
4485 if (len > (int)sizeof(path_buf) - 32) {
4486 len = (int)sizeof(path_buf) - 32;
4487 }
4488
4489 /* Start with the file extension from the configuration. */
4490 tstr = strchr(error_page_file_ext, '.');
4491
4492 while (tstr) {
4493 for (i = 1;
4494 (i < 32) && (tstr[i] != 0) && (tstr[i] != ',');
4495 i++) {
4496 /* buffer overrun is not possible here, since
4497 * (i < 32) && (len < sizeof(path_buf) - 32)
4498 * ==> (i + len) < sizeof(path_buf) */
4499 path_buf[len + i - 1] = tstr[i];
4500 }
4501 /* buffer overrun is not possible here, since
4502 * (i <= 32) && (len < sizeof(path_buf) - 32)
4503 * ==> (i + len) <= sizeof(path_buf) */
4504 path_buf[len + i - 1] = 0;
4505
4506 if (mg_stat(conn, path_buf, &error_page_file.stat)) {
4507 DEBUG_TRACE("Check error page %s - found",
4508 path_buf);
4509 page_handler_found = 1;
4510 break;
4511 }
4512 DEBUG_TRACE("Check error page %s - not found",
4513 path_buf);
4514
4515 /* Continue with the next file extension from the
4516 * configuration (if there is a next one). */
4517 tstr = strchr(tstr + i, '.');
4518 }
4519 }
4520 }
4521
4522 if (page_handler_found) {
4523 conn->in_error_handler = 1;
4524 handle_file_based_request(conn, path_buf, &error_page_file);
4525 conn->in_error_handler = 0;
4526 return 0;
4527 }
4528#endif /* NO_FILESYSTEMS */
4529 }
4530
4531 /* No custom error page. Send default error page. */
4532 conn->must_close = 1;
4533 mg_response_header_start(conn, status);
4536 send_cors_header(conn);
4537 if (has_body) {
4539 "Content-Type",
4540 "text/plain; charset=utf-8",
4541 -1);
4542 }
4544
4545 /* HTTP responses 1xx, 204 and 304 MUST NOT send a body */
4546 if (has_body) {
4547 /* For other errors, send a generic error message. */
4548 const char *status_text = mg_get_response_code_text(conn, status);
4549 mg_printf(conn, "Error %d: %s\n", status, status_text);
4550 mg_write(conn, errmsg_buf, strlen(errmsg_buf));
4551
4552 } else {
4553 /* No body allowed. Close the connection. */
4554 DEBUG_TRACE("Error %i", status);
4555 }
4556 }
4557 return 0;
4558}
CIVETWEB_API const char * mg_get_response_code_text(const struct mg_connection *conn, int response_code)
Definition civetweb.c:4176
int(* http_error)(struct mg_connection *conn, int status, const char *errmsg)
Definition civetweb.h:359

References mg_context::callbacks, mg_domain_context::config, DEBUG_TRACE, mg_connection::dom_ctx, ERROR_PAGES, handle_file_based_request(), mg_callbacks::http_error, mg_connection::in_error_handler, INDEX_FILES, vec::len, MG_BUF_LEN, mg_get_response_code_text(), mg_printf(), mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_snprintf(), mg_stat(), mg_vsnprintf(), mg_write(), mg_connection::must_close, NULL, mg_connection::phys_ctx, send_additional_header(), send_cors_header(), send_no_cache_header(), mg_file::stat, mg_connection::status_code, STRUCT_FILE_INITIALIZER, UTF8_PATH_MAX, and va_copy.

Referenced by mg_send_http_error().

◆ mg_send_http_ok()

CIVETWEB_API int mg_send_http_ok ( struct mg_connection * conn,
const char * mime_type,
long long content_length )

Definition at line 4576 of file civetweb.c.

4579{
4580 if ((mime_type == NULL) || (*mime_type == 0)) {
4581 /* No content type defined: default to text/html */
4582 mime_type = "text/html";
4583 }
4584
4585 mg_response_header_start(conn, 200);
4588 send_cors_header(conn);
4589 mg_response_header_add(conn, "Content-Type", mime_type, -1);
4590 if (content_length < 0) {
4591 /* Size not known. Use chunked encoding (HTTP/1.x) */
4592 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
4593 /* Only HTTP/1.x defines "chunked" encoding, HTTP/2 does not*/
4594 mg_response_header_add(conn, "Transfer-Encoding", "chunked", -1);
4595 }
4596 } else {
4597 char len[32];
4598 int trunc = 0;
4599 mg_snprintf(conn,
4600 &trunc,
4601 len,
4602 sizeof(len),
4603 "%" UINT64_FMT,
4604 (uint64_t)content_length);
4605 if (!trunc) {
4606 /* Since 32 bytes is enough to hold any 64 bit decimal number,
4607 * !trunc is always true */
4608 mg_response_header_add(conn, "Content-Length", len, -1);
4609 }
4610 }
4612
4613 return 0;
4614}

References mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_snprintf(), mime_type, NULL, mg_connection::protocol_type, PROTOCOL_TYPE_HTTP1, send_additional_header(), send_cors_header(), send_no_cache_header(), and UINT64_FMT.

◆ mg_send_http_redirect()

CIVETWEB_API int mg_send_http_redirect ( struct mg_connection * conn,
const char * target_url,
int redirect_code )

Definition at line 4618 of file civetweb.c.

4621{
4622 /* Send a 30x redirect response.
4623 *
4624 * Redirect types (status codes):
4625 *
4626 * Status | Perm/Temp | Method | Version
4627 * 301 | permanent | POST->GET undefined | HTTP/1.0
4628 * 302 | temporary | POST->GET undefined | HTTP/1.0
4629 * 303 | temporary | always use GET | HTTP/1.1
4630 * 307 | temporary | always keep method | HTTP/1.1
4631 * 308 | permanent | always keep method | HTTP/1.1
4632 */
4633
4634#if defined(MG_SEND_REDIRECT_BODY)
4635 char redirect_body[MG_BUF_LEN];
4636 size_t content_len = 0;
4637 char content_len_text[32];
4638#endif
4639
4640 /* In case redirect_code=0, use 307. */
4641 if (redirect_code == 0) {
4642 redirect_code = 307;
4643 }
4644
4645 /* In case redirect_code is none of the above, return error. */
4646 if ((redirect_code != 301) && (redirect_code != 302)
4647 && (redirect_code != 303) && (redirect_code != 307)
4648 && (redirect_code != 308)) {
4649 /* Parameter error */
4650 return -2;
4651 }
4652
4653 /* If target_url is not defined, redirect to "/". */
4654 if ((target_url == NULL) || (*target_url == 0)) {
4655 target_url = "/";
4656 }
4657
4658#if defined(MG_SEND_REDIRECT_BODY)
4659 /* TODO: condition name? */
4660
4661 /* Prepare a response body with a hyperlink.
4662 *
4663 * According to RFC2616 (and RFC1945 before):
4664 * Unless the request method was HEAD, the entity of the
4665 * response SHOULD contain a short hypertext note with a hyperlink to
4666 * the new URI(s).
4667 *
4668 * However, this response body is not useful in M2M communication.
4669 * Probably the original reason in the RFC was, clients not supporting
4670 * a 30x HTTP redirect could still show the HTML page and let the user
4671 * press the link. Since current browsers support 30x HTTP, the additional
4672 * HTML body does not seem to make sense anymore.
4673 *
4674 * The new RFC7231 (Section 6.4) does no longer recommend it ("SHOULD"),
4675 * but it only notes:
4676 * The server's response payload usually contains a short
4677 * hypertext note with a hyperlink to the new URI(s).
4678 *
4679 * Deactivated by default. If you need the 30x body, set the define.
4680 */
4682 conn,
4683 NULL /* ignore truncation */,
4684 redirect_body,
4685 sizeof(redirect_body),
4686 "<html><head>%s</head><body><a href=\"%s\">%s</a></body></html>",
4687 redirect_text,
4688 target_url,
4689 target_url);
4690 content_len = strlen(reply);
4691 sprintf(content_len_text, "%lu", (unsigned long)content_len);
4692#endif
4693
4694 /* Send all required headers */
4695 mg_response_header_start(conn, redirect_code);
4696 mg_response_header_add(conn, "Location", target_url, -1);
4697 if ((redirect_code == 301) || (redirect_code == 308)) {
4698 /* Permanent redirect */
4700 } else {
4701 /* Temporary redirect */
4703 }
4705 send_cors_header(conn);
4706#if defined(MG_SEND_REDIRECT_BODY)
4707 mg_response_header_add(conn, "Content-Type", "text/html", -1);
4708 mg_response_header_add(conn, "Content-Length", content_len_text, -1);
4709#else
4710 mg_response_header_add(conn, "Content-Length", "0", 1);
4711#endif
4713
4714#if defined(MG_SEND_REDIRECT_BODY)
4715 /* Send response body */
4716 /* ... unless it is a HEAD request */
4717 if (0 != strcmp(conn->request_info.request_method, "HEAD")) {
4718 ret = mg_write(conn, redirect_body, content_len);
4719 }
4720#endif
4721
4722 return 1;
4723}

References MG_BUF_LEN, mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_snprintf(), mg_write(), NULL, mg_connection::request_info, mg_request_info::request_method, send_additional_header(), send_cors_header(), send_no_cache_header(), and send_static_cache_header().

Referenced by handle_request(), and redirect_to_https_port().

◆ mg_send_mime_file()

CIVETWEB_API void mg_send_mime_file ( struct mg_connection * conn,
const char * path,
const char * mime_type )

Definition at line 10517 of file civetweb.c.

10520{
10521 mg_send_mime_file2(conn, path, mime_type, NULL);
10522}

References mg_send_mime_file2(), mime_type, and NULL.

◆ mg_send_mime_file2()

CIVETWEB_API void mg_send_mime_file2 ( struct mg_connection * conn,
const char * path,
const char * mime_type,
const char * additional_headers )

Definition at line 10526 of file civetweb.c.

10530{
10531 struct mg_file file = STRUCT_FILE_INITIALIZER;
10532
10533 if (!conn) {
10534 /* No conn */
10535 return;
10536 }
10537
10538 if (mg_stat(conn, path, &file.stat)) {
10539#if !defined(NO_CACHING)
10540 if (is_not_modified(conn, &file.stat)) {
10541 /* Send 304 "Not Modified" - this must not send any body data */
10543 } else
10544#endif /* NO_CACHING */
10545 if (file.stat.is_directory) {
10547 "yes")) {
10548 handle_directory_request(conn, path);
10549 } else {
10550 mg_send_http_error(conn,
10551 403,
10552 "%s",
10553 "Error: Directory listing denied");
10554 }
10555 } else {
10557 conn, path, &file, mime_type, additional_headers);
10558 }
10559 } else {
10560 mg_send_http_error(conn, 404, "%s", "Error: File not found");
10561 }
10562}

References mg_domain_context::config, mg_connection::dom_ctx, ENABLE_DIRECTORY_LISTING, handle_directory_request(), handle_not_modified_static_file_request(), handle_static_file_request(), mg_file_stat::is_directory, is_not_modified(), mg_send_http_error(), mg_stat(), mg_strcasecmp(), mime_type, mg_file::stat, and STRUCT_FILE_INITIALIZER.

Referenced by mg_send_file(), and mg_send_mime_file().

◆ mg_set_auth_handler()

CIVETWEB_API void mg_set_auth_handler ( struct mg_context * ctx,
const char * uri,
mg_authorization_handler handler,
void * cbdata )

Definition at line 14538 of file civetweb.c.

14542{
14544 &(ctx->dd),
14545 uri,
14547 handler == NULL,
14548 NULL,
14549 NULL,
14550 NULL,
14551 NULL,
14552 NULL,
14553 NULL,
14554 handler,
14555 cbdata);
14556}
static void mg_set_handler_type(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx, const char *uri, int handler_type, int is_delete_request, mg_request_handler handler, struct mg_websocket_subprotocols *subprotocols, mg_websocket_connect_handler connect_handler, mg_websocket_ready_handler ready_handler, mg_websocket_data_handler data_handler, mg_websocket_close_handler close_handler, mg_authorization_handler auth_handler, void *cbdata)
Definition civetweb.c:14280

References AUTH_HANDLER, mg_handler_info::cbdata, mg_context::dd, mg_handler_info::handler, mg_set_handler_type(), NULL, and mg_handler_info::uri.

◆ mg_set_handler_type()

static void mg_set_handler_type ( struct mg_context * phys_ctx,
struct mg_domain_context * dom_ctx,
const char * uri,
int handler_type,
int is_delete_request,
mg_request_handler handler,
struct mg_websocket_subprotocols * subprotocols,
mg_websocket_connect_handler connect_handler,
mg_websocket_ready_handler ready_handler,
mg_websocket_data_handler data_handler,
mg_websocket_close_handler close_handler,
mg_authorization_handler auth_handler,
void * cbdata )
static

Definition at line 14280 of file civetweb.c.

14293{
14294 struct mg_handler_info *tmp_rh, **lastref;
14295 size_t urilen = strlen(uri);
14296
14299 DEBUG_ASSERT(is_delete_request || connect_handler != NULL
14301 || close_handler != NULL);
14302
14304 if (handler != NULL) {
14305 return;
14306 }
14307 if (!is_delete_request && (connect_handler == NULL)
14308 && (ready_handler == NULL) && (data_handler == NULL)
14309 && (close_handler == NULL)) {
14310 return;
14311 }
14312 if (auth_handler != NULL) {
14313 return;
14314 }
14315
14316 } else if (handler_type == REQUEST_HANDLER) {
14318 && data_handler == NULL && close_handler == NULL);
14319 DEBUG_ASSERT(is_delete_request || (handler != NULL));
14321
14322 if ((connect_handler != NULL) || (ready_handler != NULL)
14323 || (data_handler != NULL) || (close_handler != NULL)) {
14324 return;
14325 }
14326 if (!is_delete_request && (handler == NULL)) {
14327 return;
14328 }
14329 if (auth_handler != NULL) {
14330 return;
14331 }
14332
14333 } else if (handler_type == AUTH_HANDLER) {
14336 && data_handler == NULL && close_handler == NULL);
14337 DEBUG_ASSERT(is_delete_request || (auth_handler != NULL));
14338 if (handler != NULL) {
14339 return;
14340 }
14341 if ((connect_handler != NULL) || (ready_handler != NULL)
14342 || (data_handler != NULL) || (close_handler != NULL)) {
14343 return;
14344 }
14345 if (!is_delete_request && (auth_handler == NULL)) {
14346 return;
14347 }
14348 } else {
14349 /* Unknown handler type. */
14350 return;
14351 }
14352
14353 if (!phys_ctx || !dom_ctx) {
14354 /* no context available */
14355 return;
14356 }
14357
14358 mg_lock_context(phys_ctx);
14359
14360 /* first try to find an existing handler */
14361 do {
14362 lastref = &(dom_ctx->handlers);
14363 for (tmp_rh = dom_ctx->handlers; tmp_rh != NULL;
14364 tmp_rh = tmp_rh->next) {
14365 if (tmp_rh->handler_type == handler_type
14366 && (urilen == tmp_rh->uri_len) && !strcmp(tmp_rh->uri, uri)) {
14367 if (!is_delete_request) {
14368 /* update existing handler */
14370 /* Wait for end of use before updating */
14371 if (tmp_rh->refcount) {
14372 mg_unlock_context(phys_ctx);
14373 mg_sleep(1);
14374 mg_lock_context(phys_ctx);
14375 /* tmp_rh might have been freed, search again. */
14376 break;
14377 }
14378 /* Ok, the handler is no more use -> Update it */
14379 tmp_rh->handler = handler;
14380 } else if (handler_type == WEBSOCKET_HANDLER) {
14381 tmp_rh->subprotocols = subprotocols;
14383 tmp_rh->ready_handler = ready_handler;
14384 tmp_rh->data_handler = data_handler;
14385 tmp_rh->close_handler = close_handler;
14386 } else { /* AUTH_HANDLER */
14387 tmp_rh->auth_handler = auth_handler;
14388 }
14389 tmp_rh->cbdata = cbdata;
14390 } else {
14391 /* remove existing handler */
14393 /* Wait for end of use before removing */
14394 if (tmp_rh->refcount) {
14395 tmp_rh->removing = 1;
14396 mg_unlock_context(phys_ctx);
14397 mg_sleep(1);
14398 mg_lock_context(phys_ctx);
14399 /* tmp_rh might have been freed, search again. */
14400 break;
14401 }
14402 /* Ok, the handler is no more used */
14403 }
14404 *lastref = tmp_rh->next;
14405 mg_free(tmp_rh->uri);
14406 mg_free(tmp_rh);
14407 }
14408 mg_unlock_context(phys_ctx);
14409 return;
14410 }
14411 lastref = &(tmp_rh->next);
14412 }
14413 } while (tmp_rh != NULL);
14414
14415 if (is_delete_request) {
14416 /* no handler to set, this was a remove request to a non-existing
14417 * handler */
14418 mg_unlock_context(phys_ctx);
14419 return;
14420 }
14421
14422 tmp_rh =
14423 (struct mg_handler_info *)mg_calloc_ctx(1,
14424 sizeof(struct mg_handler_info),
14425 phys_ctx);
14426 if (tmp_rh == NULL) {
14427 mg_unlock_context(phys_ctx);
14428 mg_cry_ctx_internal(phys_ctx,
14429 "%s",
14430 "Cannot create new request handler struct, OOM");
14431 return;
14432 }
14433 tmp_rh->uri = mg_strdup_ctx(uri, phys_ctx);
14434 if (!tmp_rh->uri) {
14435 mg_unlock_context(phys_ctx);
14436 mg_free(tmp_rh);
14437 mg_cry_ctx_internal(phys_ctx,
14438 "%s",
14439 "Cannot create new request handler struct, OOM");
14440 return;
14441 }
14442 tmp_rh->uri_len = urilen;
14444 tmp_rh->refcount = 0;
14445 tmp_rh->removing = 0;
14446 tmp_rh->handler = handler;
14447 } else if (handler_type == WEBSOCKET_HANDLER) {
14448 tmp_rh->subprotocols = subprotocols;
14450 tmp_rh->ready_handler = ready_handler;
14451 tmp_rh->data_handler = data_handler;
14452 tmp_rh->close_handler = close_handler;
14453 } else { /* AUTH_HANDLER */
14454 tmp_rh->auth_handler = auth_handler;
14455 }
14456 tmp_rh->cbdata = cbdata;
14457 tmp_rh->handler_type = handler_type;
14458 tmp_rh->next = NULL;
14459
14460 *lastref = tmp_rh;
14461 mg_unlock_context(phys_ctx);
14462}
#define mg_sleep(x)
Definition civetweb.c:920

References AUTH_HANDLER, mg_handler_info::auth_handler, mg_handler_info::cbdata, mg_handler_info::close_handler, mg_handler_info::connect_handler, mg_handler_info::data_handler, DEBUG_ASSERT, mg_handler_info::handler, mg_handler_info::handler_type, mg_domain_context::handlers, mg_calloc_ctx, mg_cry_ctx_internal, mg_free(), mg_lock_context(), mg_sleep, mg_strdup_ctx(), mg_unlock_context(), mg_handler_info::next, NULL, mg_handler_info::ready_handler, mg_handler_info::refcount, mg_handler_info::removing, REQUEST_HANDLER, mg_handler_info::subprotocols, mg_handler_info::uri, mg_handler_info::uri_len, and WEBSOCKET_HANDLER.

Referenced by mg_set_auth_handler(), mg_set_request_handler(), and mg_set_websocket_handler_with_subprotocols().

◆ mg_set_request_handler()

CIVETWEB_API void mg_set_request_handler ( struct mg_context * ctx,
const char * uri,
mg_request_handler handler,
void * cbdata )

Definition at line 14466 of file civetweb.c.

14470{
14472 &(ctx->dd),
14473 uri,
14475 handler == NULL,
14476 handler,
14477 NULL,
14478 NULL,
14479 NULL,
14480 NULL,
14481 NULL,
14482 NULL,
14483 cbdata);
14484}

References mg_handler_info::cbdata, mg_context::dd, mg_handler_info::handler, mg_set_handler_type(), NULL, REQUEST_HANDLER, and mg_handler_info::uri.

Referenced by start_http_server().

◆ mg_set_thread_name()

static void static void mg_set_thread_name ( const char * name)
static

Definition at line 2780 of file civetweb.c.

2781{
2782 char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */
2783
2785 NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name);
2786
2787#if defined(_WIN32)
2788#if defined(_MSC_VER)
2789 /* Windows and Visual Studio Compiler */
2790 __try {
2791 THREADNAME_INFO info;
2792 info.dwType = 0x1000;
2793 info.szName = threadName;
2794 info.dwThreadID = ~0U;
2795 info.dwFlags = 0;
2796
2797 RaiseException(0x406D1388,
2798 0,
2799 sizeof(info) / sizeof(ULONG_PTR),
2800 (ULONG_PTR *)&info);
2801 } __except (EXCEPTION_EXECUTE_HANDLER) {
2802 }
2803#elif defined(__MINGW32__)
2804 /* No option known to set thread name for MinGW known */
2805#endif
2806#elif defined(_GNU_SOURCE) && defined(__GLIBC__) \
2807 && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
2808 /* pthread_setname_np first appeared in glibc in version 2.12 */
2809#if defined(__MACH__) && defined(__APPLE__)
2810 /* OS X only current thread name can be changed */
2811 (void)pthread_setname_np(threadName);
2812#else
2813 (void)pthread_setname_np(pthread_self(), threadName);
2814#endif
2815#elif defined(__linux__)
2816 /* On Linux we can use the prctl function.
2817 * When building for Linux Standard Base (LSB) use
2818 * NO_THREAD_NAME. However, thread names are a big
2819 * help for debugging, so the stadard is to set them.
2820 */
2821 (void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
2822#endif
2823}

References mg_snprintf(), name, and NULL.

Referenced by master_thread_run(), and worker_thread_run().

◆ mg_set_user_connection_data()

CIVETWEB_API void mg_set_user_connection_data ( const struct mg_connection * const_conn,
void * data )

Definition at line 3225 of file civetweb.c.

3226{
3227 if (const_conn != NULL) {
3228 /* Const cast, since "const struct mg_connection *" does not mean
3229 * the connection object is not modified. Here "const" is used,
3230 * to indicate mg_read/mg_write/mg_send/.. must not be called. */
3231 struct mg_connection *conn = (struct mg_connection *)const_conn;
3232 conn->request_info.conn_data = data;
3233 }
3234}

References mg_request_info::conn_data, NULL, and mg_connection::request_info.

Referenced by close_connection(), and init_connection().

◆ mg_set_websocket_handler()

CIVETWEB_API void mg_set_websocket_handler ( struct mg_context * ctx,
const char * uri,
mg_websocket_connect_handler connect_handler,
mg_websocket_ready_handler ready_handler,
mg_websocket_data_handler data_handler,
mg_websocket_close_handler close_handler,
void * cbdata )

Definition at line 14488 of file civetweb.c.

14495{
14497 uri,
14498 NULL,
14499 connect_handler,
14500 ready_handler,
14501 data_handler,
14502 close_handler,
14503 cbdata);
14504}
CIVETWEB_API void mg_set_websocket_handler_with_subprotocols(struct mg_context *ctx, const char *uri, struct mg_websocket_subprotocols *subprotocols, mg_websocket_connect_handler connect_handler, mg_websocket_ready_handler ready_handler, mg_websocket_data_handler data_handler, mg_websocket_close_handler close_handler, void *cbdata)
Definition civetweb.c:14508

References mg_handler_info::cbdata, mg_handler_info::close_handler, mg_handler_info::connect_handler, mg_handler_info::data_handler, mg_set_websocket_handler_with_subprotocols(), NULL, mg_handler_info::ready_handler, and mg_handler_info::uri.

◆ mg_set_websocket_handler_with_subprotocols()

CIVETWEB_API void mg_set_websocket_handler_with_subprotocols ( struct mg_context * ctx,
const char * uri,
struct mg_websocket_subprotocols * subprotocols,
mg_websocket_connect_handler connect_handler,
mg_websocket_ready_handler ready_handler,
mg_websocket_data_handler data_handler,
mg_websocket_close_handler close_handler,
void * cbdata )

Definition at line 14508 of file civetweb.c.

14517{
14518 int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL)
14519 && (data_handler == NULL)
14520 && (close_handler == NULL);
14522 &(ctx->dd),
14523 uri,
14525 is_delete_request,
14526 NULL,
14527 subprotocols,
14528 connect_handler,
14529 ready_handler,
14530 data_handler,
14531 close_handler,
14532 NULL,
14533 cbdata);
14534}

References mg_handler_info::cbdata, mg_handler_info::close_handler, mg_handler_info::connect_handler, mg_handler_info::data_handler, mg_context::dd, mg_set_handler_type(), NULL, mg_handler_info::ready_handler, mg_handler_info::subprotocols, mg_handler_info::uri, and WEBSOCKET_HANDLER.

Referenced by mg_set_websocket_handler().

◆ mg_snprintf() [1/2]

static void mg_snprintf ( const struct mg_connection * conn,
int * truncated,
char * buf,
size_t buflen,
const char * fmt,
... )
static

Definition at line 3143 of file civetweb.c.

3149{
3150 va_list ap;
3151
3152 va_start(ap, fmt);
3153 mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap);
3154 va_end(ap);
3155}

References mg_vsnprintf().

◆ mg_snprintf() [2/2]

◆ mg_split_form_urlencoded()

CIVETWEB_API int mg_split_form_urlencoded ( char * data,
struct mg_header * form_fields,
unsigned num_form_fields )

Definition at line 7157 of file civetweb.c.

7160{
7161 char *b;
7162 int i;
7163 int num = 0;
7164
7165 if (data == NULL) {
7166 /* parameter error */
7167 return -1;
7168 }
7169
7170 if ((form_fields == NULL) && (num_form_fields == 0)) {
7171 /* determine the number of expected fields */
7172 if (data[0] == 0) {
7173 return 0;
7174 }
7175 /* count number of & to return the number of key-value-pairs */
7176 num = 1;
7177 while (*data) {
7178 if (*data == '&') {
7179 num++;
7180 }
7181 data++;
7182 }
7183 return num;
7184 }
7185
7186 if ((form_fields == NULL) || ((int)num_form_fields <= 0)) {
7187 /* parameter error */
7188 return -1;
7189 }
7190
7191 for (i = 0; i < (int)num_form_fields; i++) {
7192 /* extract key-value pairs from input data */
7193 while ((*data == ' ') || (*data == '\t')) {
7194 /* skip initial spaces */
7195 data++;
7196 }
7197 if (*data == 0) {
7198 /* end of string reached */
7199 break;
7200 }
7201 form_fields[num].name = data;
7202
7203 /* find & or = */
7204 b = data;
7205 while ((*b != 0) && (*b != '&') && (*b != '=')) {
7206 b++;
7207 }
7208
7209 if (*b == 0) {
7210 /* last key without value */
7211 form_fields[num].value = NULL;
7212 } else if (*b == '&') {
7213 /* mid key without value */
7214 form_fields[num].value = NULL;
7215 } else {
7216 /* terminate string */
7217 *b = 0;
7218 /* value starts after '=' */
7219 data = b + 1;
7220 form_fields[num].value = data;
7221 }
7222
7223 /* new field is stored */
7224 num++;
7225
7226 /* find a next key */
7227 b = strchr(data, '&');
7228 if (b == 0) {
7229 /* no more data */
7230 break;
7231 } else {
7232 /* terminate value of last field at '&' */
7233 *b = 0;
7234 /* next key-value-pairs starts after '&' */
7235 data = b + 1;
7236 }
7237 }
7238
7239 /* Decode all values */
7240 for (i = 0; i < num; i++) {
7241 if (form_fields[i].name) {
7242 url_decode_in_place((char *)form_fields[i].name);
7243 }
7244 if (form_fields[i].value) {
7245 url_decode_in_place((char *)form_fields[i].value);
7246 }
7247 }
7248
7249 /* return number of fields found */
7250 return num;
7251}
int value
Definition lsqlite3.c:2155

References mg_header::name, name, NULL, url_decode_in_place(), mg_header::value, and value.

◆ mg_start()

CIVETWEB_API struct mg_context * mg_start ( const struct mg_callbacks * callbacks,
void * user_data,
const char ** options )

Definition at line 21205 of file civetweb.c.

21208{
21209 struct mg_init_data init = {0};
21210 init.callbacks = callbacks;
21211 init.user_data = user_data;
21212 init.configuration_options = options;
21213
21214 return mg_start2(&init, NULL);
21215}
CIVETWEB_API struct mg_context * mg_start2(struct mg_init_data *init, struct mg_error_data *error)
Definition civetweb.c:20464
void * user_data
Definition civetweb.h:1800
const struct mg_callbacks * callbacks
Definition civetweb.h:1799
const char ** configuration_options
Definition civetweb.h:1801

References mg_init_data::callbacks, mg_init_data::configuration_options, mg_start2(), NULL, and mg_init_data::user_data.

Referenced by start_http_server().

◆ mg_start2()

CIVETWEB_API struct mg_context * mg_start2 ( struct mg_init_data * init,
struct mg_error_data * error )

Definition at line 20464 of file civetweb.c.

20465{
20466 struct mg_context *ctx;
20467 const char *name, *value, *default_value;
20468 int idx, ok, workerthreadcount;
20469 unsigned int i;
20470 int itmp;
20471 void (*exit_callback)(const struct mg_context *ctx) = 0;
20472 const char **options =
20473 ((init != NULL) ? (init->configuration_options) : (NULL));
20474
20475 struct mg_workerTLS tls;
20476
20477 if (error != NULL) {
20479 error->code_sub = 0;
20480 if (error->text_buffer_size > 0) {
20481 *error->text = 0;
20482 }
20483 }
20484
20485 if (mg_init_library_called == 0) {
20486 /* Legacy INIT, if mg_start is called without mg_init_library.
20487 * Note: This will cause a memory leak when unloading the library.
20488 */
20489 legacy_init(options);
20490 }
20491 if (mg_init_library_called == 0) {
20492 if (error != NULL) {
20495 NULL, /* No truncation check for error buffers */
20496 error->text,
20497 error->text_buffer_size,
20498 "%s",
20499 "Library uninitialized");
20500 }
20501 return NULL;
20502 }
20503
20504 /* Allocate context and initialize reasonable general case defaults. */
20505 ctx = (struct mg_context *)mg_calloc(1, sizeof(*ctx));
20506 if (ctx == NULL) {
20507 if (error != NULL) {
20509 error->code_sub = (unsigned)sizeof(*ctx);
20511 NULL, /* No truncation check for error buffers */
20512 error->text,
20513 error->text_buffer_size,
20514 "%s",
20515 "Out of memory");
20516 }
20517 return NULL;
20518 }
20519
20520 /* Random number generator will initialize at the first call */
20521 ctx->dd.auth_nonce_mask =
20522 (uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options);
20523
20524 /* Save started thread index to reuse in other external API calls
20525 * For the sake of thread synchronization all non-civetweb threads
20526 * can be considered as single external thread */
20528 tls.is_master = -1; /* Thread calling mg_start */
20529 tls.thread_idx = ctx->starter_thread_idx;
20530#if defined(_WIN32)
20531 tls.pthread_cond_helper_mutex = NULL;
20532#endif
20533 pthread_setspecific(sTlsKey, &tls);
20534
20535 ok = (0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr));
20536#if !defined(ALTERNATIVE_QUEUE)
20537 ok &= (0 == pthread_cond_init(&ctx->sq_empty, NULL));
20538 ok &= (0 == pthread_cond_init(&ctx->sq_full, NULL));
20539 ctx->sq_blocked = 0;
20540#endif
20541 ok &= (0 == pthread_mutex_init(&ctx->nonce_mutex, &pthread_mutex_attr));
20542#if defined(USE_LUA)
20543 ok &= (0 == pthread_mutex_init(&ctx->lua_bg_mutex, &pthread_mutex_attr));
20544#endif
20545 if (!ok) {
20546 unsigned error_id = (unsigned)ERRNO;
20547 const char *err_msg =
20548 "Cannot initialize thread synchronization objects";
20549 /* Fatal error - abort start. However, this situation should never
20550 * occur in practice. */
20551
20552 mg_cry_ctx_internal(ctx, "%s", err_msg);
20553 if (error != NULL) {
20555 error->code_sub = error_id;
20557 NULL, /* No truncation check for error buffers */
20558 error->text,
20559 error->text_buffer_size,
20560 "%s",
20561 err_msg);
20562 }
20563
20564 mg_free(ctx);
20565 pthread_setspecific(sTlsKey, NULL);
20566 return NULL;
20567 }
20568
20569 if ((init != NULL) && (init->callbacks != NULL)) {
20570 /* Set all callbacks except exit_context. */
20571 ctx->callbacks = *init->callbacks;
20572 exit_callback = init->callbacks->exit_context;
20573 /* The exit callback is activated once the context is successfully
20574 * created. It should not be called, if an incomplete context object
20575 * is deleted during a failed initialization. */
20576 ctx->callbacks.exit_context = 0;
20577 }
20578 ctx->user_data = ((init != NULL) ? (init->user_data) : (NULL));
20579 ctx->dd.handlers = NULL;
20580 ctx->dd.next = NULL;
20581
20582#if defined(USE_LUA)
20583 lua_ctx_init(ctx);
20584#endif
20585
20586 /* Store options */
20587 while (options && (name = *options++) != NULL) {
20588 idx = get_option_index(name);
20589 if (idx == -1) {
20590 mg_cry_ctx_internal(ctx, "Invalid option: %s", name);
20591 if (error != NULL) {
20593 error->code_sub = (unsigned)-1;
20595 NULL, /* No truncation check for error buffers */
20596 error->text,
20597 error->text_buffer_size,
20598 "Invalid configuration option: %s",
20599 name);
20600 }
20601
20602 free_context(ctx);
20603 pthread_setspecific(sTlsKey, NULL);
20604 return NULL;
20605
20606 } else if ((value = *options++) == NULL) {
20607 mg_cry_ctx_internal(ctx, "%s: option value cannot be NULL", name);
20608 if (error != NULL) {
20610 error->code_sub = (unsigned)idx;
20612 NULL, /* No truncation check for error buffers */
20613 error->text,
20614 error->text_buffer_size,
20615 "Invalid configuration option value: %s",
20616 name);
20617 }
20618
20619 free_context(ctx);
20620 pthread_setspecific(sTlsKey, NULL);
20621 return NULL;
20622 }
20623 if (ctx->dd.config[idx] != NULL) {
20624 /* A duplicate configuration option is not an error - the last
20625 * option value will be used. */
20626 mg_cry_ctx_internal(ctx, "warning: %s: duplicate option", name);
20627 mg_free(ctx->dd.config[idx]);
20628 }
20629 ctx->dd.config[idx] = mg_strdup_ctx(value, ctx);
20630 DEBUG_TRACE("[%s] -> [%s]", name, value);
20631 }
20632
20633 /* Set default value if needed */
20634 for (i = 0; config_options[i].name != NULL; i++) {
20635 default_value = config_options[i].default_value;
20636 if ((ctx->dd.config[i] == NULL) && (default_value != NULL)) {
20637 ctx->dd.config[i] = mg_strdup_ctx(default_value, ctx);
20638 }
20639 }
20640
20641 /* Request size option */
20642 itmp = atoi(ctx->dd.config[MAX_REQUEST_SIZE]);
20643 if (itmp < 1024) {
20645 "%s too small",
20647 if (error != NULL) {
20649 error->code_sub = (unsigned)MAX_REQUEST_SIZE;
20651 NULL, /* No truncation check for error buffers */
20652 error->text,
20653 error->text_buffer_size,
20654 "Invalid configuration option value: %s",
20656 }
20657
20658 free_context(ctx);
20659 pthread_setspecific(sTlsKey, NULL);
20660 return NULL;
20661 }
20662 ctx->max_request_size = (unsigned)itmp;
20663
20664 /* Queue length */
20665#if !defined(ALTERNATIVE_QUEUE)
20666 itmp = atoi(ctx->dd.config[CONNECTION_QUEUE_SIZE]);
20667 if (itmp < 1) {
20669 "%s too small",
20671 if (error != NULL) {
20673 error->code_sub = CONNECTION_QUEUE_SIZE;
20675 NULL, /* No truncation check for error buffers */
20676 error->text,
20677 error->text_buffer_size,
20678 "Invalid configuration option value: %s",
20680 }
20681
20682 free_context(ctx);
20683 pthread_setspecific(sTlsKey, NULL);
20684 return NULL;
20685 }
20686 ctx->squeue =
20687 (struct socket *)mg_calloc((unsigned int)itmp, sizeof(struct socket));
20688 if (ctx->squeue == NULL) {
20690 "Out of memory: Cannot allocate %s",
20692 if (error != NULL) {
20694 error->code_sub = (unsigned)itmp * (unsigned)sizeof(struct socket);
20696 NULL, /* No truncation check for error buffers */
20697 error->text,
20698 error->text_buffer_size,
20699 "Out of memory: Cannot allocate %s",
20701 }
20702
20703 free_context(ctx);
20704 pthread_setspecific(sTlsKey, NULL);
20705 return NULL;
20706 }
20707 ctx->sq_size = itmp;
20708#endif
20709
20710 /* Worker thread count option */
20711 workerthreadcount = atoi(ctx->dd.config[NUM_THREADS]);
20712
20713 if ((workerthreadcount > MAX_WORKER_THREADS) || (workerthreadcount <= 0)) {
20714 if (workerthreadcount <= 0) {
20715 mg_cry_ctx_internal(ctx, "%s", "Invalid number of worker threads");
20716 } else {
20717 mg_cry_ctx_internal(ctx, "%s", "Too many worker threads");
20718 }
20719 if (error != NULL) {
20721 error->code_sub = NUM_THREADS;
20723 NULL, /* No truncation check for error buffers */
20724 error->text,
20725 error->text_buffer_size,
20726 "Invalid configuration option value: %s",
20728 }
20729
20730 free_context(ctx);
20731 pthread_setspecific(sTlsKey, NULL);
20732 return NULL;
20733 }
20734
20735 /* Document root */
20736#if defined(NO_FILES)
20737 if (ctx->dd.config[DOCUMENT_ROOT] != NULL) {
20738 mg_cry_ctx_internal(ctx, "%s", "Document root must not be set");
20739 if (error != NULL) {
20741 error->code_sub = (unsigned)DOCUMENT_ROOT;
20743 NULL, /* No truncation check for error buffers */
20744 error->text,
20745 error->text_buffer_size,
20746 "Invalid configuration option value: %s",
20748 }
20749
20750 free_context(ctx);
20751 pthread_setspecific(sTlsKey, NULL);
20752 return NULL;
20753 }
20754#endif
20755
20757
20758#if defined(USE_LUA)
20759 /* If a Lua background script has been configured, start it. */
20760 ctx->lua_bg_log_available = 0;
20761 if (ctx->dd.config[LUA_BACKGROUND_SCRIPT] != NULL) {
20762 char ebuf[256];
20763 struct vec opt_vec;
20764 struct vec eq_vec;
20765 const char *sparams;
20766
20767 memset(ebuf, 0, sizeof(ebuf));
20768 pthread_mutex_lock(&ctx->lua_bg_mutex);
20769
20770 /* Create a Lua state, load all standard libraries and the mg table */
20771 lua_State *state = mg_lua_context_script_prepare(
20772 ctx->dd.config[LUA_BACKGROUND_SCRIPT], ctx, ebuf, sizeof(ebuf));
20773 if (!state) {
20775 "lua_background_script load error: %s",
20776 ebuf);
20777 if (error != NULL) {
20780 NULL, /* No truncation check for error buffers */
20781 error->text,
20782 error->text_buffer_size,
20783 "Error in script %s: %s",
20784 config_options[LUA_BACKGROUND_SCRIPT].name,
20785 ebuf);
20786 }
20787
20788 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20789
20790 free_context(ctx);
20791 pthread_setspecific(sTlsKey, NULL);
20792 return NULL;
20793 }
20794
20795 /* Add a table with parameters into mg.params */
20796 sparams = ctx->dd.config[LUA_BACKGROUND_SCRIPT_PARAMS];
20797 if (sparams && sparams[0]) {
20798 lua_getglobal(state, "mg");
20799 lua_pushstring(state, "params");
20800 lua_newtable(state);
20801
20802 while ((sparams = next_option(sparams, &opt_vec, &eq_vec))
20803 != NULL) {
20804 reg_llstring(
20805 state, opt_vec.ptr, opt_vec.len, eq_vec.ptr, eq_vec.len);
20806 if (mg_strncasecmp(sparams, opt_vec.ptr, opt_vec.len) == 0)
20807 break;
20808 }
20809 lua_rawset(state, -3);
20810 lua_pop(state, 1);
20811 }
20812
20813 /* Call script */
20814 state = mg_lua_context_script_run(state,
20815 ctx->dd.config[LUA_BACKGROUND_SCRIPT],
20816 ctx,
20817 ebuf,
20818 sizeof(ebuf));
20819 if (!state) {
20821 "lua_background_script start error: %s",
20822 ebuf);
20823 if (error != NULL) {
20826 NULL, /* No truncation check for error buffers */
20827 error->text,
20828 error->text_buffer_size,
20829 "Error in script %s: %s",
20831 ebuf);
20832 }
20833 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20834
20835 free_context(ctx);
20836 pthread_setspecific(sTlsKey, NULL);
20837 return NULL;
20838 }
20839
20840 /* state remains valid */
20841 ctx->lua_background_state = (void *)state;
20842 pthread_mutex_unlock(&ctx->lua_bg_mutex);
20843
20844 } else {
20845 ctx->lua_background_state = 0;
20846 }
20847#endif
20848
20849 /* Step by step initialization of ctx - depending on build options */
20850#if !defined(NO_FILESYSTEMS)
20851 if (!set_gpass_option(ctx, NULL)) {
20852 const char *err_msg = "Invalid global password file";
20853 /* Fatal error - abort start. */
20854 mg_cry_ctx_internal(ctx, "%s", err_msg);
20855
20856 if (error != NULL) {
20859 NULL, /* No truncation check for error buffers */
20860 error->text,
20861 error->text_buffer_size,
20862 "%s",
20863 err_msg);
20864 }
20865 free_context(ctx);
20866 pthread_setspecific(sTlsKey, NULL);
20867 return NULL;
20868 }
20869#endif
20870
20871#if defined(USE_MBEDTLS)
20872 if (!mg_sslctx_init(ctx, NULL)) {
20873 const char *err_msg = "Error initializing SSL context";
20874 /* Fatal error - abort start. */
20875 mg_cry_ctx_internal(ctx, "%s", err_msg);
20876
20877 if (error != NULL) {
20880 NULL, /* No truncation check for error buffers */
20881 error->text,
20882 error->text_buffer_size,
20883 "%s",
20884 err_msg);
20885 }
20886
20887 free_context(ctx);
20888 pthread_setspecific(sTlsKey, NULL);
20889 return NULL;
20890 }
20891
20892#elif !defined(NO_SSL)
20893 if (!init_ssl_ctx(ctx, NULL)) {
20894 const char *err_msg = "Error initializing SSL context";
20895 /* Fatal error - abort start. */
20896 mg_cry_ctx_internal(ctx, "%s", err_msg);
20897
20898 if (error != NULL) {
20901 NULL, /* No truncation check for error buffers */
20902 error->text,
20903 error->text_buffer_size,
20904 "%s",
20905 err_msg);
20906 }
20907
20908 free_context(ctx);
20909 pthread_setspecific(sTlsKey, NULL);
20910 return NULL;
20911 }
20912#endif
20913
20914 if (!set_ports_option(ctx)) {
20915 const char *err_msg = "Failed to setup server ports";
20916 /* Fatal error - abort start. */
20917 mg_cry_ctx_internal(ctx, "%s", err_msg);
20918
20919 if (error != NULL) {
20922 NULL, /* No truncation check for error buffers */
20923 error->text,
20924 error->text_buffer_size,
20925 "%s",
20926 err_msg);
20927 }
20928
20929 free_context(ctx);
20930 pthread_setspecific(sTlsKey, NULL);
20931 return NULL;
20932 }
20933
20934#if !defined(_WIN32) && !defined(__ZEPHYR__)
20935 if (!set_uid_option(ctx)) {
20936 const char *err_msg = "Failed to run as configured user";
20937 /* Fatal error - abort start. */
20938 mg_cry_ctx_internal(ctx, "%s", err_msg);
20939
20940 if (error != NULL) {
20943 NULL, /* No truncation check for error buffers */
20944 error->text,
20945 error->text_buffer_size,
20946 "%s",
20947 err_msg);
20948 }
20949
20950 free_context(ctx);
20951 pthread_setspecific(sTlsKey, NULL);
20952 return NULL;
20953 }
20954#endif
20955
20956 if (!set_acl_option(ctx)) {
20957 const char *err_msg = "Failed to setup access control list";
20958 /* Fatal error - abort start. */
20959 mg_cry_ctx_internal(ctx, "%s", err_msg);
20960
20961 if (error != NULL) {
20964 NULL, /* No truncation check for error buffers */
20965 error->text,
20966 error->text_buffer_size,
20967 "%s",
20968 err_msg);
20969 }
20970
20971 free_context(ctx);
20972 pthread_setspecific(sTlsKey, NULL);
20973 return NULL;
20974 }
20975
20976 ctx->cfg_worker_threads = ((unsigned int)(workerthreadcount));
20977 ctx->worker_threadids = (pthread_t *)mg_calloc_ctx(ctx->cfg_worker_threads,
20978 sizeof(pthread_t),
20979 ctx);
20980
20981 if (ctx->worker_threadids == NULL) {
20982 const char *err_msg = "Not enough memory for worker thread ID array";
20983 mg_cry_ctx_internal(ctx, "%s", err_msg);
20984
20985 if (error != NULL) {
20987 error->code_sub =
20988 (unsigned)ctx->cfg_worker_threads * (unsigned)sizeof(pthread_t);
20990 NULL, /* No truncation check for error buffers */
20991 error->text,
20992 error->text_buffer_size,
20993 "%s",
20994 err_msg);
20995 }
20996
20997 free_context(ctx);
20998 pthread_setspecific(sTlsKey, NULL);
20999 return NULL;
21000 }
21001 ctx->worker_connections =
21003 sizeof(struct mg_connection),
21004 ctx);
21005 if (ctx->worker_connections == NULL) {
21006 const char *err_msg =
21007 "Not enough memory for worker thread connection array";
21008 mg_cry_ctx_internal(ctx, "%s", err_msg);
21009
21010 if (error != NULL) {
21012 error->code_sub = (unsigned)ctx->cfg_worker_threads
21013 * (unsigned)sizeof(struct mg_connection);
21015 NULL, /* No truncation check for error buffers */
21016 error->text,
21017 error->text_buffer_size,
21018 "%s",
21019 err_msg);
21020 }
21021
21022 free_context(ctx);
21023 pthread_setspecific(sTlsKey, NULL);
21024 return NULL;
21025 }
21026
21027#if defined(ALTERNATIVE_QUEUE)
21028 ctx->client_wait_events =
21029 (void **)mg_calloc_ctx(ctx->cfg_worker_threads,
21030 sizeof(ctx->client_wait_events[0]),
21031 ctx);
21032 if (ctx->client_wait_events == NULL) {
21033 const char *err_msg = "Not enough memory for worker event array";
21034 mg_cry_ctx_internal(ctx, "%s", err_msg);
21036
21037 if (error != NULL) {
21039 error->code_sub = (unsigned)ctx->cfg_worker_threads
21040 * (unsigned)sizeof(ctx->client_wait_events[0]);
21042 NULL, /* No truncation check for error buffers */
21043 error->text,
21044 error->text_buffer_size,
21045 "%s",
21046 err_msg);
21047 }
21048
21049 free_context(ctx);
21050 pthread_setspecific(sTlsKey, NULL);
21051 return NULL;
21052 }
21053
21054 ctx->client_socks =
21056 sizeof(ctx->client_socks[0]),
21057 ctx);
21058 if (ctx->client_socks == NULL) {
21059 const char *err_msg = "Not enough memory for worker socket array";
21060 mg_cry_ctx_internal(ctx, "%s", err_msg);
21061 mg_free(ctx->client_wait_events);
21063
21064 if (error != NULL) {
21066 error->code_sub = (unsigned)ctx->cfg_worker_threads
21067 * (unsigned)sizeof(ctx->client_socks[0]);
21069 NULL, /* No truncation check for error buffers */
21070 error->text,
21071 error->text_buffer_size,
21072 "%s",
21073 err_msg);
21074 }
21075
21076 free_context(ctx);
21077 pthread_setspecific(sTlsKey, NULL);
21078 return NULL;
21079 }
21080
21081 for (i = 0; (unsigned)i < ctx->cfg_worker_threads; i++) {
21082 ctx->client_wait_events[i] = event_create();
21083 if (ctx->client_wait_events[i] == 0) {
21084 const char *err_msg = "Error creating worker event %i";
21085 mg_cry_ctx_internal(ctx, err_msg, i);
21086 while (i > 0) {
21087 i--;
21088 event_destroy(ctx->client_wait_events[i]);
21089 }
21090 mg_free(ctx->client_socks);
21091 mg_free(ctx->client_wait_events);
21093
21094 if (error != NULL) {
21096 error->code_sub = (unsigned)ERRNO;
21098 NULL, /* No truncation check for error buffers */
21099 error->text,
21100 error->text_buffer_size,
21101 err_msg,
21102 i);
21103 }
21104
21105 free_context(ctx);
21106 pthread_setspecific(sTlsKey, NULL);
21107 return NULL;
21108 }
21109 }
21110#endif
21111
21112#if defined(USE_TIMERS)
21113 if (timers_init(ctx) != 0) {
21114 const char *err_msg = "Error creating timers";
21115 mg_cry_ctx_internal(ctx, "%s", err_msg);
21116
21117 if (error != NULL) {
21119 error->code_sub = (unsigned)ERRNO;
21121 NULL, /* No truncation check for error buffers */
21122 error->text,
21123 error->text_buffer_size,
21124 "%s",
21125 err_msg);
21126 }
21127
21128 free_context(ctx);
21129 pthread_setspecific(sTlsKey, NULL);
21130 return NULL;
21131 }
21132#endif
21133
21134 /* Context has been created - init user libraries */
21135 if (ctx->callbacks.init_context) {
21136 ctx->callbacks.init_context(ctx);
21137 }
21138
21139 /* From now, the context is successfully created.
21140 * When it is destroyed, the exit callback should be called. */
21141 ctx->callbacks.exit_context = exit_callback;
21142 ctx->context_type = CONTEXT_SERVER; /* server context */
21143
21144 /* Start worker threads */
21145 for (i = 0; i < ctx->cfg_worker_threads; i++) {
21146 /* worker_thread sets up the other fields */
21147 ctx->worker_connections[i].phys_ctx = ctx;
21149 &ctx->worker_connections[i],
21150 &ctx->worker_threadids[i])
21151 != 0) {
21152
21153 long error_no = (long)ERRNO;
21154
21155 /* thread was not created */
21156 if (i > 0) {
21157 /* If the second, third, ... thread cannot be created, set a
21158 * warning, but keep running. */
21160 "Cannot start worker thread %i: error %ld",
21161 i + 1,
21162 error_no);
21163
21164 /* If the server initialization should stop here, all
21165 * threads that have already been created must be stopped
21166 * first, before any free_context(ctx) call.
21167 */
21168
21169 } else {
21170 /* If the first worker thread cannot be created, stop
21171 * initialization and free the entire server context. */
21173 "Cannot create threads: error %ld",
21174 error_no);
21175
21176 if (error != NULL) {
21178 error->code_sub = (unsigned)error_no;
21180 NULL,
21181 NULL, /* No truncation check for error buffers */
21182 error->text,
21183 error->text_buffer_size,
21184 "Cannot create first worker thread: error %ld",
21185 error_no);
21186 }
21187
21188 free_context(ctx);
21189 pthread_setspecific(sTlsKey, NULL);
21190 return NULL;
21191 }
21192 break;
21193 }
21194 }
21195
21196 /* Start master (listening) thread */
21198
21199 pthread_setspecific(sTlsKey, NULL);
21200 return ctx;
21201}
#define MAX_WORKER_THREADS
Definition civetweb.c:465
static int set_uid_option(struct mg_context *phys_ctx)
Definition civetweb.c:16267
static int set_gpass_option(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
Definition civetweb.c:17636
static int set_ports_option(struct mg_context *phys_ctx)
Definition civetweb.c:15736
static void get_system_name(char **sysName)
Definition civetweb.c:20389
static int set_acl_option(struct mg_context *phys_ctx)
Definition civetweb.c:17662
static void * master_thread(void *thread_func_param)
Definition civetweb.c:20231
static void free_context(struct mg_context *ctx)
Definition civetweb.c:20249
static void legacy_init(const char **options)
Definition civetweb.c:20436
static void * worker_thread(void *thread_func_param)
Definition civetweb.c:19937
static int init_ssl_ctx(struct mg_context *phys_ctx, struct mg_domain_context *dom_ctx)
Definition civetweb.c:17485
static uint64_t get_random(void)
Definition civetweb.c:5909
@ MG_ERROR_DATA_CODE_INVALID_OPTION
Definition civetweb.h:1740
@ MG_ERROR_DATA_CODE_INIT_ACL_FAILED
Definition civetweb.h:1770
@ MG_ERROR_DATA_CODE_SCRIPT_ERROR
Definition civetweb.h:1776
@ MG_ERROR_DATA_CODE_INIT_PORTS_FAILED
Definition civetweb.h:1764
@ MG_ERROR_DATA_CODE_INIT_USER_FAILED
Definition civetweb.h:1767
@ MG_ERROR_DATA_CODE_INVALID_PASS_FILE
Definition civetweb.h:1773
LUA_API void lua_pushstring(lua_State *L, const char *s)
LUA_API void lua_rawset(lua_State *L, int idx)
#define lua_newtable(L)
void(* init_context)(const struct mg_context *ctx)
Definition civetweb.h:367
unsigned long starter_thread_idx
Definition civetweb.c:2380
pthread_t masterthreadid
Definition civetweb.c:2376
uint64_t auth_nonce_mask
Definition civetweb.c:2276
struct mg_domain_context * next
Definition civetweb.c:2285

References mg_domain_context::auth_nonce_mask, mg_context::callbacks, mg_init_data::callbacks, mg_context::cfg_worker_threads, mg_domain_context::config, config_options, mg_init_data::configuration_options, CONNECTION_QUEUE_SIZE, CONTEXT_SERVER, mg_context::context_type, mg_context::dd, DEBUG_TRACE, mg_option::default_value, DOCUMENT_ROOT, ERRNO, error(), mg_callbacks::exit_context, free_context(), get_option_index(), get_random(), get_system_name(), mg_domain_context::handlers, mg_callbacks::init_context, init_ssl_ctx(), mg_workerTLS::is_master, legacy_init(), vec::len, lua_getglobal, lua_newtable, lua_pop, lua_pushstring(), lua_rawset(), master_thread(), mg_context::masterthreadid, MAX_REQUEST_SIZE, mg_context::max_request_size, MAX_WORKER_THREADS, mg_atomic_inc(), mg_calloc(), mg_calloc_ctx, mg_cry_ctx_internal, MG_ERROR_DATA_CODE_INIT_ACL_FAILED, MG_ERROR_DATA_CODE_INIT_LIBRARY_FAILED, MG_ERROR_DATA_CODE_INIT_PORTS_FAILED, MG_ERROR_DATA_CODE_INIT_TLS_FAILED, MG_ERROR_DATA_CODE_INIT_USER_FAILED, MG_ERROR_DATA_CODE_INVALID_OPTION, MG_ERROR_DATA_CODE_INVALID_PASS_FILE, MG_ERROR_DATA_CODE_OK, MG_ERROR_DATA_CODE_OS_ERROR, MG_ERROR_DATA_CODE_OUT_OF_MEMORY, MG_ERROR_DATA_CODE_SCRIPT_ERROR, mg_free(), mg_init_library_called, mg_snprintf(), mg_start_thread_with_id(), mg_strdup_ctx(), mg_strncasecmp(), mg_option::name, name, mg_domain_context::next, next_option(), mg_context::nonce_mutex, NULL, NUM_THREADS, mg_connection::phys_ctx, pthread_mutex_attr, vec::ptr, set_acl_option(), set_gpass_option(), set_ports_option(), set_uid_option(), mg_context::sq_blocked, mg_context::sq_empty, mg_context::sq_full, mg_context::sq_size, mg_context::squeue, mg_context::starter_thread_idx, sTlsKey, mg_context::systemName, mg_workerTLS::thread_idx, thread_idx_max, mg_context::thread_mutex, mg_context::user_data, mg_init_data::user_data, value, mg_context::worker_connections, worker_thread(), and mg_context::worker_threadids.

Referenced by mg_start(), and start_civetweb().

◆ mg_start_domain()

CIVETWEB_API int mg_start_domain ( struct mg_context * ctx,
const char ** options )

Definition at line 21422 of file civetweb.c.

21423{
21424 return mg_start_domain2(ctx, options, NULL);
21425}
CIVETWEB_API int mg_start_domain2(struct mg_context *ctx, const char **options, struct mg_error_data *error)
Definition civetweb.c:21220

References mg_start_domain2(), and NULL.

Referenced by start_civetweb().

◆ mg_start_domain2()

CIVETWEB_API int mg_start_domain2 ( struct mg_context * ctx,
const char ** options,
struct mg_error_data * error )

Definition at line 21220 of file civetweb.c.

21223{
21224 const char *name;
21225 const char *value;
21226 const char *default_value;
21227 struct mg_domain_context *new_dom;
21228 struct mg_domain_context *dom;
21229 int idx, i;
21230
21231 if (error != NULL) {
21233 error->code_sub = 0;
21234 if (error->text_buffer_size > 0) {
21235 *error->text = 0;
21236 }
21237 }
21238
21239 if ((ctx == NULL) || (options == NULL)) {
21240 if (error != NULL) {
21243 NULL, /* No truncation check for error buffers */
21244 error->text,
21245 error->text_buffer_size,
21246 "%s",
21247 "Invalid parameters");
21248 }
21249 return -1;
21250 }
21251
21252 if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
21253 if (error != NULL) {
21256 NULL, /* No truncation check for error buffers */
21257 error->text,
21258 error->text_buffer_size,
21259 "%s",
21260 "Server already stopped");
21261 }
21262 return -7;
21263 }
21264
21265 new_dom = (struct mg_domain_context *)
21266 mg_calloc_ctx(1, sizeof(struct mg_domain_context), ctx);
21267
21268 if (!new_dom) {
21269 /* Out of memory */
21270 if (error != NULL) {
21272 error->code_sub = (unsigned)sizeof(struct mg_domain_context);
21274 NULL, /* No truncation check for error buffers */
21275 error->text,
21276 error->text_buffer_size,
21277 "%s",
21278 "Out or memory");
21279 }
21280 return -6;
21281 }
21282
21283 /* Store options - TODO: unite duplicate code */
21284 while (options && (name = *options++) != NULL) {
21285 idx = get_option_index(name);
21286 if (idx == -1) {
21287 mg_cry_ctx_internal(ctx, "Invalid option: %s", name);
21288 if (error != NULL) {
21290 error->code_sub = (unsigned)-1;
21292 NULL, /* No truncation check for error buffers */
21293 error->text,
21294 error->text_buffer_size,
21295 "Invalid option: %s",
21296 name);
21297 }
21298 mg_free(new_dom);
21299 return -2;
21300 } else if ((value = *options++) == NULL) {
21301 mg_cry_ctx_internal(ctx, "%s: option value cannot be NULL", name);
21302 if (error != NULL) {
21304 error->code_sub = (unsigned)idx;
21306 NULL, /* No truncation check for error buffers */
21307 error->text,
21308 error->text_buffer_size,
21309 "Invalid option value: %s",
21310 name);
21311 }
21312 mg_free(new_dom);
21313 return -2;
21314 }
21315 if (new_dom->config[idx] != NULL) {
21316 /* Duplicate option: Later values overwrite earlier ones. */
21317 mg_cry_ctx_internal(ctx, "warning: %s: duplicate option", name);
21318 mg_free(new_dom->config[idx]);
21319 }
21320 new_dom->config[idx] = mg_strdup_ctx(value, ctx);
21321 DEBUG_TRACE("[%s] -> [%s]", name, value);
21322 }
21323
21324 /* Authentication domain is mandatory */
21325 /* TODO: Maybe use a new option hostname? */
21326 if (!new_dom->config[AUTHENTICATION_DOMAIN]) {
21327 mg_cry_ctx_internal(ctx, "%s", "authentication domain required");
21328 if (error != NULL) {
21330 error->code_sub = AUTHENTICATION_DOMAIN;
21332 NULL, /* No truncation check for error buffers */
21333 error->text,
21334 error->text_buffer_size,
21335 "Mandatory option %s missing",
21337 }
21338 mg_free(new_dom);
21339 return -4;
21340 }
21341
21342 /* Set default value if needed. Take the config value from
21343 * ctx as a default value. */
21344 for (i = 0; config_options[i].name != NULL; i++) {
21345 default_value = ctx->dd.config[i];
21346 if ((new_dom->config[i] == NULL) && (default_value != NULL)) {
21347 new_dom->config[i] = mg_strdup_ctx(default_value, ctx);
21348 }
21349 }
21350
21351 new_dom->handlers = NULL;
21352 new_dom->next = NULL;
21353 new_dom->nonce_count = 0;
21354 new_dom->auth_nonce_mask = get_random() ^ (get_random() << 31);
21355
21356#if defined(USE_LUA) && defined(USE_WEBSOCKET)
21357 new_dom->shared_lua_websockets = NULL;
21358#endif
21359
21360#if !defined(NO_SSL) && !defined(USE_MBEDTLS)
21361 if (!init_ssl_ctx(ctx, new_dom)) {
21362 /* Init SSL failed */
21363 if (error != NULL) {
21366 NULL, /* No truncation check for error buffers */
21367 error->text,
21368 error->text_buffer_size,
21369 "%s",
21370 "Initializing SSL context failed");
21371 }
21372 mg_free(new_dom);
21373 return -3;
21374 }
21375#endif
21376
21377 /* Add element to linked list. */
21378 mg_lock_context(ctx);
21379
21380 idx = 0;
21381 dom = &(ctx->dd);
21382 for (;;) {
21385 /* Domain collision */
21387 "domain %s already in use",
21388 new_dom->config[AUTHENTICATION_DOMAIN]);
21389 if (error != NULL) {
21392 NULL, /* No truncation check for error buffers */
21393 error->text,
21394 error->text_buffer_size,
21395 "Domain %s specified by %s is already in use",
21396 new_dom->config[AUTHENTICATION_DOMAIN],
21398 }
21399 mg_free(new_dom);
21400 mg_unlock_context(ctx);
21401 return -5;
21402 }
21403
21404 /* Count number of domains */
21405 idx++;
21406
21407 if (dom->next == NULL) {
21408 dom->next = new_dom;
21409 break;
21410 }
21411 dom = dom->next;
21412 }
21413
21414 mg_unlock_context(ctx);
21415
21416 /* Return domain number */
21417 return idx;
21418}
@ MG_ERROR_DATA_CODE_MISSING_OPTION
Definition civetweb.h:1746
@ MG_ERROR_DATA_CODE_DUPLICATE_DOMAIN
Definition civetweb.h:1749
@ MG_ERROR_DATA_CODE_SERVER_STOPPED
Definition civetweb.h:1755
unsigned long nonce_count
Definition civetweb.c:2277

References mg_domain_context::auth_nonce_mask, AUTHENTICATION_DOMAIN, mg_domain_context::config, config_options, mg_context::dd, DEBUG_TRACE, error(), get_option_index(), get_random(), mg_domain_context::handlers, init_ssl_ctx(), mg_calloc_ctx, mg_cry_ctx_internal, MG_ERROR_DATA_CODE_DUPLICATE_DOMAIN, MG_ERROR_DATA_CODE_INIT_TLS_FAILED, MG_ERROR_DATA_CODE_INVALID_OPTION, MG_ERROR_DATA_CODE_INVALID_PARAM, MG_ERROR_DATA_CODE_MISSING_OPTION, MG_ERROR_DATA_CODE_OK, MG_ERROR_DATA_CODE_OUT_OF_MEMORY, MG_ERROR_DATA_CODE_SERVER_STOPPED, mg_free(), mg_lock_context(), mg_snprintf(), mg_strcasecmp(), mg_strdup_ctx(), mg_unlock_context(), mg_option::name, name, mg_domain_context::next, mg_domain_context::nonce_count, NULL, mg_context::stop_flag, STOP_FLAG_IS_ZERO, and value.

Referenced by mg_start_domain().

◆ mg_start_thread()

CIVETWEB_API int mg_start_thread ( mg_thread_func_t func,
void * param )

Definition at line 5700 of file civetweb.c.

5701{
5702 pthread_t thread_id;
5703 pthread_attr_t attr;
5704 int result;
5705
5706 (void)pthread_attr_init(&attr);
5707 (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
5708
5709#if defined(__ZEPHYR__)
5710 pthread_attr_setstack(&attr, &civetweb_main_stack, ZEPHYR_STACK_SIZE);
5711#elif defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5712 /* Compile-time option to control stack size,
5713 * e.g. -DUSE_STACK_SIZE=16384 */
5714 (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
5715#endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */
5716
5717 result = pthread_create(&thread_id, &attr, func, param);
5718 pthread_attr_destroy(&attr);
5719
5720 return result;
5721}

◆ mg_start_thread_with_id()

static int mg_start_thread_with_id ( mg_thread_func_t func,
void * param,
pthread_t * threadidptr )
static

Definition at line 5726 of file civetweb.c.

5729{
5730 pthread_t thread_id;
5731 pthread_attr_t attr;
5732 int result;
5733
5734 (void)pthread_attr_init(&attr);
5735
5736#if defined(__ZEPHYR__)
5737 pthread_attr_setstack(&attr,
5738 &civetweb_worker_stacks[zephyr_worker_stack_index++],
5739 ZEPHYR_STACK_SIZE);
5740#elif defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1)
5741 /* Compile-time option to control stack size,
5742 * e.g. -DUSE_STACK_SIZE=16384 */
5743 (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
5744#endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */
5745
5746 result = pthread_create(&thread_id, &attr, func, param);
5747 pthread_attr_destroy(&attr);
5748 if ((result == 0) && (threadidptr != NULL)) {
5749 *threadidptr = thread_id;
5750 }
5751 return result;
5752}

References NULL.

Referenced by mg_connect_websocket_client_impl(), and mg_start2().

◆ mg_stat()

static int mg_stat ( const struct mg_connection * conn,
const char * path,
struct mg_file_stat * filep )
static

Definition at line 5650 of file civetweb.c.

5653{
5654 struct stat st;
5655 if (!filep) {
5656 return 0;
5657 }
5658 memset(filep, 0, sizeof(*filep));
5659
5660 if (mg_path_suspicious(conn, path)) {
5661 return 0;
5662 }
5663
5664 if (0 == stat(path, &st)) {
5665 filep->size = (uint64_t)(st.st_size);
5666 filep->last_modified = st.st_mtime;
5667 filep->is_directory = S_ISDIR(st.st_mode);
5668 return 1;
5669 }
5670
5671 return 0;
5672}

References mg_file_stat::is_directory, mg_file_stat::last_modified, mg_path_suspicious(), and mg_file_stat::size.

Referenced by dav_mkcol(), dav_move_file(), delete_file(), handle_static_file_request(), interpret_uri(), mg_fopen(), mg_send_http_error_impl(), mg_send_mime_file2(), open_auth_file(), put_dir(), put_file(), remove_directory(), scan_directory(), set_gpass_option(), and substitute_index_file().

◆ mg_static_assert() [1/6]

mg_static_assert ( (sizeof(config_options)/sizeof(config_options[0])) = =(NUM_OPTIONS+1),
"config_options and enum not sync"  )

◆ mg_static_assert() [2/6]

mg_static_assert ( MAX_WORKER_THREADS >= 1,
"worker threads must be a positive number"  )

◆ mg_static_assert() [3/6]

mg_static_assert ( sizeof(int) = =4||sizeof(int)==8,
"int data type size check"  )

◆ mg_static_assert() [4/6]

mg_static_assert ( sizeof(size_t) = =4||sizeof(size_t)==8,
"size_t data type size check"  )

◆ mg_static_assert() [5/6]

mg_static_assert ( sizeof(void *) >=sizeof(int) ,
"data type size check"  )

◆ mg_static_assert() [6/6]

mg_static_assert ( sizeof(void *) = =4||sizeof(void *)==8,
"pointer data type size check"  )

◆ mg_stop()

CIVETWEB_API void mg_stop ( struct mg_context * ctx)

Definition at line 20346 of file civetweb.c.

20347{
20348 pthread_t mt;
20349 if (!ctx) {
20350 return;
20351 }
20352
20353 /* We don't use a lock here. Calling mg_stop with the same ctx from
20354 * two threads is not allowed. */
20355 mt = ctx->masterthreadid;
20356 if (mt == 0) {
20357 return;
20358 }
20359
20360 ctx->masterthreadid = 0;
20361
20362 /* Set stop flag, so all threads know they have to exit. */
20363 STOP_FLAG_ASSIGN(&ctx->stop_flag, 1);
20364
20365 /* Join timer thread */
20366#if defined(USE_TIMERS)
20367 timers_exit(ctx);
20368#endif
20369
20370 /* Wait until everything has stopped. */
20371 while (!STOP_FLAG_IS_TWO(&ctx->stop_flag)) {
20372 (void)mg_sleep(10);
20373 }
20374
20375 /* Wait to stop master thread */
20376 mg_join_thread(mt);
20377
20378 /* Close remaining Lua states */
20379#if defined(USE_LUA)
20380 lua_ctx_exit(ctx);
20381#endif
20382
20383 /* Free memory */
20384 free_context(ctx);
20385}
#define STOP_FLAG_IS_TWO(f)
Definition civetweb.c:2323

References free_context(), mg_context::masterthreadid, mg_join_thread(), mg_sleep, mg_context::stop_flag, STOP_FLAG_ASSIGN, and STOP_FLAG_IS_TWO.

Referenced by start_http_server(), and stop_civetweb().

◆ mg_store_body()

CIVETWEB_API long long mg_store_body ( struct mg_connection * conn,
const char * path )

Definition at line 10622 of file civetweb.c.

10623{
10624 char buf[MG_BUF_LEN];
10625 long long len = 0;
10626 int ret, n;
10627 struct mg_file fi;
10628
10629 if (conn->consumed_content != 0) {
10630 mg_cry_internal(conn, "%s: Contents already consumed", __func__);
10631 return -11;
10632 }
10633
10634 ret = put_dir(conn, path);
10635 if (ret < 0) {
10636 /* -1 for path too long,
10637 * -2 for path can not be created. */
10638 return ret;
10639 }
10640 if (ret != 1) {
10641 /* Return 0 means, path itself is a directory. */
10642 return 0;
10643 }
10644
10645 if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fi) == 0) {
10646 return -12;
10647 }
10648
10649 ret = mg_read(conn, buf, sizeof(buf));
10650 while (ret > 0) {
10651 n = (int)fwrite(buf, 1, (size_t)ret, fi.access.fp);
10652 if (n != ret) {
10653 (void)mg_fclose(
10654 &fi.access); /* File is bad and will be removed anyway. */
10655 remove_bad_file(conn, path);
10656 return -13;
10657 }
10658 len += ret;
10659 ret = mg_read(conn, buf, sizeof(buf));
10660 }
10661
10662 /* File is open for writing. If fclose fails, there was probably an
10663 * error flushing the buffer to disk, so the file on disk might be
10664 * broken. Delete it and return an error to the caller. */
10665 if (mg_fclose(&fi.access) != 0) {
10666 remove_bad_file(conn, path);
10667 return -14;
10668 }
10669
10670 return len;
10671}
static void remove_bad_file(const struct mg_connection *conn, const char *path)
Definition civetweb.c:10609
static int put_dir(struct mg_connection *conn, const char *path)
Definition civetweb.c:10572

References mg_file::access, mg_connection::consumed_content, mg_file_access::fp, fwrite(), MG_BUF_LEN, mg_cry_internal, mg_fclose(), mg_fopen(), MG_FOPEN_MODE_WRITE, mg_read(), put_dir(), and remove_bad_file().

◆ mg_str_append()

static size_t mg_str_append ( char ** dst,
char * end,
const char * src )
static

Definition at line 21493 of file civetweb.c.

21494{
21495 size_t len = strlen(src);
21496 if (*dst != end) {
21497 /* Append src if enough space, or close dst. */
21498 if ((size_t)(end - *dst) > len) {
21499 strcpy(*dst, src);
21500 *dst += len;
21501 } else {
21502 *dst = end;
21503 }
21504 }
21505 return len;
21506}

Referenced by mg_get_context_info(), and mg_get_system_info().

◆ mg_strcasecmp()

◆ mg_strcasestr()

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

Definition at line 3075 of file civetweb.c.

3076{
3077 size_t i, big_len = strlen(big_str), small_len = strlen(small_str);
3078
3079 if (big_len >= small_len) {
3080 for (i = 0; i <= (big_len - small_len); i++) {
3081 if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) {
3082 return big_str + i;
3083 }
3084 }
3085 }
3086
3087 return NULL;
3088}

References mg_strncasecmp(), and NULL.

Referenced by mg_get_cookie(), and should_switch_to_protocol().

◆ mg_strdup()

static char * mg_strdup ( const char * str)
static

Definition at line 3068 of file civetweb.c.

3069{
3070 return mg_strndup_ctx(str, strlen(str), NULL);
3071}
static char * mg_strndup_ctx(const char *ptr, size_t len, struct mg_context *ctx)
Definition civetweb.c:3047

References mg_strndup_ctx(), and NULL.

Referenced by dir_scan_callback(), get_system_name(), and handle_request().

◆ mg_strdup_ctx()

static char * mg_strdup_ctx ( const char * str,
struct mg_context * ctx )
static

Definition at line 3062 of file civetweb.c.

3063{
3064 return mg_strndup_ctx(str, strlen(str), ctx);
3065}

References mg_strndup_ctx().

Referenced by authorize(), connect_socket(), dav_move_file(), mg_set_handler_type(), mg_start2(), mg_start_domain2(), and ssl_get_client_cert_info().

◆ mg_strlcpy()

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

Definition at line 3002 of file civetweb.c.

3003{
3004 for (; *src != '\0' && n > 1; n--) {
3005 *dst++ = *src++;
3006 }
3007 *dst = '\0';
3008}

Referenced by dav_lock_file(), gmt_time_string(), handle_directory_request(), log_access(), mg_get_cookie(), mg_strndup_ctx(), parse_auth_header(), parse_match_net(), parse_port_string(), print_dir_entry(), sockaddr_to_string(), and substitute_index_file().

◆ mg_strncasecmp()

CIVETWEB_API int mg_strncasecmp ( const char * s1,
const char * s2,
size_t len )

Definition at line 3019 of file civetweb.c.

3020{
3021 int diff = 0;
3022
3023 if (len > 0) {
3024 do {
3025 diff = lowercase(s1++) - lowercase(s2++);
3026 } while (diff == 0 && s1[-1] != '\0' && --len > 0);
3027 }
3028
3029 return diff;
3030}

References lowercase().

Referenced by get_mime_type(), get_rel_url_at_current_server(), get_uri_type(), header_has_option(), mg_get_var2(), mg_start2(), mg_strcasestr(), parse_auth_header(), and switch_domain_context().

◆ mg_strndup_ctx()

static char * mg_strndup_ctx ( const char * ptr,
size_t len,
struct mg_context * ctx )
static

Definition at line 3047 of file civetweb.c.

3048{
3049 char *p;
3050 (void)ctx; /* Avoid Visual Studio warning if USE_SERVER_STATS is not
3051 * defined */
3052
3053 if ((p = (char *)mg_malloc_ctx(len + 1, ctx)) != NULL) {
3054 mg_strlcpy(p, ptr, len + 1);
3055 }
3056
3057 return p;
3058}

References mg_malloc_ctx, mg_strlcpy(), and NULL.

Referenced by mg_strdup(), and mg_strdup_ctx().

◆ mg_unlock_connection()

CIVETWEB_API void mg_unlock_connection ( struct mg_connection * conn)

Definition at line 13009 of file civetweb.c.

13010{
13011 if (conn) {
13012 (void)pthread_mutex_unlock(&conn->mutex);
13013 }
13014}

References mg_connection::mutex.

Referenced by close_connection().

◆ mg_unlock_context()

◆ mg_url_decode()

CIVETWEB_API int mg_url_decode ( const char * src,
int src_len,
char * dst,
int dst_len,
int is_form_url_encoded )

Definition at line 7048 of file civetweb.c.

7053{
7054 int i, j, a, b;
7055#define HEXTOI(x) (isdigit(x) ? (x - '0') : (x - 'W'))
7056
7057 for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) {
7058 if ((i < src_len - 2) && (src[i] == '%')
7059 && isxdigit((unsigned char)src[i + 1])
7060 && isxdigit((unsigned char)src[i + 2])) {
7061 a = tolower((unsigned char)src[i + 1]);
7062 b = tolower((unsigned char)src[i + 2]);
7063 dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b));
7064 i += 2;
7065 } else if (is_form_url_encoded && (src[i] == '+')) {
7066 dst[j] = ' ';
7067 } else {
7068 dst[j] = src[i];
7069 }
7070 }
7071
7072 dst[j] = '\0'; /* Null-terminate the destination */
7073
7074 return (i >= src_len) ? j : -1;
7075}
#define HEXTOI(x)

References HEXTOI.

Referenced by dav_move_file(), mg_get_var2(), and url_decode_in_place().

◆ mg_url_encode()

CIVETWEB_API int mg_url_encode ( const char * src,
char * dst,
size_t dst_len )

Definition at line 9581 of file civetweb.c.

9582{
9583 static const char *dont_escape = "._-$,;~()";
9584 static const char *hex = "0123456789abcdef";
9585 char *pos = dst;
9586 const char *end = dst + dst_len - 1;
9587
9588 for (; ((*src != '\0') && (pos < end)); src++, pos++) {
9589 if (isalnum((unsigned char)*src)
9590 || (strchr(dont_escape, *src) != NULL)) {
9591 *pos = *src;
9592 } else if (pos + 2 < end) {
9593 pos[0] = '%';
9594 pos[1] = hex[(unsigned char)*src >> 4];
9595 pos[2] = hex[(unsigned char)*src & 0xf];
9596 pos += 2;
9597 } else {
9598 break;
9599 }
9600 }
9601
9602 *pos = '\0';
9603 return (*src == '\0') ? (int)(pos - dst) : -1;
9604}

References NULL.

Referenced by mg_construct_local_link(), and print_dir_entry().

◆ mg_version()

CIVETWEB_API const char * mg_version ( void )

Definition at line 3514 of file civetweb.c.

3515{
3516 return CIVETWEB_VERSION;
3517}
#define CIVETWEB_VERSION
Definition civetweb.h:26

References CIVETWEB_VERSION.

Referenced by init_server_name(), mg_get_system_info(), prepare_cgi_environment(), and show_server_name().

◆ mg_vprintf()

static int mg_vprintf ( struct mg_connection * conn,
const char * fmt,
va_list ap )
static

Definition at line 7016 of file civetweb.c.

7017{
7018 char mem[MG_BUF_LEN];
7019 char *buf = NULL;
7020 int len;
7021
7022 if ((len = alloc_vprintf(&buf, mem, sizeof(mem), fmt, ap)) > 0) {
7023 len = mg_write(conn, buf, (size_t)len);
7024 }
7025 if (buf != mem) {
7026 mg_free(buf);
7027 }
7028
7029 return len;
7030}

References alloc_vprintf(), MG_BUF_LEN, mg_free(), mg_write(), and NULL.

Referenced by mg_download(), and mg_printf().

◆ mg_vsnprintf()

static void mg_vsnprintf ( const struct mg_connection * conn,
int * truncated,
char * buf,
size_t buflen,
const char * fmt,
va_list ap )
static

Definition at line 3094 of file civetweb.c.

3100{
3101 int n, ok;
3102
3103 if (buflen == 0) {
3104 if (truncated) {
3105 *truncated = 1;
3106 }
3107 return;
3108 }
3109
3110#if defined(__clang__)
3111#pragma clang diagnostic push
3112#pragma clang diagnostic ignored "-Wformat-nonliteral"
3113 /* Using fmt as a non-literal is intended here, since it is mostly called
3114 * indirectly by mg_snprintf */
3115#endif
3116
3117 n = (int)vsnprintf_impl(buf, buflen, fmt, ap);
3118 ok = (n >= 0) && ((size_t)n < buflen);
3119
3120#if defined(__clang__)
3121#pragma clang diagnostic pop
3122#endif
3123
3124 if (ok) {
3125 if (truncated) {
3126 *truncated = 0;
3127 }
3128 } else {
3129 if (truncated) {
3130 *truncated = 1;
3131 }
3132 mg_cry_internal(conn,
3133 "truncating vsnprintf buffer: [%.*s]",
3134 (int)((buflen > 200) ? 200 : (buflen - 1)),
3135 buf);
3136 n = (int)buflen - 1;
3137 }
3138 buf[n] = '\0';
3139}

References mg_cry_internal, and vsnprintf_impl.

Referenced by addenv(), mg_send_http_error_impl(), and mg_snprintf().

◆ mg_write()

CIVETWEB_API int mg_write ( struct mg_connection * conn,
const void * buf,
size_t len )

Definition at line 6779 of file civetweb.c.

6780{
6781 time_t now;
6782 int n, total, allowed;
6783
6784 if (conn == NULL) {
6785 return 0;
6786 }
6787 if (len > INT_MAX) {
6788 return -1;
6789 }
6790
6791 /* Mark connection as "data sent" */
6792 conn->request_state = 10;
6793#if defined(USE_HTTP2)
6794 if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
6795 http2_data_frame_head(conn, len, 0);
6796 }
6797#endif
6798
6799 if (conn->throttle > 0) {
6800 if ((now = time(NULL)) != conn->last_throttle_time) {
6801 conn->last_throttle_time = now;
6802 conn->last_throttle_bytes = 0;
6803 }
6804 allowed = conn->throttle - conn->last_throttle_bytes;
6805 if (allowed > (int)len) {
6806 allowed = (int)len;
6807 }
6808
6809 total = push_all(conn->phys_ctx,
6810 NULL,
6811 conn->client.sock,
6812 conn->ssl,
6813 (const char *)buf,
6814 allowed);
6815
6816 if (total == allowed) {
6817
6818 buf = (const char *)buf + total;
6819 conn->last_throttle_bytes += total;
6820 while ((total < (int)len)
6821 && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6822 allowed = (conn->throttle > ((int)len - total))
6823 ? (int)len - total
6824 : conn->throttle;
6825
6826 n = push_all(conn->phys_ctx,
6827 NULL,
6828 conn->client.sock,
6829 conn->ssl,
6830 (const char *)buf,
6831 allowed);
6832
6833 if (n != allowed) {
6834 break;
6835 }
6836 sleep(1);
6837 conn->last_throttle_bytes = allowed;
6838 conn->last_throttle_time = time(NULL);
6839 buf = (const char *)buf + n;
6840 total += n;
6841 }
6842 }
6843 } else {
6844 total = push_all(conn->phys_ctx,
6845 NULL,
6846 conn->client.sock,
6847 conn->ssl,
6848 (const char *)buf,
6849 (int)len);
6850 }
6851 if (total > 0) {
6852 conn->num_bytes_sent += total;
6853 }
6854 return total;
6855}
time_t last_throttle_time
Definition civetweb.c:2563
int last_throttle_bytes
Definition civetweb.c:2564

References mg_connection::client, mg_connection::last_throttle_bytes, mg_connection::last_throttle_time, NULL, mg_connection::num_bytes_sent, mg_connection::phys_ctx, mg_connection::protocol_type, PROTOCOL_TYPE_HTTP2, push_all(), mg_connection::request_state, socket::sock, mg_connection::ssl, mg_context::stop_flag, STOP_FLAG_IS_ZERO, and mg_connection::throttle.

Referenced by handle_cgi_request(), mg_send_chunk(), mg_send_http_error_impl(), mg_send_http_redirect(), mg_vprintf(), send_file_data(), and send_ssi_file().

◆ must_hide_file()

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

Definition at line 9763 of file civetweb.c.

9764{
9765 if (conn && conn->dom_ctx) {
9766 const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
9767 const char *pattern = conn->dom_ctx->config[HIDE_FILES];
9768 return (match_prefix_strlen(pw_pattern, path) > 0)
9769 || (match_prefix_strlen(pattern, path) > 0);
9770 }
9771 return 0;
9772}
#define PASSWORDS_FILE_NAME
Definition civetweb.c:482

References mg_domain_context::config, mg_connection::dom_ctx, HIDE_FILES, and PASSWORDS_FILE_NAME.

Referenced by handle_request(), and scan_directory().

◆ next_option()

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

Definition at line 3899 of file civetweb.c.

3900{
3901 int end;
3902
3903reparse:
3904 if (val == NULL || list == NULL || *list == '\0') {
3905 /* End of the list */
3906 return NULL;
3907 }
3908
3909 /* Skip over leading LWS */
3910 while (*list == ' ' || *list == '\t')
3911 list++;
3912
3913 val->ptr = list;
3914 if ((list = strchr(val->ptr, ',')) != NULL) {
3915 /* Comma found. Store length and shift the list ptr */
3916 val->len = ((size_t)(list - val->ptr));
3917 list++;
3918 } else {
3919 /* This value is the last one */
3920 list = val->ptr + strlen(val->ptr);
3921 val->len = ((size_t)(list - val->ptr));
3922 }
3923
3924 /* Adjust length for trailing LWS */
3925 end = (int)val->len - 1;
3926 while (end >= 0 && ((val->ptr[end] == ' ') || (val->ptr[end] == '\t')))
3927 end--;
3928 val->len = (size_t)(end) + (size_t)(1);
3929
3930 if (val->len == 0) {
3931 /* Ignore any empty entries. */
3932 goto reparse;
3933 }
3934
3935 if (eq_val != NULL) {
3936 /* Value has form "x=y", adjust pointers and lengths
3937 * so that val points to "x", and eq_val points to "y". */
3938 eq_val->len = 0;
3939 eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len);
3940 if (eq_val->ptr != NULL) {
3941 eq_val->ptr++; /* Skip over '=' character */
3942 eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len;
3943 val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1;
3944 }
3945 }
3946
3947 return list;
3948}

References vec::len, NULL, and vec::ptr.

Referenced by check_acl(), check_authorization(), get_mime_type(), header_has_option(), interpret_uri(), mg_start2(), prepare_cgi_environment(), set_ports_option(), set_throttle(), and substitute_index_file().

◆ open_auth_file()

static void open_auth_file ( struct mg_connection * conn,
const char * path,
struct mg_file * filep )
static

Definition at line 8472 of file civetweb.c.

8475{
8476 if ((conn != NULL) && (conn->dom_ctx != NULL)) {
8477 char name[UTF8_PATH_MAX];
8478 const char *p, *e,
8479 *gpass = conn->dom_ctx->config[GLOBAL_PASSWORDS_FILE];
8480 int truncated;
8481
8482 if (gpass != NULL) {
8483 /* Use global passwords file */
8484 if (!mg_fopen(conn, gpass, MG_FOPEN_MODE_READ, filep)) {
8485#if defined(DEBUG)
8486 /* Use mg_cry_internal here, since gpass has been
8487 * configured. */
8488 mg_cry_internal(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
8489#endif
8490 }
8491 /* Important: using local struct mg_file to test path for
8492 * is_directory flag. If filep is used, mg_stat() makes it
8493 * appear as if auth file was opened.
8494 * TODO(mid): Check if this is still required after rewriting
8495 * mg_stat */
8496 } else if (mg_stat(conn, path, &filep->stat)
8497 && filep->stat.is_directory) {
8498 mg_snprintf(conn,
8499 &truncated,
8500 name,
8501 sizeof(name),
8502 "%s/%s",
8503 path,
8505
8506 if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
8507#if defined(DEBUG)
8508 /* Don't use mg_cry_internal here, but only a trace, since
8509 * this is a typical case. It will occur for every directory
8510 * without a password file. */
8511 DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
8512#endif
8513 }
8514 } else {
8515 /* Try to find .htpasswd in requested directory. */
8516 for (p = path, e = p + strlen(p) - 1; e > p; e--) {
8517 if (e[0] == '/') {
8518 break;
8519 }
8520 }
8521 mg_snprintf(conn,
8522 &truncated,
8523 name,
8524 sizeof(name),
8525 "%.*s/%s",
8526 (int)(e - p),
8527 p,
8529
8530 if (truncated || !mg_fopen(conn, name, MG_FOPEN_MODE_READ, filep)) {
8531#if defined(DEBUG)
8532 /* Don't use mg_cry_internal here, but only a trace, since
8533 * this is a typical case. It will occur for every directory
8534 * without a password file. */
8535 DEBUG_TRACE("fopen(%s): %s", name, strerror(ERRNO));
8536#endif
8537 }
8538 }
8539 }
8540}

References mg_domain_context::config, DEBUG_TRACE, mg_connection::dom_ctx, ERRNO, GLOBAL_PASSWORDS_FILE, mg_file_stat::is_directory, mg_cry_internal, mg_fopen(), MG_FOPEN_MODE_READ, mg_snprintf(), mg_stat(), name, NULL, PASSWORDS_FILE_NAME, mg_file::stat, and UTF8_PATH_MAX.

Referenced by check_authorization().

◆ 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 8555 of file civetweb.c.

8559{
8560 char *name, *value, *s;
8561 const char *auth_header;
8562 uint64_t nonce;
8563
8564 if (!ah || !conn) {
8565 return 0;
8566 }
8567
8568 (void)memset(ah, 0, sizeof(*ah));
8569 auth_header = mg_get_header(conn, "Authorization");
8570
8571 if (auth_header == NULL) {
8572 /* No Authorization header at all */
8573 return 0;
8574 }
8575 if (0 == mg_strncasecmp(auth_header, "Basic ", 6)) {
8576 /* Basic Auth (we never asked for this, but some client may send it) */
8577 char *split;
8578 const char *userpw_b64 = auth_header + 6;
8579 size_t userpw_b64_len = strlen(userpw_b64);
8580 size_t buf_len_r = buf_size;
8581 if (mg_base64_decode(
8582 userpw_b64, userpw_b64_len, (unsigned char *)buf, &buf_len_r)
8583 != -1) {
8584 return 0; /* decode error */
8585 }
8586 split = strchr(buf, ':');
8587 if (!split) {
8588 return 0; /* Format error */
8589 }
8590
8591 /* Separate string at ':' */
8592 *split = 0;
8593
8594 /* User name is before ':', Password is after ':' */
8595 ah->user = buf;
8596 ah->type = 1;
8597 ah->plain_password = split + 1;
8598
8599 return 1;
8600
8601 } else if (0 == mg_strncasecmp(auth_header, "Digest ", 7)) {
8602 /* Digest Auth ... implemented below */
8603 ah->type = 2;
8604
8605 } else {
8606 /* Unknown or invalid Auth method */
8607 return 0;
8608 }
8609
8610 /* Make modifiable copy of the auth header */
8611 (void)mg_strlcpy(buf, auth_header + 7, buf_size);
8612 s = buf;
8613
8614 /* Parse authorization header */
8615 for (;;) {
8616 /* Gobble initial spaces */
8617 while (isspace((unsigned char)*s)) {
8618 s++;
8619 }
8620 name = skip_quoted(&s, "=", " ", 0);
8621 /* Value is either quote-delimited, or ends at first comma or space.
8622 */
8623 if (s[0] == '\"') {
8624 s++;
8625 value = skip_quoted(&s, "\"", " ", '\\');
8626 if (s[0] == ',') {
8627 s++;
8628 }
8629 } else {
8630 value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF
8631 * uses spaces */
8632 }
8633 if (*name == '\0') {
8634 break;
8635 }
8636
8637 if (!strcmp(name, "username")) {
8638 ah->user = value;
8639 } else if (!strcmp(name, "cnonce")) {
8640 ah->cnonce = value;
8641 } else if (!strcmp(name, "response")) {
8642 ah->response = value;
8643 } else if (!strcmp(name, "uri")) {
8644 ah->uri = value;
8645 } else if (!strcmp(name, "qop")) {
8646 ah->qop = value;
8647 } else if (!strcmp(name, "nc")) {
8648 ah->nc = value;
8649 } else if (!strcmp(name, "nonce")) {
8650 ah->nonce = value;
8651 }
8652 }
8653
8654#if !defined(NO_NONCE_CHECK)
8655 /* Read the nonce from the response. */
8656 if (ah->nonce == NULL) {
8657 return 0;
8658 }
8659 s = NULL;
8660 nonce = strtoull(ah->nonce, &s, 10);
8661 if ((s == NULL) || (*s != 0)) {
8662 return 0;
8663 }
8664
8665 /* Convert the nonce from the client to a number. */
8666 nonce ^= conn->dom_ctx->auth_nonce_mask;
8667
8668 /* The converted number corresponds to the time the nounce has been
8669 * created. This should not be earlier than the server start. */
8670 /* Server side nonce check is valuable in all situations but one:
8671 * if the server restarts frequently, but the client should not see
8672 * that, so the server should accept nonces from previous starts. */
8673 /* However, the reasonable default is to not accept a nonce from a
8674 * previous start, so if anyone changed the access rights between
8675 * two restarts, a new login is required. */
8676 if (nonce < (uint64_t)conn->phys_ctx->start_time) {
8677 /* nonce is from a previous start of the server and no longer valid
8678 * (replay attack?) */
8679 return 0;
8680 }
8681 /* Check if the nonce is too high, so it has not (yet) been used by the
8682 * server. */
8683 if (nonce >= ((uint64_t)conn->phys_ctx->start_time
8684 + conn->dom_ctx->nonce_count)) {
8685 return 0;
8686 }
8687#else
8688 (void)nonce;
8689#endif
8690
8691 return (ah->user != NULL);
8692}
CIVETWEB_API int mg_base64_decode(const char *src, size_t src_len, unsigned char *dst, size_t *dst_len)
Definition civetweb.c:7383
static char * skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar)
Definition civetweb.c:3757
char * nc
Definition civetweb.c:8549
char * nonce
Definition civetweb.c:8549
char * cnonce
Definition civetweb.c:8549
char * uri
Definition civetweb.c:8549
char * user
Definition civetweb.c:8546
char * plain_password
Definition civetweb.c:8548
char * qop
Definition civetweb.c:8549
char * response
Definition civetweb.c:8549
int type
Definition civetweb.c:8547

References mg_domain_context::auth_nonce_mask, ah::cnonce, mg_connection::dom_ctx, mg_base64_decode(), mg_get_header(), mg_strlcpy(), mg_strncasecmp(), name, ah::nc, ah::nonce, mg_domain_context::nonce_count, NULL, mg_connection::phys_ctx, ah::plain_password, ah::qop, ah::response, s, skip_quoted(), mg_context::start_time, ah::type, ah::uri, ah::user, and value.

Referenced by authorize().

◆ parse_date_string()

static time_t parse_date_string ( const char * datetime)
static

Definition at line 8005 of file civetweb.c.

8006{
8007 char month_str[32] = {0};
8008 int second, minute, hour, day, month, year;
8009 time_t result = (time_t)0;
8010 struct tm tm;
8011
8012 if ((sscanf(datetime,
8013 "%d/%3s/%d %d:%d:%d",
8014 &day,
8015 month_str,
8016 &year,
8017 &hour,
8018 &minute,
8019 &second)
8020 == 6)
8021 || (sscanf(datetime,
8022 "%d %3s %d %d:%d:%d",
8023 &day,
8024 month_str,
8025 &year,
8026 &hour,
8027 &minute,
8028 &second)
8029 == 6)
8030 || (sscanf(datetime,
8031 "%*3s, %d %3s %d %d:%d:%d",
8032 &day,
8033 month_str,
8034 &year,
8035 &hour,
8036 &minute,
8037 &second)
8038 == 6)
8039 || (sscanf(datetime,
8040 "%d-%3s-%d %d:%d:%d",
8041 &day,
8042 month_str,
8043 &year,
8044 &hour,
8045 &minute,
8046 &second)
8047 == 6)) {
8048 month = get_month_index(month_str);
8049 if ((month >= 0) && (year >= 1970)) {
8050 memset(&tm, 0, sizeof(tm));
8051 tm.tm_year = year - 1900;
8052 tm.tm_mon = month;
8053 tm.tm_mday = day;
8054 tm.tm_hour = hour;
8055 tm.tm_min = minute;
8056 tm.tm_sec = second;
8057 result = timegm(&tm);
8058 }
8059 }
8060
8061 return result;
8062}
static int get_month_index(const char *s)
Definition civetweb.c:7989

References get_month_index().

Referenced by is_not_modified().

◆ parse_http_headers()

static int parse_http_headers ( char ** buf,
struct mg_header hdr[MG_MAX_HEADERS] )
static

Definition at line 10725 of file civetweb.c.

10726{
10727 int i;
10728 int num_headers = 0;
10729
10730 for (i = 0; i < (int)MG_MAX_HEADERS; i++) {
10731 char *dp = *buf;
10732
10733 /* Skip all ASCII characters (>SPACE, <127), to find a ':' */
10734 while ((*dp != ':') && (*dp >= 33) && (*dp <= 126)) {
10735 dp++;
10736 }
10737 if (dp == *buf) {
10738 /* End of headers reached. */
10739 break;
10740 }
10741
10742 /* Drop all spaces after header name before : */
10743 while (*dp == ' ') {
10744 *dp = 0;
10745 dp++;
10746 }
10747 if (*dp != ':') {
10748 /* This is not a valid field. */
10749 return -1;
10750 }
10751
10752 /* End of header key (*dp == ':') */
10753 /* Truncate here and set the key name */
10754 *dp = 0;
10755 hdr[i].name = *buf;
10756
10757 /* Skip all spaces */
10758 do {
10759 dp++;
10760 } while ((*dp == ' ') || (*dp == '\t'));
10761
10762 /* The rest of the line is the value */
10763 hdr[i].value = dp;
10764
10765 /* Find end of line */
10766 while ((*dp != 0) && (*dp != '\r') && (*dp != '\n')) {
10767 dp++;
10768 };
10769
10770 /* eliminate \r */
10771 if (*dp == '\r') {
10772 *dp = 0;
10773 dp++;
10774 if (*dp != '\n') {
10775 /* This is not a valid line. */
10776 return -1;
10777 }
10778 }
10779
10780 /* here *dp is either 0 or '\n' */
10781 /* in any case, we have a new header */
10782 num_headers = i + 1;
10783
10784 if (*dp) {
10785 *dp = 0;
10786 dp++;
10787 *buf = dp;
10788
10789 if ((dp[0] == '\r') || (dp[0] == '\n')) {
10790 /* This is the end of the header */
10791 break;
10792 }
10793 } else {
10794 *buf = dp;
10795 break;
10796 }
10797 }
10798 return num_headers;
10799}
#define MG_MAX_HEADERS
Definition civetweb.h:141

References MG_MAX_HEADERS.

Referenced by handle_cgi_request(), parse_http_request(), and parse_http_response().

◆ parse_http_request()

static int parse_http_request ( char * buf,
int len,
struct mg_request_info * ri )
static

Definition at line 10915 of file civetweb.c.

10916{
10917 int request_length;
10918 int init_skip = 0;
10919
10920 /* Reset attributes. DO NOT TOUCH is_ssl, remote_addr,
10921 * remote_port */
10922 ri->remote_user = ri->request_method = ri->request_uri = ri->http_version =
10923 NULL;
10924 ri->num_headers = 0;
10925
10926 /* RFC says that all initial whitespaces should be ignored */
10927 /* This included all leading \r and \n (isspace) */
10928 /* See table: http://www.cplusplus.com/reference/cctype/ */
10929 while ((len > 0) && isspace((unsigned char)*buf)) {
10930 buf++;
10931 len--;
10932 init_skip++;
10933 }
10934
10935 if (len == 0) {
10936 /* Incomplete request */
10937 return 0;
10938 }
10939
10940 /* Control characters are not allowed, including zero */
10941 if (iscntrl((unsigned char)*buf)) {
10942 return -1;
10943 }
10944
10945 /* Find end of HTTP header */
10946 request_length = get_http_header_len(buf, len);
10947 if (request_length <= 0) {
10948 return request_length;
10949 }
10950 buf[request_length - 1] = '\0';
10951
10952 if ((*buf == 0) || (*buf == '\r') || (*buf == '\n')) {
10953 return -1;
10954 }
10955
10956 /* The first word has to be the HTTP method */
10957 ri->request_method = buf;
10958
10959 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
10960 return -1;
10961 }
10962
10963 /* The second word is the URI */
10964 ri->request_uri = buf;
10965
10966 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
10967 return -1;
10968 }
10969
10970 /* Next would be the HTTP version */
10971 ri->http_version = buf;
10972
10973 if (skip_to_end_of_word_and_terminate(&buf, 1) <= 0) {
10974 return -1;
10975 }
10976
10977 /* Check for a valid HTTP version key */
10978 if (strncmp(ri->http_version, "HTTP/", 5) != 0) {
10979 /* Invalid request */
10980 return -1;
10981 }
10982 ri->http_version += 5;
10983
10984 /* Check for a valid http method */
10986 return -1;
10987 }
10988
10989 /* Parse all HTTP headers */
10991 if (ri->num_headers < 0) {
10992 /* Error while parsing headers */
10993 return -1;
10994 }
10995
10996 return request_length + init_skip;
10997}
static int skip_to_end_of_word_and_terminate(char **ppw, int eol)
Definition civetweb.c:10680
static int get_http_header_len(const char *buf, int buflen)
Definition civetweb.c:7952
static int is_valid_http_method(const char *method)
Definition civetweb.c:10899

References get_http_header_len(), mg_request_info::http_headers, mg_request_info::http_version, is_valid_http_method(), NULL, mg_request_info::num_headers, parse_http_headers(), mg_request_info::remote_user, mg_request_info::request_method, mg_request_info::request_uri, and skip_to_end_of_word_and_terminate().

Referenced by get_request().

◆ parse_http_response()

static int parse_http_response ( char * buf,
int len,
struct mg_response_info * ri )
static

Definition at line 11001 of file civetweb.c.

11002{
11003 int response_length;
11004 int init_skip = 0;
11005 char *tmp, *tmp2;
11006 long l;
11007
11008 /* Initialize elements. */
11009 ri->http_version = ri->status_text = NULL;
11010 ri->num_headers = ri->status_code = 0;
11011
11012 /* RFC says that all initial whitespaces should be ignored */
11013 /* This included all leading \r and \n (isspace) */
11014 /* See table: http://www.cplusplus.com/reference/cctype/ */
11015 while ((len > 0) && isspace((unsigned char)*buf)) {
11016 buf++;
11017 len--;
11018 init_skip++;
11019 }
11020
11021 if (len == 0) {
11022 /* Incomplete request */
11023 return 0;
11024 }
11025
11026 /* Control characters are not allowed, including zero */
11027 if (iscntrl((unsigned char)*buf)) {
11028 return -1;
11029 }
11030
11031 /* Find end of HTTP header */
11032 response_length = get_http_header_len(buf, len);
11033 if (response_length <= 0) {
11034 return response_length;
11035 }
11036 buf[response_length - 1] = '\0';
11037
11038 if ((*buf == 0) || (*buf == '\r') || (*buf == '\n')) {
11039 return -1;
11040 }
11041
11042 /* The first word is the HTTP version */
11043 /* Check for a valid HTTP version key */
11044 if (strncmp(buf, "HTTP/", 5) != 0) {
11045 /* Invalid request */
11046 return -1;
11047 }
11048 buf += 5;
11049 if (!isgraph((unsigned char)buf[0])) {
11050 /* Invalid request */
11051 return -1;
11052 }
11053 ri->http_version = buf;
11054
11055 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11056 return -1;
11057 }
11058
11059 /* The second word is the status as a number */
11060 tmp = buf;
11061
11062 if (skip_to_end_of_word_and_terminate(&buf, 0) <= 0) {
11063 return -1;
11064 }
11065
11066 l = strtol(tmp, &tmp2, 10);
11067 if ((l < 100) || (l >= 1000) || ((tmp2 - tmp) != 3) || (*tmp2 != 0)) {
11068 /* Everything else but a 3 digit code is invalid */
11069 return -1;
11070 }
11071 ri->status_code = (int)l;
11072
11073 /* The rest of the line is the status text */
11074 ri->status_text = buf;
11075
11076 /* Find end of status text */
11077 /* isgraph or isspace = isprint */
11078 while (isprint((unsigned char)*buf)) {
11079 buf++;
11080 }
11081 if ((*buf != '\r') && (*buf != '\n')) {
11082 return -1;
11083 }
11084 /* Terminate string and forward buf to next line */
11085 do {
11086 *buf = 0;
11087 buf++;
11088 } while (isspace((unsigned char)*buf));
11089
11090 /* Parse all HTTP headers */
11092 if (ri->num_headers < 0) {
11093 /* Error while parsing headers */
11094 return -1;
11095 }
11096
11097 return response_length + init_skip;
11098}
const char * status_text
Definition civetweb.h:193

References get_http_header_len(), mg_response_info::http_headers, mg_response_info::http_version, NULL, mg_response_info::num_headers, parse_http_headers(), skip_to_end_of_word_and_terminate(), mg_response_info::status_code, and mg_response_info::status_text.

Referenced by get_response().

◆ parse_match_net()

static int parse_match_net ( const struct vec * vec,
const union usa * sa,
int no_strict )
static

Definition at line 13966 of file civetweb.c.

13967{
13968 int n;
13969 unsigned int a, b, c, d, slash;
13970
13971 if (sscanf(vec->ptr, "%u.%u.%u.%u/%u%n", &a, &b, &c, &d, &slash, &n)
13972 != 5) { // NOLINT(cert-err34-c) 'sscanf' used to convert a string to an
13973 // integer value, but function will not report conversion
13974 // errors; consider using 'strtol' instead
13975 slash = 32;
13976 if (sscanf(vec->ptr, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n)
13977 != 4) { // NOLINT(cert-err34-c) 'sscanf' used to convert a string to
13978 // an integer value, but function will not report conversion
13979 // errors; consider using 'strtol' instead
13980 n = 0;
13981 }
13982 }
13983
13984 if ((n > 0) && ((size_t)n == vec->len)) {
13985 if ((a < 256) && (b < 256) && (c < 256) && (d < 256) && (slash < 33)) {
13986 /* IPv4 format */
13987 if (sa->sa.sa_family == AF_INET) {
13988 uint32_t ip = ntohl(sa->sin.sin_addr.s_addr);
13989 uint32_t net = ((uint32_t)a << 24) | ((uint32_t)b << 16)
13990 | ((uint32_t)c << 8) | (uint32_t)d;
13991 uint32_t mask = slash ? (0xFFFFFFFFu << (32 - slash)) : 0;
13992 return (ip & mask) == net;
13993 }
13994 return 0;
13995 }
13996 }
13997#if defined(USE_IPV6)
13998 else {
13999 char ad[50];
14000 const char *p;
14001
14002 if (sscanf(vec->ptr, "[%49[^]]]/%u%n", ad, &slash, &n) != 2) {
14003 slash = 128;
14004 if (sscanf(vec->ptr, "[%49[^]]]%n", ad, &n) != 1) {
14005 n = 0;
14006 }
14007 }
14008
14009 if ((n <= 0) && no_strict) {
14010 /* no square brackets? */
14011 p = strchr(vec->ptr, '/');
14012 if (p && (p < (vec->ptr + vec->len))) {
14013 if (((size_t)(p - vec->ptr) < sizeof(ad))
14014 && (sscanf(p, "/%u%n", &slash, &n) == 1)) {
14015 n += (int)(p - vec->ptr);
14016 mg_strlcpy(ad, vec->ptr, (size_t)(p - vec->ptr) + 1);
14017 } else {
14018 n = 0;
14019 }
14020 } else if (vec->len < sizeof(ad)) {
14021 n = (int)vec->len;
14022 slash = 128;
14023 mg_strlcpy(ad, vec->ptr, vec->len + 1);
14024 }
14025 }
14026
14027 if ((n > 0) && ((size_t)n == vec->len) && (slash < 129)) {
14028 p = ad;
14029 c = 0;
14030 /* zone indexes are unsupported, at least two colons are needed */
14031 while (isxdigit((unsigned char)*p) || (*p == '.') || (*p == ':')) {
14032 if (*(p++) == ':') {
14033 c++;
14034 }
14035 }
14036 if ((*p == '\0') && (c >= 2)) {
14037 struct sockaddr_in6 sin6;
14038 unsigned int i;
14039
14040 /* for strict validation, an actual IPv6 argument is needed */
14041 if (sa->sa.sa_family != AF_INET6) {
14042 return 0;
14043 }
14044 if (mg_inet_pton(AF_INET6, ad, &sin6, sizeof(sin6), 0)) {
14045 /* IPv6 format */
14046 for (i = 0; i < 16; i++) {
14047 uint8_t ip = sa->sin6.sin6_addr.s6_addr[i];
14048 uint8_t net = sin6.sin6_addr.s6_addr[i];
14049 uint8_t mask = 0;
14050
14051 if (8 * i + 8 < slash) {
14052 mask = 0xFFu;
14053 } else if (8 * i < slash) {
14054 mask = (uint8_t)(0xFFu << (8 * i + 8 - slash));
14055 }
14056 if ((ip & mask) != net) {
14057 return 0;
14058 }
14059 }
14060 return 1;
14061 }
14062 }
14063 }
14064 }
14065#else
14066 (void)no_strict;
14067#endif
14068
14069 /* malformed */
14070 return -1;
14071}
#define mask(n)

References vec::len, mask, mg_inet_pton(), mg_strlcpy(), vec::ptr, usa::sa, and usa::sin.

Referenced by check_acl(), and set_throttle().

◆ parse_port_string()

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

Definition at line 15501 of file civetweb.c.

15502{
15503 unsigned int a, b, c, d;
15504 unsigned port;
15505 unsigned long portUL;
15506 int ch, len;
15507 const char *cb;
15508 char *endptr;
15509#if defined(USE_IPV6)
15510 char buf[100] = {0};
15511#endif
15512
15513 /* MacOS needs that. If we do not zero it, subsequent bind() will fail.
15514 * Also, all-zeroes in the socket address means binding to all addresses
15515 * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */
15516 memset(so, 0, sizeof(*so));
15517 so->lsa.sin.sin_family = AF_INET;
15518 *ip_version = 0;
15519
15520 /* Initialize len as invalid. */
15521 port = 0;
15522 len = 0;
15523
15524 /* Test for different ways to format this string */
15525 if (sscanf(vec->ptr,
15526 "%u.%u.%u.%u:%u%n",
15527 &a,
15528 &b,
15529 &c,
15530 &d,
15531 &port,
15532 &len) // NOLINT(cert-err34-c) 'sscanf' used to convert a string
15533 // to an integer value, but function will not report
15534 // conversion errors; consider using 'strtol' instead
15535 == 5) {
15536 /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */
15537 so->lsa.sin.sin_addr.s_addr =
15538 htonl((a << 24) | (b << 16) | (c << 8) | d);
15539 so->lsa.sin.sin_port = htons((uint16_t)port);
15540 *ip_version = 4;
15541
15542#if defined(USE_IPV6)
15543 } else if (sscanf(vec->ptr, "[%49[^]]]:%u%n", buf, &port, &len) == 2
15544 && ((size_t)len <= vec->len)
15545 && mg_inet_pton(
15546 AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6), 0)) {
15547 /* IPv6 address, examples: see above */
15548 /* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton
15549 */
15550 so->lsa.sin6.sin6_port = htons((uint16_t)port);
15551 *ip_version = 6;
15552#endif
15553
15554 } else if ((vec->ptr[0] == '+')
15555 && (sscanf(vec->ptr + 1, "%u%n", &port, &len)
15556 == 1)) { // NOLINT(cert-err34-c) 'sscanf' used to convert a
15557 // string to an integer value, but function will not
15558 // report conversion errors; consider using 'strtol'
15559 // instead
15560
15561 /* Port is specified with a +, bind to IPv6 and IPv4, INADDR_ANY */
15562 /* Add 1 to len for the + character we skipped before */
15563 len++;
15564
15565#if defined(USE_IPV6)
15566 /* Set socket family to IPv6, do not use IPV6_V6ONLY */
15567 so->lsa.sin6.sin6_family = AF_INET6;
15568 so->lsa.sin6.sin6_port = htons((uint16_t)port);
15569 *ip_version = 4 + 6;
15570#else
15571 /* Bind to IPv4 only, since IPv6 is not built in. */
15572 so->lsa.sin.sin_port = htons((uint16_t)port);
15573 *ip_version = 4;
15574#endif
15575
15576 } else if (is_valid_port(portUL = strtoul(vec->ptr, &endptr, 0))
15577 && (vec->ptr != endptr)) {
15578 len = (int)(endptr - vec->ptr);
15579 port = (uint16_t)portUL;
15580 /* If only port is specified, bind to IPv4, INADDR_ANY */
15581 so->lsa.sin.sin_port = htons((uint16_t)port);
15582 *ip_version = 4;
15583
15584 } else if ((cb = strchr(vec->ptr, ':')) != NULL) {
15585 /* String could be a hostname. This check algorithm
15586 * will only work for RFC 952 compliant hostnames,
15587 * starting with a letter, containing only letters,
15588 * digits and hyphen ('-'). Newer specs may allow
15589 * more, but this is not guaranteed here, since it
15590 * may interfere with rules for port option lists. */
15591
15592 /* According to RFC 1035, hostnames are restricted to 255 characters
15593 * in total (63 between two dots). */
15594 char hostname[256];
15595 size_t hostnlen = (size_t)(cb - vec->ptr);
15596
15597 if ((hostnlen >= vec->len) || (hostnlen >= sizeof(hostname))) {
15598 /* This would be invalid in any case */
15599 *ip_version = 0;
15600 return 0;
15601 }
15602
15603 mg_strlcpy(hostname, vec->ptr, hostnlen + 1);
15604
15605 if (mg_inet_pton(
15606 AF_INET, hostname, &so->lsa.sin, sizeof(so->lsa.sin), 1)) {
15607 if (sscanf(cb + 1, "%u%n", &port, &len)
15608 == 1) { // NOLINT(cert-err34-c) 'sscanf' used to convert a
15609 // string to an integer value, but function will not
15610 // report conversion errors; consider using 'strtol'
15611 // instead
15612 *ip_version = 4;
15613 so->lsa.sin.sin_port = htons((uint16_t)port);
15614 len += (int)(hostnlen + 1);
15615 } else {
15616 len = 0;
15617 }
15618#if defined(USE_IPV6)
15619 } else if (mg_inet_pton(AF_INET6,
15620 hostname,
15621 &so->lsa.sin6,
15622 sizeof(so->lsa.sin6),
15623 1)) {
15624 if (sscanf(cb + 1, "%u%n", &port, &len) == 1) {
15625 *ip_version = 6;
15626 so->lsa.sin6.sin6_port = htons((uint16_t)port);
15627 len += (int)(hostnlen + 1);
15628 } else {
15629 len = 0;
15630 }
15631#endif
15632 } else {
15633 len = 0;
15634 }
15635
15636#if defined(USE_X_DOM_SOCKET)
15637
15638 } else if (vec->ptr[0] == 'x') {
15639 /* unix (linux) domain socket */
15640 if (vec->len < sizeof(so->lsa.sun.sun_path)) {
15641 len = vec->len;
15642 so->lsa.sun.sun_family = AF_UNIX;
15643 memset(so->lsa.sun.sun_path, 0, sizeof(so->lsa.sun.sun_path));
15644 memcpy(so->lsa.sun.sun_path, (char *)vec->ptr + 1, vec->len - 1);
15645 port = 0;
15646 *ip_version = 99;
15647 } else {
15648 /* String too long */
15649 len = 0;
15650 }
15651#endif
15652
15653 } else {
15654 /* Parsing failure. */
15655 len = 0;
15656 }
15657
15658 /* sscanf and the option splitting code ensure the following condition
15659 * Make sure the port is valid and vector ends with the port, 's' or 'r' */
15660 if ((len > 0) && is_valid_port(port)
15661 && (((size_t)len == vec->len) || (((size_t)len + 1) == vec->len))) {
15662 /* Next character after the port number */
15663 ch = ((size_t)len < vec->len) ? vec->ptr[len] : '\0';
15664 so->is_ssl = (ch == 's');
15665 so->ssl_redir = (ch == 'r');
15666 if ((ch == '\0') || (ch == 's') || (ch == 'r')) {
15667 return 1;
15668 }
15669 }
15670
15671 /* Reset ip_version to 0 if there is an error */
15672 *ip_version = 0;
15673 return 0;
15674}

References socket::is_ssl, is_valid_port(), vec::len, socket::lsa, mg_inet_pton(), mg_strlcpy(), NULL, vec::ptr, usa::sin, and socket::ssl_redir.

Referenced by set_ports_option().

◆ parse_range_header()

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

Definition at line 10169 of file civetweb.c.

10170{
10171 return sscanf(header,
10172 "bytes=%" INT64_FMT "-%" INT64_FMT,
10173 a,
10174 b); // NOLINT(cert-err34-c) 'sscanf' used to convert a string
10175 // to an integer value, but function will not report
10176 // conversion errors; consider using 'strtol' instead
10177}

References INT64_FMT.

Referenced by handle_static_file_request(), and put_file().

◆ prepare_cgi_environment()

static int prepare_cgi_environment ( struct mg_connection * conn,
const char * prog,
struct cgi_environment * env,
int cgi_config_idx )
static

Definition at line 11354 of file civetweb.c.

11358{
11359 const char *s;
11360 struct vec var_vec;
11361 char *p, src_addr[IP_ADDR_STR_LEN], http_var_name[128];
11362 int i, truncated, uri_len;
11363
11364 if ((conn == NULL) || (prog == NULL) || (env == NULL)) {
11365 return -1;
11366 }
11367
11368 env->conn = conn;
11370 env->bufused = 0;
11371 env->buf = (char *)mg_malloc_ctx(env->buflen, conn->phys_ctx);
11372 if (env->buf == NULL) {
11373 mg_cry_internal(conn,
11374 "%s: Not enough memory for environmental buffer",
11375 __func__);
11376 return -1;
11377 }
11379 env->varused = 0;
11380 env->var =
11381 (char **)mg_malloc_ctx(env->varlen * sizeof(char *), conn->phys_ctx);
11382 if (env->var == NULL) {
11383 mg_cry_internal(conn,
11384 "%s: Not enough memory for environmental variables",
11385 __func__);
11386 mg_free(env->buf);
11387 return -1;
11388 }
11389
11390 addenv(env, "SERVER_NAME=%s", conn->dom_ctx->config[AUTHENTICATION_DOMAIN]);
11391 addenv(env, "SERVER_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11392 addenv(env, "DOCUMENT_ROOT=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11393 addenv(env, "SERVER_SOFTWARE=CivetWeb/%s", mg_version());
11394
11395 /* Prepare the environment block */
11396 addenv(env, "%s", "GATEWAY_INTERFACE=CGI/1.1");
11397 addenv(env, "%s", "SERVER_PROTOCOL=HTTP/1.1");
11398 addenv(env, "%s", "REDIRECT_STATUS=200"); /* For PHP */
11399
11400 addenv(env, "SERVER_PORT=%d", conn->request_info.server_port);
11401
11402 sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
11403 addenv(env, "REMOTE_ADDR=%s", src_addr);
11404
11405 addenv(env, "REQUEST_METHOD=%s", conn->request_info.request_method);
11406 addenv(env, "REMOTE_PORT=%d", conn->request_info.remote_port);
11407
11408 addenv(env, "REQUEST_URI=%s", conn->request_info.request_uri);
11409 addenv(env, "LOCAL_URI=%s", conn->request_info.local_uri);
11410 addenv(env, "LOCAL_URI_RAW=%s", conn->request_info.local_uri_raw);
11411
11412 /* SCRIPT_NAME */
11413 uri_len = (int)strlen(conn->request_info.local_uri);
11414 if (conn->path_info == NULL) {
11415 if (conn->request_info.local_uri[uri_len - 1] != '/') {
11416 /* URI: /path_to_script/script.cgi */
11417 addenv(env, "SCRIPT_NAME=%s", conn->request_info.local_uri);
11418 } else {
11419 /* URI: /path_to_script/ ... using index.cgi */
11420 const char *index_file = strrchr(prog, '/');
11421 if (index_file) {
11422 addenv(env,
11423 "SCRIPT_NAME=%s%s",
11424 conn->request_info.local_uri,
11425 index_file + 1);
11426 }
11427 }
11428 } else {
11429 /* URI: /path_to_script/script.cgi/path_info */
11430 addenv(env,
11431 "SCRIPT_NAME=%.*s",
11432 uri_len - (int)strlen(conn->path_info),
11433 conn->request_info.local_uri);
11434 }
11435
11436 addenv(env, "SCRIPT_FILENAME=%s", prog);
11437 if (conn->path_info == NULL) {
11438 addenv(env, "PATH_TRANSLATED=%s", conn->dom_ctx->config[DOCUMENT_ROOT]);
11439 } else {
11440 addenv(env,
11441 "PATH_TRANSLATED=%s%s",
11443 conn->path_info);
11444 }
11445
11446 addenv(env, "HTTPS=%s", (conn->ssl == NULL) ? "off" : "on");
11447
11448 if ((s = mg_get_header(conn, "Content-Type")) != NULL) {
11449 addenv(env, "CONTENT_TYPE=%s", s);
11450 }
11451 if (conn->request_info.query_string != NULL) {
11452 addenv(env, "QUERY_STRING=%s", conn->request_info.query_string);
11453 }
11454 if ((s = mg_get_header(conn, "Content-Length")) != NULL) {
11455 addenv(env, "CONTENT_LENGTH=%s", s);
11456 }
11457 if ((s = getenv("PATH")) != NULL) {
11458 addenv(env, "PATH=%s", s);
11459 }
11460 if (conn->path_info != NULL) {
11461 addenv(env, "PATH_INFO=%s", conn->path_info);
11462 }
11463
11464 if (conn->status_code > 0) {
11465 /* CGI error handler should show the status code */
11466 addenv(env, "STATUS=%d", conn->status_code);
11467 }
11468
11469#if defined(_WIN32)
11470 if ((s = getenv("COMSPEC")) != NULL) {
11471 addenv(env, "COMSPEC=%s", s);
11472 }
11473 if ((s = getenv("SYSTEMROOT")) != NULL) {
11474 addenv(env, "SYSTEMROOT=%s", s);
11475 }
11476 if ((s = getenv("SystemDrive")) != NULL) {
11477 addenv(env, "SystemDrive=%s", s);
11478 }
11479 if ((s = getenv("ProgramFiles")) != NULL) {
11480 addenv(env, "ProgramFiles=%s", s);
11481 }
11482 if ((s = getenv("ProgramFiles(x86)")) != NULL) {
11483 addenv(env, "ProgramFiles(x86)=%s", s);
11484 }
11485#else
11486 if ((s = getenv("LD_LIBRARY_PATH")) != NULL) {
11487 addenv(env, "LD_LIBRARY_PATH=%s", s);
11488 }
11489#endif /* _WIN32 */
11490
11491 if ((s = getenv("PERLLIB")) != NULL) {
11492 addenv(env, "PERLLIB=%s", s);
11493 }
11494
11495 if (conn->request_info.remote_user != NULL) {
11496 addenv(env, "REMOTE_USER=%s", conn->request_info.remote_user);
11497 addenv(env, "%s", "AUTH_TYPE=Digest");
11498 }
11499
11500 /* Add all headers as HTTP_* variables */
11501 for (i = 0; i < conn->request_info.num_headers; i++) {
11502
11503 (void)mg_snprintf(conn,
11504 &truncated,
11505 http_var_name,
11506 sizeof(http_var_name),
11507 "HTTP_%s",
11508 conn->request_info.http_headers[i].name);
11509
11510 if (truncated) {
11511 mg_cry_internal(conn,
11512 "%s: HTTP header variable too long [%s]",
11513 __func__,
11514 conn->request_info.http_headers[i].name);
11515 continue;
11516 }
11517
11518 /* Convert variable name into uppercase, and change - to _ */
11519 for (p = http_var_name; *p != '\0'; p++) {
11520 if (*p == '-') {
11521 *p = '_';
11522 }
11523 *p = (char)toupper((unsigned char)*p);
11524 }
11525
11526 addenv(env,
11527 "%s=%s",
11528 http_var_name,
11530 }
11531
11532 /* Add user-specified variables */
11533 s = conn->dom_ctx->config[CGI_ENVIRONMENT + cgi_config_idx];
11534 while ((s = next_option(s, &var_vec, NULL)) != NULL) {
11535 addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr);
11536 }
11537
11538 env->var[env->varused] = NULL;
11539 env->buf[env->bufused] = '\0';
11540
11541 return 0;
11542}
static void addenv(struct cgi_environment *env, PRINTF_FORMAT_STRING(const char *fmt),...) PRINTF_ARGS(2
#define MAX_CGI_ENVIR_VARS
Definition civetweb.c:493

References addenv(), AUTHENTICATION_DOMAIN, cgi_environment::buf, cgi_environment::buflen, cgi_environment::bufused, CGI_ENVIRONMENT, CGI_ENVIRONMENT_SIZE, mg_connection::client, mg_domain_context::config, cgi_environment::conn, DOCUMENT_ROOT, mg_connection::dom_ctx, mg_request_info::http_headers, IP_ADDR_STR_LEN, vec::len, mg_request_info::local_uri, mg_request_info::local_uri_raw, MAX_CGI_ENVIR_VARS, mg_cry_internal, mg_free(), mg_get_header(), mg_malloc_ctx, mg_snprintf(), mg_version(), mg_header::name, next_option(), NULL, mg_request_info::num_headers, mg_connection::path_info, mg_connection::phys_ctx, vec::ptr, mg_request_info::query_string, mg_request_info::remote_port, mg_request_info::remote_user, mg_connection::request_info, mg_request_info::request_method, mg_request_info::request_uri, socket::rsa, s, mg_request_info::server_port, sockaddr_to_string(), mg_connection::ssl, mg_connection::status_code, mg_header::value, cgi_environment::var, cgi_environment::varlen, and cgi_environment::varused.

Referenced by handle_cgi_request().

◆ print_dav_dir_entry()

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

Definition at line 12745 of file civetweb.c.

12746{
12747 struct mg_connection *conn = (struct mg_connection *)data;
12748 if (!de || !conn
12749 || !print_props(
12750 conn, conn->request_info.local_uri, de->file_name, &de->file)) {
12751 /* stop scan */
12752 return 1;
12753 }
12754 return 0;
12755}

References de::file, de::file_name, mg_request_info::local_uri, print_props(), and mg_connection::request_info.

Referenced by handle_propfind().

◆ print_dir_entry()

static int print_dir_entry ( struct mg_connection * conn,
struct de * de )
static

Definition at line 9609 of file civetweb.c.

9610{
9611 size_t namesize, escsize, i;
9612 char *href, *esc, *p;
9613 char size[64], mod[64];
9614#if defined(REENTRANT_TIME)
9615 struct tm _tm;
9616 struct tm *tm = &_tm;
9617#else
9618 struct tm *tm;
9619#endif
9620
9621 /* Estimate worst case size for encoding and escaping */
9622 namesize = strlen(de->file_name) + 1;
9623 escsize = de->file_name[strcspn(de->file_name, "&<>")] ? namesize * 5 : 0;
9624 href = (char *)mg_malloc(namesize * 3 + escsize);
9625 if (href == NULL) {
9626 return -1;
9627 }
9628 mg_url_encode(de->file_name, href, namesize * 3);
9629 esc = NULL;
9630 if (escsize > 0) {
9631 /* HTML escaping needed */
9632 esc = href + namesize * 3;
9633 for (i = 0, p = esc; de->file_name[i]; i++, p += strlen(p)) {
9634 mg_strlcpy(p, de->file_name + i, 2);
9635 if (*p == '&') {
9636 strcpy(p, "&amp;");
9637 } else if (*p == '<') {
9638 strcpy(p, "&lt;");
9639 } else if (*p == '>') {
9640 strcpy(p, "&gt;");
9641 }
9642 }
9643 }
9644
9645 if (de->file.is_directory) {
9646 mg_snprintf(conn,
9647 NULL, /* Buffer is big enough */
9648 size,
9649 sizeof(size),
9650 "%s",
9651 "[DIRECTORY]");
9652 } else {
9653 /* We use (signed) cast below because MSVC 6 compiler cannot
9654 * convert unsigned __int64 to double. Sigh. */
9655 if (de->file.size < 1024) {
9656 mg_snprintf(conn,
9657 NULL, /* Buffer is big enough */
9658 size,
9659 sizeof(size),
9660 "%d",
9661 (int)de->file.size);
9662 } else if (de->file.size < 0x100000) {
9663 mg_snprintf(conn,
9664 NULL, /* Buffer is big enough */
9665 size,
9666 sizeof(size),
9667 "%.1fk",
9668 (double)de->file.size / 1024.0);
9669 } else if (de->file.size < 0x40000000) {
9670 mg_snprintf(conn,
9671 NULL, /* Buffer is big enough */
9672 size,
9673 sizeof(size),
9674 "%.1fM",
9675 (double)de->file.size / 1048576);
9676 } else {
9677 mg_snprintf(conn,
9678 NULL, /* Buffer is big enough */
9679 size,
9680 sizeof(size),
9681 "%.1fG",
9682 (double)de->file.size / 1073741824);
9683 }
9684 }
9685
9686 /* Note: mg_snprintf will not cause a buffer overflow above.
9687 * So, string truncation checks are not required here. */
9688
9689#if defined(REENTRANT_TIME)
9690 localtime_r(&de->file.last_modified, tm);
9691#else
9692 tm = localtime(&de->file.last_modified);
9693#endif
9694 if (tm != NULL) {
9695 strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm);
9696 } else {
9697 mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod));
9698 }
9699 mg_printf(conn,
9700 "<tr><td><a href=\"%s%s\">%s%s</a></td>"
9701 "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
9702 href,
9703 de->file.is_directory ? "/" : "",
9704 esc ? esc : de->file_name,
9705 de->file.is_directory ? "/" : "",
9706 mod,
9707 size);
9708 mg_free(href);
9709 return 0;
9710}

References de::file, de::file_name, mg_file_stat::is_directory, mg_file_stat::last_modified, mg_free(), mg_malloc(), mg_printf(), mg_snprintf(), mg_strlcpy(), mg_url_encode(), NULL, and mg_file_stat::size.

Referenced by handle_directory_request().

◆ print_props()

static int print_props ( struct mg_connection * conn,
const char * uri,
const char * name,
struct mg_file_stat * filep )
static

Definition at line 12660 of file civetweb.c.

12664{
12665 size_t i;
12666 char mtime[64];
12667 char link_buf[UTF8_PATH_MAX * 2]; /* Path + server root */
12668 char *link_concat;
12669 size_t link_concat_len;
12670
12671 if ((conn == NULL) || (uri == NULL) || (name == NULL) || (filep == NULL)) {
12672 return 0;
12673 }
12674
12675 link_concat_len = strlen(uri) + strlen(name) + 1;
12676 link_concat = mg_malloc_ctx(link_concat_len, conn->phys_ctx);
12677 if (!link_concat) {
12678 return 0;
12679 }
12680 strcpy(link_concat, uri);
12681 strcat(link_concat, name);
12682
12683 /* Get full link used in request */
12685 conn, link_buf, sizeof(link_buf), NULL, 0, link_concat);
12686
12687 /*
12688 OutputDebugStringA("print_props:\n uri: ");
12689 OutputDebugStringA(uri);
12690 OutputDebugStringA("\n name: ");
12691 OutputDebugStringA(name);
12692 OutputDebugStringA("\n link: ");
12693 OutputDebugStringA(link_buf);
12694 OutputDebugStringA("\n");
12695 */
12696
12697 gmt_time_string(mtime, sizeof(mtime), &filep->last_modified);
12698 mg_printf(conn,
12699 "<d:response>"
12700 "<d:href>%s</d:href>"
12701 "<d:propstat>"
12702 "<d:prop>"
12703 "<d:resourcetype>%s</d:resourcetype>"
12704 "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
12705 "<d:getlastmodified>%s</d:getlastmodified>"
12706 "<d:lockdiscovery>",
12707 link_buf,
12708 filep->is_directory ? "<d:collection/>" : "",
12709 filep->size,
12710 mtime);
12711
12712 for (i = 0; i < NUM_WEBDAV_LOCKS; i++) {
12713 struct twebdav_lock *dav_lock = conn->phys_ctx->webdav_lock;
12714 if (!strcmp(dav_lock[i].path, link_buf)) {
12715 mg_printf(conn,
12716 "<d:activelock>"
12717 "<d:locktype><d:write/></d:locktype>"
12718 "<d:lockscope><d:exclusive/></d:lockscope>"
12719 "<d:depth>0</d:depth>"
12720 "<d:owner>%s</d:owner>"
12721 "<d:timeout>Second-%u</d:timeout>"
12722 "<d:locktoken>"
12723 "<d:href>%s</d:href>"
12724 "</d:locktoken>"
12725 "</d:activelock>\n",
12726 dav_lock[i].user,
12727 (unsigned)LOCK_DURATION_S,
12728 dav_lock[i].token);
12729 }
12730 }
12731
12732 mg_printf(conn,
12733 "</d:lockdiscovery>"
12734 "</d:prop>"
12735 "<d:status>HTTP/1.1 200 OK</d:status>"
12736 "</d:propstat>"
12737 "</d:response>\n");
12738
12739 mg_free(link_concat);
12740 return 1;
12741}

References gmt_time_string(), INT64_FMT, mg_file_stat::is_directory, mg_file_stat::last_modified, LOCK_DURATION_S, mg_construct_local_link(), mg_free(), mg_malloc_ctx, mg_printf(), name, NULL, NUM_WEBDAV_LOCKS, twebdav_lock::path, mg_connection::phys_ctx, mg_file_stat::size, twebdav_lock::token, twebdav_lock::user, UTF8_PATH_MAX, and mg_context::webdav_lock.

Referenced by handle_propfind(), and print_dav_dir_entry().

◆ process_new_connection()

static void process_new_connection ( struct mg_connection * conn)
static

Definition at line 19368 of file civetweb.c.

19369{
19370 struct mg_request_info *ri = &conn->request_info;
19371 int keep_alive, discard_len;
19372 char ebuf[100];
19373 const char *hostend;
19374 int reqerr, uri_type;
19375
19376#if defined(USE_SERVER_STATS)
19377 ptrdiff_t mcon = mg_atomic_inc(&(conn->phys_ctx->active_connections));
19378 mg_atomic_add(&(conn->phys_ctx->total_connections), 1);
19379 mg_atomic_max(&(conn->phys_ctx->max_active_connections), mcon);
19380#endif
19381
19382 DEBUG_TRACE("Start processing connection from %s",
19384
19385 /* Loop over multiple requests sent using the same connection
19386 * (while "keep alive"). */
19387 do {
19388 DEBUG_TRACE("calling get_request (%i times for this connection)",
19389 conn->handled_requests + 1);
19390
19391#if defined(USE_SERVER_STATS)
19392 conn->conn_state = 3; /* ready */
19393#endif
19394
19395 if (!get_request(conn, ebuf, sizeof(ebuf), &reqerr)) {
19396 /* The request sent by the client could not be understood by
19397 * the server, or it was incomplete or a timeout. Send an
19398 * error message and close the connection. */
19399 if (reqerr > 0) {
19400 DEBUG_ASSERT(ebuf[0] != '\0');
19401 mg_send_http_error(conn, reqerr, "%s", ebuf);
19402 }
19403
19404 } else if (strcmp(ri->http_version, "1.0")
19405 && strcmp(ri->http_version, "1.1")) {
19406 /* HTTP/2 is not allowed here */
19407 mg_snprintf(conn,
19408 NULL, /* No truncation check for ebuf */
19409 ebuf,
19410 sizeof(ebuf),
19411 "Bad HTTP version: [%s]",
19412 ri->http_version);
19413 mg_send_http_error(conn, 505, "%s", ebuf);
19414 }
19415
19416 if (ebuf[0] == '\0') {
19417 uri_type = get_uri_type(conn->request_info.request_uri);
19418 switch (uri_type) {
19419 case 1:
19420 /* Asterisk */
19421 conn->request_info.local_uri_raw = 0;
19422 /* TODO: Deal with '*'. */
19423 break;
19424 case 2:
19425 /* relative uri */
19428 break;
19429 case 3:
19430 case 4:
19431 /* absolute uri (with/without port) */
19433 conn->request_info.request_uri, conn);
19434 if (hostend) {
19435 conn->request_info.local_uri_raw = hostend;
19436 } else {
19438 }
19439 break;
19440 default:
19441 mg_snprintf(conn,
19442 NULL, /* No truncation check for ebuf */
19443 ebuf,
19444 sizeof(ebuf),
19445 "Invalid URI");
19446 mg_send_http_error(conn, 400, "%s", ebuf);
19448 break;
19449 }
19450 conn->request_info.local_uri =
19451 (char *)conn->request_info.local_uri_raw;
19452 }
19453
19454 if (ebuf[0] != '\0') {
19455 conn->protocol_type = -1;
19456
19457 } else {
19458 /* HTTP/1 allows protocol upgrade */
19460
19461 if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
19462 /* This will occur, if a HTTP/1.1 request should be upgraded
19463 * to HTTP/2 - but not if HTTP/2 is negotiated using ALPN.
19464 * Since most (all?) major browsers only support HTTP/2 using
19465 * ALPN, this is hard to test and very low priority.
19466 * Deactivate it (at least for now).
19467 */
19469 }
19470 }
19471
19472 DEBUG_TRACE("http: %s, error: %s",
19473 (ri->http_version ? ri->http_version : "none"),
19474 (ebuf[0] ? ebuf : "none"));
19475
19476 if (ebuf[0] == '\0') {
19477 if (conn->request_info.local_uri) {
19478
19479 /* handle request to local server */
19481
19482 } else {
19483 /* TODO: handle non-local request (PROXY) */
19484 conn->must_close = 1;
19485 }
19486 } else {
19487 conn->must_close = 1;
19488 }
19489
19490 /* Response complete. Free header buffer */
19491 free_buffered_response_header_list(conn);
19492
19493 if (ri->remote_user != NULL) {
19494 mg_free((void *)ri->remote_user);
19495 /* Important! When having connections with and without auth
19496 * would cause double free and then crash */
19497 ri->remote_user = NULL;
19498 }
19499
19500 /* NOTE(lsm): order is important here. should_keep_alive() call
19501 * is using parsed request, which will be invalid after
19502 * memmove's below.
19503 * Therefore, memorize should_keep_alive() result now for later
19504 * use in loop exit condition. */
19505 /* Enable it only if this request is completely discardable. */
19506 keep_alive = STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)
19507 && should_keep_alive(conn) && (conn->content_len >= 0)
19508 && (conn->request_len > 0)
19509 && ((conn->is_chunked == 4)
19510 || (!conn->is_chunked
19511 && ((conn->consumed_content == conn->content_len)
19512 || ((conn->request_len + conn->content_len)
19513 <= conn->data_len))))
19514 && (conn->protocol_type == PROTOCOL_TYPE_HTTP1);
19515
19516 if (keep_alive) {
19517 /* Discard all buffered data for this request */
19518 discard_len =
19519 ((conn->request_len + conn->content_len) < conn->data_len)
19520 ? (int)(conn->request_len + conn->content_len)
19521 : conn->data_len;
19522 conn->data_len -= discard_len;
19523
19524 if (conn->data_len > 0) {
19525 DEBUG_TRACE("discard_len = %d", discard_len);
19526 memmove(conn->buf,
19527 conn->buf + discard_len,
19528 (size_t)conn->data_len);
19529 }
19530 }
19531
19532 DEBUG_ASSERT(conn->data_len >= 0);
19533 DEBUG_ASSERT(conn->data_len <= conn->buf_size);
19534
19535 if ((conn->data_len < 0) || (conn->data_len > conn->buf_size)) {
19536 DEBUG_TRACE("internal error: data_len = %li, buf_size = %li",
19537 (long int)conn->data_len,
19538 (long int)conn->buf_size);
19539 break;
19540 }
19541 conn->handled_requests++;
19542 } while (keep_alive);
19543
19544 DEBUG_TRACE("Done processing connection from %s (%f sec)",
19546 difftime(time(NULL), conn->conn_birth_time));
19547
19548 close_connection(conn);
19549
19550#if defined(USE_SERVER_STATS)
19551 mg_atomic_add(&(conn->phys_ctx->total_requests), conn->handled_requests);
19552 mg_atomic_dec(&(conn->phys_ctx->active_connections));
19553#endif
19554}
static int should_switch_to_protocol(const struct mg_connection *conn)
Definition civetweb.c:13912
static int get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
Definition civetweb.c:18645
static void handle_request_stat_log(struct mg_connection *conn)
Definition civetweb.c:6562
char remote_addr[48]
Definition civetweb.h:166

References mg_connection::buf, mg_connection::buf_size, close_connection(), mg_connection::conn_birth_time, mg_connection::consumed_content, mg_connection::content_len, mg_connection::data_len, DEBUG_ASSERT, DEBUG_TRACE, get_rel_url_at_current_server(), get_request(), get_uri_type(), handle_request_stat_log(), mg_connection::handled_requests, mg_request_info::http_version, mg_connection::is_chunked, mg_request_info::local_uri, mg_request_info::local_uri_raw, mg_atomic_dec(), mg_atomic_inc(), mg_free(), mg_send_http_error(), mg_snprintf(), mg_connection::must_close, NULL, mg_connection::phys_ctx, mg_connection::protocol_type, PROTOCOL_TYPE_HTTP1, PROTOCOL_TYPE_HTTP2, mg_request_info::remote_addr, mg_request_info::remote_user, mg_connection::request_info, mg_connection::request_len, mg_request_info::request_uri, should_keep_alive(), should_switch_to_protocol(), mg_context::stop_flag, and STOP_FLAG_IS_ZERO.

Referenced by worker_thread_run().

◆ produce_socket()

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

Definition at line 19661 of file civetweb.c.

19662{
19663 int queue_filled;
19664
19665 (void)pthread_mutex_lock(&ctx->thread_mutex);
19666
19667 queue_filled = ctx->sq_head - ctx->sq_tail;
19668
19669 /* If the queue is full, wait */
19670 while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)
19671 && (queue_filled >= ctx->sq_size)) {
19672 ctx->sq_blocked = 1; /* Status information: All threads busy */
19673#if defined(USE_SERVER_STATS)
19674 if (queue_filled > ctx->sq_max_fill) {
19675 ctx->sq_max_fill = queue_filled;
19676 }
19677#endif
19678 (void)pthread_cond_wait(&ctx->sq_empty, &ctx->thread_mutex);
19679 ctx->sq_blocked = 0; /* Not blocked now */
19680 queue_filled = ctx->sq_head - ctx->sq_tail;
19681 }
19682
19683 if (queue_filled < ctx->sq_size) {
19684 /* Copy socket to the queue and increment head */
19685 ctx->squeue[ctx->sq_head % ctx->sq_size] = *sp;
19686 ctx->sq_head++;
19687 DEBUG_TRACE("queued socket %d", sp ? sp->sock : -1);
19688 }
19689
19690 queue_filled = ctx->sq_head - ctx->sq_tail;
19691#if defined(USE_SERVER_STATS)
19692 if (queue_filled > ctx->sq_max_fill) {
19693 ctx->sq_max_fill = queue_filled;
19694 }
19695#endif
19696
19697 (void)pthread_cond_signal(&ctx->sq_full);
19698 (void)pthread_mutex_unlock(&ctx->thread_mutex);
19699}

References DEBUG_TRACE, socket::sock, mg_context::sq_blocked, mg_context::sq_empty, mg_context::sq_full, mg_context::sq_head, mg_context::sq_size, mg_context::sq_tail, mg_context::squeue, mg_context::stop_flag, STOP_FLAG_IS_ZERO, and mg_context::thread_mutex.

Referenced by accept_new_connection().

◆ pull_all()

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

Definition at line 6439 of file civetweb.c.

6440{
6441 int n, nread = 0;
6442 double timeout = -1.0;
6443 uint64_t start_time = 0, now = 0, timeout_ns = 0;
6444
6445 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
6446 timeout = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]) / 1000.0;
6447 }
6448 if (timeout <= 0.0) {
6449 timeout = strtod(config_options[REQUEST_TIMEOUT].default_value, NULL)
6450 / 1000.0;
6451 }
6452 start_time = mg_get_current_time_ns();
6453 timeout_ns = (uint64_t)(timeout * 1.0E9);
6454
6455 while ((len > 0) && STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6456 n = pull_inner(fp, conn, buf + nread, len, timeout);
6457 if (n == -2) {
6458 if (nread == 0) {
6459 nread = -1; /* Propagate the error */
6460 }
6461 break;
6462 } else if (n == -1) {
6463 /* timeout */
6464 if (timeout >= 0.0) {
6465 now = mg_get_current_time_ns();
6466 if ((now - start_time) <= timeout_ns) {
6467 continue;
6468 }
6469 }
6470 break;
6471 } else if (n == 0) {
6472 break; /* No more data to read */
6473 } else {
6474 nread += n;
6475 len -= n;
6476 }
6477 }
6478
6479 return nread;
6480}

References mg_domain_context::config, config_options, mg_connection::dom_ctx, mg_get_current_time_ns(), NULL, mg_connection::phys_ctx, pull_inner(), REQUEST_TIMEOUT, mg_context::stop_flag, and STOP_FLAG_IS_ZERO.

Referenced by handle_cgi_request(), and mg_read_inner().

◆ pull_inner()

static int pull_inner ( FILE * fp,
struct mg_connection * conn,
char * buf,
int len,
double timeout )
static

Definition at line 6210 of file civetweb.c.

6215{
6216 int nread, err = 0;
6217
6218#if defined(_WIN32)
6219 typedef int len_t;
6220#else
6221 typedef size_t len_t;
6222#endif
6223
6224 /* We need an additional wait loop around this, because in some cases
6225 * with TLSwe may get data from the socket but not from SSL_read.
6226 * In this case we need to repeat at least once.
6227 */
6228
6229 if (fp != NULL) {
6230 /* Use read() instead of fread(), because if we're reading from the
6231 * CGI pipe, fread() may block until IO buffer is filled up. We
6232 * cannot afford to block and must pass all read bytes immediately
6233 * to the client. */
6234 nread = (int)read(fileno(fp), buf, (size_t)len);
6235
6236 err = (nread < 0) ? ERRNO : 0;
6237 if ((nread == 0) && (len > 0)) {
6238 /* Should get data, but got EOL */
6239 return -2;
6240 }
6241
6242#if defined(USE_MBEDTLS)
6243 } else if (conn->ssl != NULL) {
6244 struct mg_pollfd pfd[1];
6245 int to_read;
6246 int pollres;
6247
6248 to_read = mbedtls_ssl_get_bytes_avail(conn->ssl);
6249
6250 if (to_read > 0) {
6251 /* We already know there is no more data buffered in conn->buf
6252 * but there is more available in the SSL layer. So don't poll
6253 * conn->client.sock yet. */
6254
6255 pollres = 1;
6256 if (to_read > len)
6257 to_read = len;
6258 } else {
6259 pfd[0].fd = conn->client.sock;
6260 pfd[0].events = POLLIN;
6261
6262 to_read = len;
6263
6264 pollres = mg_poll(pfd,
6265 1,
6266 (int)(timeout * 1000.0),
6267 &(conn->phys_ctx->stop_flag));
6268
6269 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6270 return -2;
6271 }
6272 }
6273
6274 if (pollres > 0) {
6275 nread = mbed_ssl_read(conn->ssl, (unsigned char *)buf, to_read);
6276 if (nread <= 0) {
6277 if ((nread == MBEDTLS_ERR_SSL_WANT_READ)
6278 || (nread == MBEDTLS_ERR_SSL_WANT_WRITE)
6279 || nread == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) {
6280 nread = 0;
6281 } else {
6282 fprintf(stderr, "SSL read failed, error %d\n", nread);
6283 return -2;
6284 }
6285 } else {
6286 err = 0;
6287 }
6288
6289 } else if (pollres < 0) {
6290 /* Error */
6291 return -2;
6292 } else {
6293 /* pollres = 0 means timeout */
6294 nread = 0;
6295 }
6296
6297#elif !defined(NO_SSL)
6298 } else if (conn->ssl != NULL) {
6299 int ssl_pending;
6300 struct mg_pollfd pfd[1];
6301 int pollres;
6302
6303 if ((ssl_pending = SSL_pending(conn->ssl)) > 0) {
6304 /* We already know there is no more data buffered in conn->buf
6305 * but there is more available in the SSL layer. So don't poll
6306 * conn->client.sock yet. */
6307 if (ssl_pending > len) {
6308 ssl_pending = len;
6309 }
6310 pollres = 1;
6311 } else {
6312 pfd[0].fd = conn->client.sock;
6313 pfd[0].events = POLLIN;
6314 pollres = mg_poll(pfd,
6315 1,
6316 (int)(timeout * 1000.0),
6317 &(conn->phys_ctx->stop_flag));
6318 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6319 return -2;
6320 }
6321 }
6322 if (pollres > 0) {
6323 ERR_clear_error();
6324 nread =
6325 SSL_read(conn->ssl, buf, (ssl_pending > 0) ? ssl_pending : len);
6326 if (nread <= 0) {
6327 err = SSL_get_error(conn->ssl, nread);
6328 if ((err == SSL_ERROR_SYSCALL) && (nread == -1)) {
6329 err = ERRNO;
6330 } else if ((err == SSL_ERROR_WANT_READ)
6331 || (err == SSL_ERROR_WANT_WRITE)) {
6332 nread = 0;
6333 } else {
6334 /* All errors should return -2 */
6335 DEBUG_TRACE("SSL_read() failed, error %d", err);
6336 ERR_clear_error();
6337 return -2;
6338 }
6339 ERR_clear_error();
6340 } else {
6341 err = 0;
6342 }
6343 } else if (pollres < 0) {
6344 /* Error */
6345 return -2;
6346 } else {
6347 /* pollres = 0 means timeout */
6348 nread = 0;
6349 }
6350#endif
6351
6352 } else {
6353 struct mg_pollfd pfd[1];
6354 int pollres;
6355
6356 pfd[0].fd = conn->client.sock;
6357 pfd[0].events = POLLIN;
6358 pollres = mg_poll(pfd,
6359 1,
6360 (int)(timeout * 1000.0),
6361 &(conn->phys_ctx->stop_flag));
6362 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6363 return -2;
6364 }
6365 if (pollres > 0) {
6366 nread = (int)recv(conn->client.sock, buf, (len_t)len, 0);
6367 err = (nread < 0) ? ERRNO : 0;
6368 if (nread <= 0) {
6369 /* shutdown of the socket at client side */
6370 return -2;
6371 }
6372 } else if (pollres < 0) {
6373 /* error calling poll */
6374 return -2;
6375 } else {
6376 /* pollres = 0 means timeout */
6377 nread = 0;
6378 }
6379 }
6380
6381 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
6382 return -2;
6383 }
6384
6385 if ((nread > 0) || ((nread == 0) && (len == 0))) {
6386 /* some data has been read, or no data was requested */
6387 return nread;
6388 }
6389
6390 if (nread < 0) {
6391 /* socket error - check errno */
6392#if defined(_WIN32)
6393 if (err == WSAEWOULDBLOCK) {
6394 /* TODO (low): check if this is still required */
6395 /* standard case if called from close_socket_gracefully */
6396 return -2;
6397 } else if (err == WSAETIMEDOUT) {
6398 /* TODO (low): check if this is still required */
6399 /* timeout is handled by the while loop */
6400 return 0;
6401 } else if (err == WSAECONNABORTED) {
6402 /* See https://www.chilkatsoft.com/p/p_299.asp */
6403 return -2;
6404 } else {
6405 DEBUG_TRACE("recv() failed, error %d", err);
6406 return -2;
6407 }
6408#else
6409 /* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases,
6410 * if the timeout is reached and if the socket was set to non-
6411 * blocking in close_socket_gracefully, so we can not distinguish
6412 * here. We have to wait for the timeout in both cases for now.
6413 */
6414 if (ERROR_TRY_AGAIN(err)) {
6415 /* TODO (low): check if this is still required */
6416 /* EAGAIN/EWOULDBLOCK:
6417 * standard case if called from close_socket_gracefully
6418 * => should return -1 */
6419 /* or timeout occurred
6420 * => the code must stay in the while loop */
6421
6422 /* EINTR can be generated on a socket with a timeout set even
6423 * when SA_RESTART is effective for all relevant signals
6424 * (see signal(7)).
6425 * => stay in the while loop */
6426 } else {
6427 DEBUG_TRACE("recv() failed, error %d", err);
6428 return -2;
6429 }
6430#endif
6431 }
6432
6433 /* Timeout occurred, but no data available. */
6434 return -1;
6435}

References mg_connection::client, DEBUG_TRACE, ERRNO, ERROR_TRY_AGAIN, mg_poll(), mg_pollfd, NULL, mg_connection::phys_ctx, socket::sock, mg_connection::ssl, mg_context::stop_flag, and STOP_FLAG_IS_ZERO.

Referenced by close_socket_gracefully(), pull_all(), and read_message().

◆ push_all()

static int push_all ( struct mg_context * ctx,
FILE * fp,
SOCKET sock,
SSL * ssl,
const char * buf,
int len )
static

Definition at line 6162 of file civetweb.c.

6168{
6169 double timeout = -1.0;
6170 int n, nwritten = 0;
6171
6172 if (ctx == NULL) {
6173 return -1;
6174 }
6175
6176 if (ctx->dd.config[REQUEST_TIMEOUT]) {
6177 timeout = atoi(ctx->dd.config[REQUEST_TIMEOUT]) / 1000.0;
6178 }
6179 if (timeout <= 0.0) {
6180 timeout = strtod(config_options[REQUEST_TIMEOUT].default_value, NULL)
6181 / 1000.0;
6182 }
6183
6184 while ((len > 0) && STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6185 n = push_inner(ctx, fp, sock, ssl, buf + nwritten, len, timeout);
6186 if (n < 0) {
6187 if (nwritten == 0) {
6188 nwritten = -1; /* Propagate the error */
6189 }
6190 break;
6191 } else if (n == 0) {
6192 break; /* No more data to write */
6193 } else {
6194 nwritten += n;
6195 len -= n;
6196 }
6197 }
6198
6199 return nwritten;
6200}
static int push_inner(struct mg_context *ctx, FILE *fp, SOCKET sock, SSL *ssl, const char *buf, int len, double timeout)
Definition civetweb.c:6003

References mg_domain_context::config, config_options, mg_context::dd, NULL, push_inner(), REQUEST_TIMEOUT, mg_context::stop_flag, and STOP_FLAG_IS_ZERO.

Referenced by forward_body_data(), and mg_write().

◆ push_inner()

static int push_inner ( struct mg_context * ctx,
FILE * fp,
SOCKET sock,
SSL * ssl,
const char * buf,
int len,
double timeout )
static

Definition at line 6003 of file civetweb.c.

6010{
6011 uint64_t start = 0, now = 0, timeout_ns = 0;
6012 int n, err;
6013 unsigned ms_wait = SOCKET_TIMEOUT_QUANTUM; /* Sleep quantum in ms */
6014
6015#if defined(_WIN32)
6016 typedef int len_t;
6017#else
6018 typedef size_t len_t;
6019#endif
6020
6021 if (timeout > 0) {
6022 now = mg_get_current_time_ns();
6023 start = now;
6024 timeout_ns = (uint64_t)(timeout * 1.0E9);
6025 }
6026
6027 if (ctx == NULL) {
6028 return -2;
6029 }
6030
6031#if defined(NO_SSL) && !defined(USE_MBEDTLS)
6032 if (ssl) {
6033 return -2;
6034 }
6035#endif
6036
6037 /* Try to read until it succeeds, fails, times out, or the server
6038 * shuts down. */
6039 for (;;) {
6040
6041#if defined(USE_MBEDTLS)
6042 if (ssl != NULL) {
6043 n = mbed_ssl_write(ssl, (const unsigned char *)buf, len);
6044 if (n <= 0) {
6045 if ((n == MBEDTLS_ERR_SSL_WANT_READ)
6046 || (n == MBEDTLS_ERR_SSL_WANT_WRITE)
6047 || n == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) {
6048 n = 0;
6049 } else {
6050 fprintf(stderr, "SSL write failed, error %d\n", n);
6051 return -2;
6052 }
6053 } else {
6054 err = 0;
6055 }
6056 } else
6057#elif !defined(NO_SSL)
6058 if (ssl != NULL) {
6059 ERR_clear_error();
6060 n = SSL_write(ssl, buf, len);
6061 if (n <= 0) {
6062 err = SSL_get_error(ssl, n);
6063 if ((err == SSL_ERROR_SYSCALL) && (n == -1)) {
6064 err = ERRNO;
6065 } else if ((err == SSL_ERROR_WANT_READ)
6066 || (err == SSL_ERROR_WANT_WRITE)) {
6067 n = 0;
6068 } else {
6069 DEBUG_TRACE("SSL_write() failed, error %d", err);
6070 ERR_clear_error();
6071 return -2;
6072 }
6073 ERR_clear_error();
6074 } else {
6075 err = 0;
6076 }
6077 } else
6078#endif
6079
6080 if (fp != NULL) {
6081 n = (int)fwrite(buf, 1, (size_t)len, fp);
6082 if (ferror(fp)) {
6083 n = -1;
6084 err = ERRNO;
6085 } else {
6086 err = 0;
6087 }
6088 } else {
6089 n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL);
6090 err = (n < 0) ? ERRNO : 0;
6091 if (ERROR_TRY_AGAIN(err)) {
6092 err = 0;
6093 n = 0;
6094 }
6095 if (n < 0) {
6096 /* shutdown of the socket at client side */
6097 return -2;
6098 }
6099 }
6100
6101 if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6102 return -2;
6103 }
6104
6105 if ((n > 0) || ((n == 0) && (len == 0))) {
6106 /* some data has been read, or no data was requested */
6107 return n;
6108 }
6109 if (n < 0) {
6110 /* socket error - check errno */
6111 DEBUG_TRACE("send() failed, error %d", err);
6112
6113 /* TODO (mid): error handling depending on the error code.
6114 * These codes are different between Windows and Linux.
6115 * Currently there is no problem with failing send calls,
6116 * if there is a reproducible situation, it should be
6117 * investigated in detail.
6118 */
6119 return -2;
6120 }
6121
6122 /* Only in case n=0 (timeout), repeat calling the write function */
6123
6124 /* If send failed, wait before retry */
6125 if (fp != NULL) {
6126 /* For files, just wait a fixed time.
6127 * Maybe it helps, maybe not. */
6128 mg_sleep(5);
6129 } else {
6130 /* For sockets, wait for the socket using poll */
6131 struct mg_pollfd pfd[1];
6132 int pollres;
6133
6134 pfd[0].fd = sock;
6135 pfd[0].events = POLLOUT;
6136 pollres = mg_poll(pfd, 1, (int)(ms_wait), &(ctx->stop_flag));
6137 if (!STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
6138 return -2;
6139 }
6140 if (pollres > 0) {
6141 continue;
6142 }
6143 }
6144
6145 if (timeout > 0) {
6146 now = mg_get_current_time_ns();
6147 if ((now - start) > timeout_ns) {
6148 /* Timeout */
6149 break;
6150 }
6151 }
6152 }
6153
6154 (void)err; /* Avoid unused warning if NO_SSL is set and DEBUG_TRACE is not
6155 used */
6156
6157 return -1;
6158}
#define MSG_NOSIGNAL
Definition civetweb.c:1735

References DEBUG_TRACE, ERRNO, ERROR_TRY_AGAIN, fwrite(), mg_get_current_time_ns(), mg_poll(), mg_pollfd, mg_sleep, MSG_NOSIGNAL, NULL, SOCKET_TIMEOUT_QUANTUM, mg_context::stop_flag, and STOP_FLAG_IS_ZERO.

Referenced by push_all().

◆ put_dir()

static int put_dir ( struct mg_connection * conn,
const char * path )
static

Definition at line 10572 of file civetweb.c.

10573{
10574 char buf[UTF8_PATH_MAX];
10575 const char *s, *p;
10576 struct mg_file file = STRUCT_FILE_INITIALIZER;
10577 size_t len;
10578 int res = 1;
10579
10580 for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
10581 len = (size_t)(p - path);
10582 if (len >= sizeof(buf)) {
10583 /* path too long */
10584 res = -1;
10585 break;
10586 }
10587 memcpy(buf, path, len);
10588 buf[len] = '\0';
10589
10590 /* Try to create intermediate directory */
10591 DEBUG_TRACE("mkdir(%s)", buf);
10592 if (!mg_stat(conn, buf, &file.stat) && mg_mkdir(conn, buf, 0755) != 0) {
10593 /* path does not exist and can not be created */
10594 res = -2;
10595 break;
10596 }
10597
10598 /* Is path itself a directory? */
10599 if (p[1] == '\0') {
10600 res = 0;
10601 }
10602 }
10603
10604 return res;
10605}

References DEBUG_TRACE, mg_mkdir, mg_stat(), NULL, s, mg_file::stat, STRUCT_FILE_INITIALIZER, and UTF8_PATH_MAX.

Referenced by mg_store_body(), and put_file().

◆ put_file()

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

Definition at line 12158 of file civetweb.c.

12159{
12160 struct mg_file file = STRUCT_FILE_INITIALIZER;
12161 const char *range;
12162 int64_t r1, r2;
12163 int rc;
12164
12165 if (conn == NULL) {
12166 return;
12167 }
12168
12169 DEBUG_TRACE("store %s", path);
12170
12171 if (mg_stat(conn, path, &file.stat)) {
12172 /* File already exists */
12173 conn->status_code = 200;
12174
12175 if (file.stat.is_directory) {
12176 /* This is an already existing directory,
12177 * so there is nothing to do for the server. */
12178 rc = 0;
12179
12180 } else {
12181 /* File exists and is not a directory. */
12182 /* Can it be replaced? */
12183
12184 /* Check if the server may write this file */
12185 if (access(path, W_OK) == 0) {
12186 /* Access granted */
12187 rc = 1;
12188 } else {
12190 conn,
12191 403,
12192 "Error: Put not possible\nReplacing %s is not allowed",
12193 path);
12194 return;
12195 }
12196 }
12197 } else {
12198 /* File should be created */
12199 conn->status_code = 201;
12200 rc = put_dir(conn, path);
12201 }
12202
12203 if (rc == 0) {
12204 /* put_dir returns 0 if path is a directory */
12205
12206 /* Create response */
12210 mg_response_header_add(conn, "Content-Length", "0", -1);
12211
12212 /* Send all headers - there is no body */
12214
12215 /* Request to create a directory has been fulfilled successfully.
12216 * No need to put a file. */
12217 return;
12218 }
12219
12220 if (rc == -1) {
12221 /* put_dir returns -1 if the path is too long */
12222 mg_send_http_error(conn,
12223 414,
12224 "Error: Path too long\nput_dir(%s): %s",
12225 path,
12226 strerror(ERRNO));
12227 return;
12228 }
12229
12230 if (rc == -2) {
12231 /* put_dir returns -2 if the directory can not be created */
12232 mg_send_http_error(conn,
12233 500,
12234 "Error: Can not create directory\nput_dir(%s): %s",
12235 path,
12236 strerror(ERRNO));
12237 return;
12238 }
12239
12240 /* A file should be created or overwritten. */
12241 /* Currently CivetWeb does not need read+write access. */
12242 if (!mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &file)
12243 || file.access.fp == NULL) {
12244 (void)mg_fclose(&file.access);
12245 mg_send_http_error(conn,
12246 500,
12247 "Error: Can not create file\nfopen(%s): %s",
12248 path,
12249 strerror(ERRNO));
12250 return;
12251 }
12252
12253 fclose_on_exec(&file.access, conn);
12254 range = mg_get_header(conn, "Content-Range");
12255 r1 = r2 = 0;
12256 if ((range != NULL) && parse_range_header(range, &r1, &r2) > 0) {
12257 conn->status_code = 206; /* Partial content */
12258 if (0 != fseeko(file.access.fp, r1, SEEK_SET)) {
12259 mg_send_http_error(conn,
12260 500,
12261 "Error: Internal error processing file %s",
12262 path);
12263 return;
12264 }
12265 }
12266
12267 if (!forward_body_data(conn, file.access.fp, INVALID_SOCKET, NULL)) {
12268 /* forward_body_data failed.
12269 * The error code has already been sent to the client,
12270 * and conn->status_code is already set. */
12271 (void)mg_fclose(&file.access);
12272 return;
12273 }
12274
12275 if (mg_fclose(&file.access) != 0) {
12276 /* fclose failed. This might have different reasons, but a likely
12277 * one is "no space on disk", http 507. */
12278 conn->status_code = 507;
12279 }
12280
12281 /* Create response (status_code has been set before) */
12285 mg_response_header_add(conn, "Content-Length", "0", -1);
12286
12287 /* Send all headers - there is no body */
12289}

References mg_file::access, DEBUG_TRACE, ERRNO, fclose_on_exec(), forward_body_data(), mg_file_access::fp, INVALID_SOCKET, mg_file_stat::is_directory, mg_fclose(), mg_fopen(), MG_FOPEN_MODE_WRITE, mg_get_header(), mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_send_http_error(), mg_stat(), NULL, parse_range_header(), put_dir(), send_additional_header(), send_no_cache_header(), mg_file::stat, mg_connection::status_code, and STRUCT_FILE_INITIALIZER.

Referenced by handle_request().

◆ read_auth_file()

static int read_auth_file ( struct mg_file * filep,
struct read_auth_file_struct * workdata,
int depth )
static

Definition at line 8733 of file civetweb.c.

8736{
8737 int is_authorized = 0;
8738 struct mg_file fp;
8739 size_t l;
8740
8741 if (!filep || !workdata || (0 == depth)) {
8742 return 0;
8743 }
8744
8745 /* Loop over passwords file */
8746 while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep) != NULL) {
8747 l = strlen(workdata->buf);
8748 while (l > 0) {
8749 if (isspace((unsigned char)workdata->buf[l - 1])
8750 || iscntrl((unsigned char)workdata->buf[l - 1])) {
8751 l--;
8752 workdata->buf[l] = 0;
8753 } else
8754 break;
8755 }
8756 if (l < 1) {
8757 continue;
8758 }
8759
8760 workdata->f_user = workdata->buf;
8761
8762 if (workdata->f_user[0] == ':') {
8763 /* user names may not contain a ':' and may not be empty,
8764 * so lines starting with ':' may be used for a special purpose
8765 */
8766 if (workdata->f_user[1] == '#') {
8767 /* :# is a comment */
8768 continue;
8769 } else if (!strncmp(workdata->f_user + 1, "include=", 8)) {
8770 if (mg_fopen(workdata->conn,
8771 workdata->f_user + 9,
8773 &fp)) {
8774 is_authorized = read_auth_file(&fp, workdata, depth - 1);
8775 (void)mg_fclose(
8776 &fp.access); /* ignore error on read only file */
8777
8778 /* No need to continue processing files once we have a
8779 * match, since nothing will reset it back
8780 * to 0.
8781 */
8782 if (is_authorized) {
8783 return is_authorized;
8784 }
8785 } else {
8786 mg_cry_internal(workdata->conn,
8787 "%s: cannot open authorization file: %s",
8788 __func__,
8789 workdata->buf);
8790 }
8791 continue;
8792 }
8793 /* everything is invalid for the moment (might change in the
8794 * future) */
8795 mg_cry_internal(workdata->conn,
8796 "%s: syntax error in authorization file: %s",
8797 __func__,
8798 workdata->buf);
8799 continue;
8800 }
8801
8802 workdata->f_domain = strchr(workdata->f_user, ':');
8803 if (workdata->f_domain == NULL) {
8804 mg_cry_internal(workdata->conn,
8805 "%s: syntax error in authorization file: %s",
8806 __func__,
8807 workdata->buf);
8808 continue;
8809 }
8810 *(char *)(workdata->f_domain) = 0;
8811 (workdata->f_domain)++;
8812
8813 workdata->f_ha1 = strchr(workdata->f_domain, ':');
8814 if (workdata->f_ha1 == NULL) {
8815 mg_cry_internal(workdata->conn,
8816 "%s: syntax error in authorization file: %s",
8817 __func__,
8818 workdata->buf);
8819 continue;
8820 }
8821 *(char *)(workdata->f_ha1) = 0;
8822 (workdata->f_ha1)++;
8823
8824 if (!strcmp(workdata->ah.user, workdata->f_user)
8825 && !strcmp(workdata->domain, workdata->f_domain)) {
8826 switch (workdata->ah.type) {
8827 case 1: /* Basic */
8828 {
8829 char md5[33];
8830 mg_md5(md5,
8831 workdata->f_user,
8832 ":",
8833 workdata->domain,
8834 ":",
8835 workdata->ah.plain_password,
8836 NULL);
8837 return 0 == memcmp(workdata->f_ha1, md5, 33);
8838 }
8839 case 2: /* Digest */
8840 return check_password_digest(
8841 workdata->conn->request_info.request_method,
8842 workdata->f_ha1,
8843 workdata->ah.uri,
8844 workdata->ah.nonce,
8845 workdata->ah.nc,
8846 workdata->ah.cnonce,
8847 workdata->ah.qop,
8848 workdata->ah.response);
8849 default: /* None/Other/Unknown */
8850 return 0;
8851 }
8852 }
8853 }
8854
8855 return is_authorized;
8856}
static int check_password_digest(const char *method, const char *ha1, const char *uri, const char *nonce, const char *nc, const char *cnonce, const char *qop, const char *response)
Definition civetweb.c:8427
static const char * mg_fgets(char *buf, size_t size, struct mg_file *filep)
Definition civetweb.c:8696
const char * f_ha1
Definition civetweb.c:8728
const char * f_domain
Definition civetweb.c:8727
const char * f_user
Definition civetweb.c:8726
const char * domain
Definition civetweb.c:8724

References mg_file::access, read_auth_file_struct::ah, read_auth_file_struct::buf, check_password_digest(), ah::cnonce, read_auth_file_struct::conn, depth, read_auth_file_struct::domain, read_auth_file_struct::f_domain, read_auth_file_struct::f_ha1, read_auth_file_struct::f_user, mg_cry_internal, mg_fclose(), mg_fgets(), mg_fopen(), MG_FOPEN_MODE_READ, mg_md5(), ah::nc, ah::nonce, NULL, ah::plain_password, ah::qop, read_auth_file(), mg_connection::request_info, mg_request_info::request_method, ah::response, ah::type, ah::uri, and ah::user.

Referenced by authorize(), and read_auth_file().

◆ read_message()

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

Definition at line 11107 of file civetweb.c.

11112{
11113 int request_len, n = 0;
11114 struct timespec last_action_time;
11115 double request_timeout;
11116
11117 if (!conn) {
11118 return 0;
11119 }
11120
11121 memset(&last_action_time, 0, sizeof(last_action_time));
11122
11123 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
11124 /* value of request_timeout is in seconds, config in milliseconds */
11125 request_timeout =
11126 strtod(conn->dom_ctx->config[REQUEST_TIMEOUT], NULL) / 1000.0;
11127 } else {
11128 request_timeout =
11129 strtod(config_options[REQUEST_TIMEOUT].default_value, NULL)
11130 / 1000.0;
11131 }
11132 if (conn->handled_requests > 0) {
11133 if (conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT]) {
11134 request_timeout =
11135 strtod(conn->dom_ctx->config[KEEP_ALIVE_TIMEOUT], NULL)
11136 / 1000.0;
11137 }
11138 }
11139
11140 request_len = get_http_header_len(buf, *nread);
11141
11142 while (request_len == 0) {
11143 /* Full request not yet received */
11144 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
11145 /* Server is to be stopped. */
11146 return -1;
11147 }
11148
11149 if (*nread >= bufsiz) {
11150 /* Request too long */
11151 return -2;
11152 }
11153
11154 n = pull_inner(
11155 fp, conn, buf + *nread, bufsiz - *nread, request_timeout);
11156 if (n == -2) {
11157 /* Receive error */
11158 return -1;
11159 }
11160
11161 /* update clock after every read request */
11162 clock_gettime(CLOCK_MONOTONIC, &last_action_time);
11163
11164 if (n > 0) {
11165 *nread += n;
11166 request_len = get_http_header_len(buf, *nread);
11167 }
11168
11169 if ((request_len == 0) && (request_timeout >= 0)) {
11170 if (mg_difftimespec(&last_action_time, &(conn->req_time))
11171 > request_timeout) {
11172 /* Timeout */
11173 return -1;
11174 }
11175 }
11176 }
11177
11178 return request_len;
11179}

References mg_domain_context::config, config_options, mg_connection::dom_ctx, get_http_header_len(), mg_connection::handled_requests, KEEP_ALIVE_TIMEOUT, mg_difftimespec(), NULL, mg_connection::phys_ctx, pull_inner(), mg_connection::req_time, REQUEST_TIMEOUT, mg_context::stop_flag, and STOP_FLAG_IS_ZERO.

Referenced by get_message(), and handle_cgi_request().

◆ redirect_to_https_port()

static void redirect_to_https_port ( struct mg_connection * conn,
int port )
static

Definition at line 14235 of file civetweb.c.

14236{
14237 char target_url[MG_BUF_LEN];
14238 int truncated = 0;
14239 const char *expect_proto =
14240 (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET) ? "wss" : "https";
14241
14242 /* Use "308 Permanent Redirect" */
14243 int redirect_code = 308;
14244
14245 /* In any case, close the current connection */
14246 conn->must_close = 1;
14247
14248 /* Send host, port, uri and (if it exists) ?query_string */
14250 conn, target_url, sizeof(target_url), expect_proto, port, NULL)
14251 < 0) {
14252 truncated = 1;
14253 } else if (conn->request_info.query_string != NULL) {
14254 size_t slen1 = strlen(target_url);
14255 size_t slen2 = strlen(conn->request_info.query_string);
14256 if ((slen1 + slen2 + 2) < sizeof(target_url)) {
14257 target_url[slen1] = '?';
14258 memcpy(target_url + slen1 + 1,
14260 slen2);
14261 target_url[slen1 + slen2 + 1] = 0;
14262 } else {
14263 truncated = 1;
14264 }
14265 }
14266
14267 /* Check overflow in location buffer (will not occur if MG_BUF_LEN
14268 * is used as buffer size) */
14269 if (truncated) {
14270 mg_send_http_error(conn, 500, "%s", "Redirect URL too long");
14271 return;
14272 }
14273
14274 /* Use redirect helper function */
14275 mg_send_http_redirect(conn, target_url, redirect_code);
14276}

References MG_BUF_LEN, mg_construct_local_link(), mg_send_http_error(), mg_send_http_redirect(), mg_connection::must_close, NULL, mg_connection::protocol_type, PROTOCOL_TYPE_WEBSOCKET, mg_request_info::query_string, and mg_connection::request_info.

Referenced by handle_request().

◆ refresh_trust()

static int refresh_trust ( struct mg_connection * conn)
static

Definition at line 16377 of file civetweb.c.

16378{
16379 struct stat cert_buf;
16380 int64_t t = 0;
16381 const char *pem;
16382 const char *chain;
16383 int should_verify_peer;
16384
16385 if ((pem = conn->dom_ctx->config[SSL_CERTIFICATE]) == NULL) {
16386 /* If pem is NULL and conn->phys_ctx->callbacks.init_ssl is not,
16387 * refresh_trust still can not work. */
16388 return 0;
16389 }
16390 chain = conn->dom_ctx->config[SSL_CERTIFICATE_CHAIN];
16391 if (chain == NULL) {
16392 /* pem is not NULL here */
16393 chain = pem;
16394 }
16395 if (*chain == 0) {
16396 chain = NULL;
16397 }
16398
16399 if (stat(pem, &cert_buf) != -1) {
16400 t = (int64_t)cert_buf.st_mtime;
16401 }
16402
16404 if ((t != 0) && (conn->dom_ctx->ssl_cert_last_mtime != t)) {
16405 conn->dom_ctx->ssl_cert_last_mtime = t;
16406
16407 should_verify_peer = 0;
16408 if (conn->dom_ctx->config[SSL_DO_VERIFY_PEER] != NULL) {
16410 == 0) {
16411 should_verify_peer = 1;
16413 "optional")
16414 == 0) {
16415 should_verify_peer = 1;
16416 }
16417 }
16418
16419 if (should_verify_peer) {
16420 char *ca_path = conn->dom_ctx->config[SSL_CA_PATH];
16421 char *ca_file = conn->dom_ctx->config[SSL_CA_FILE];
16422 if (SSL_CTX_load_verify_locations(conn->dom_ctx->ssl_ctx,
16423 ca_file,
16424 ca_path)
16425 != 1) {
16428 conn->phys_ctx,
16429 "SSL_CTX_load_verify_locations error: %s "
16430 "ssl_verify_peer requires setting "
16431 "either ssl_ca_path or ssl_ca_file. Is any of them "
16432 "present in "
16433 "the .conf file?",
16434 ssl_error());
16435 return 0;
16436 }
16437 }
16438
16439 if (ssl_use_pem_file(conn->phys_ctx, conn->dom_ctx, pem, chain) == 0) {
16441 return 0;
16442 }
16443 }
16445
16446 return 1;
16447}
int64_t ssl_cert_last_mtime
Definition civetweb.c:2273

References mg_domain_context::config, mg_connection::dom_ctx, mg_cry_ctx_internal, mg_lock_context(), mg_strcasecmp(), mg_unlock_context(), NULL, mg_connection::phys_ctx, SSL_CA_FILE, SSL_CA_PATH, mg_domain_context::ssl_cert_last_mtime, SSL_CERTIFICATE, SSL_CERTIFICATE_CHAIN, mg_domain_context::ssl_ctx, SSL_DO_VERIFY_PEER, ssl_error(), and ssl_use_pem_file().

Referenced by sslize().

◆ release_handler_ref()

static void release_handler_ref ( struct mg_connection * conn,
struct mg_handler_info * handler_info )
static

Definition at line 14687 of file civetweb.c.

14689{
14690 if (handler_info != NULL) {
14691 /* Use context lock for ref counter */
14693 handler_info->refcount--;
14695 }
14696}

References mg_lock_context(), mg_unlock_context(), NULL, mg_connection::phys_ctx, and mg_handler_info::refcount.

Referenced by handle_request().

◆ remove_bad_file()

static void remove_bad_file ( const struct mg_connection * conn,
const char * path )
static

Definition at line 10609 of file civetweb.c.

10610{
10611 int r = mg_remove(conn, path);
10612 if (r != 0) {
10613 mg_cry_internal(conn,
10614 "%s: Cannot remove invalid file %s",
10615 __func__,
10616 path);
10617 }
10618}

References mg_cry_internal, and mg_remove.

Referenced by mg_store_body().

◆ remove_directory()

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

Definition at line 9836 of file civetweb.c.

9837{
9838 char path[UTF8_PATH_MAX];
9839 struct dirent *dp;
9840 DIR *dirp;
9841 struct de de;
9842 int truncated;
9843 int ok = 1;
9844
9845 if ((dirp = mg_opendir(conn, dir)) == NULL) {
9846 return 0;
9847 } else {
9848
9849 while ((dp = mg_readdir(dirp)) != NULL) {
9850 /* Do not show current dir (but show hidden files as they will
9851 * also be removed) */
9852 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
9853 continue;
9854 }
9855
9857 conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
9858
9859 /* If we don't memset stat structure to zero, mtime will have
9860 * garbage and strftime() will segfault later on in
9861 * print_dir_entry(). memset is required only if mg_stat()
9862 * fails. For more details, see
9863 * http://code.google.com/p/mongoose/issues/detail?id=79 */
9864 memset(&de.file, 0, sizeof(de.file));
9865
9866 if (truncated) {
9867 /* Do not delete anything shorter */
9868 ok = 0;
9869 continue;
9870 }
9871
9872 if (!mg_stat(conn, path, &de.file)) {
9873 mg_cry_internal(conn,
9874 "%s: mg_stat(%s) failed: %s",
9875 __func__,
9876 path,
9877 strerror(ERRNO));
9878 ok = 0;
9879 }
9880
9881 if (de.file.is_directory) {
9882 if (remove_directory(conn, path) == 0) {
9883 ok = 0;
9884 }
9885 } else {
9886 /* This will fail file is the file is in memory */
9887 if (mg_remove(conn, path) == 0) {
9888 ok = 0;
9889 }
9890 }
9891 }
9892 (void)mg_closedir(dirp);
9893
9894 IGNORE_UNUSED_RESULT(rmdir(dir));
9895 }
9896
9897 return ok;
9898}
#define mg_readdir(x)
Definition civetweb.c:923
#define mg_opendir(conn, x)
Definition civetweb.c:921
#define mg_closedir(x)
Definition civetweb.c:922

References ERRNO, de::file, IGNORE_UNUSED_RESULT, mg_file_stat::is_directory, mg_closedir, mg_cry_internal, mg_opendir, mg_readdir, mg_remove, mg_snprintf(), mg_stat(), NULL, remove_directory(), and UTF8_PATH_MAX.

Referenced by delete_file(), and remove_directory().

◆ remove_dot_segments()

static void remove_dot_segments ( char * inout)
static

Definition at line 8069 of file civetweb.c.

8070{
8071 /* Windows backend protection
8072 * (https://tools.ietf.org/html/rfc3986#section-7.3): Replace backslash
8073 * in URI by slash */
8074 char *out_end = inout;
8075 char *in = inout;
8076
8077 if (!in) {
8078 /* Param error. */
8079 return;
8080 }
8081
8082 while (*in) {
8083 if (*in == '\\') {
8084 *in = '/';
8085 }
8086 in++;
8087 }
8088
8089 /* Algorithm "remove_dot_segments" from
8090 * https://tools.ietf.org/html/rfc3986#section-5.2.4 */
8091 /* Step 1:
8092 * The input buffer is initialized.
8093 * The output buffer is initialized to the empty string.
8094 */
8095 in = inout;
8096
8097 /* Step 2:
8098 * While the input buffer is not empty, loop as follows:
8099 */
8100 /* Less than out_end of the inout buffer is used as output, so keep
8101 * condition: out_end <= in */
8102 while (*in) {
8103 /* Step 2a:
8104 * If the input buffer begins with a prefix of "../" or "./",
8105 * then remove that prefix from the input buffer;
8106 */
8107 if (!strncmp(in, "../", 3)) {
8108 in += 3;
8109 } else if (!strncmp(in, "./", 2)) {
8110 in += 2;
8111 }
8112 /* otherwise */
8113 /* Step 2b:
8114 * if the input buffer begins with a prefix of "/./" or "/.",
8115 * where "." is a complete path segment, then replace that
8116 * prefix with "/" in the input buffer;
8117 */
8118 else if (!strncmp(in, "/./", 3)) {
8119 in += 2;
8120 } else if (!strcmp(in, "/.")) {
8121 in[1] = 0;
8122 }
8123 /* otherwise */
8124 /* Step 2c:
8125 * if the input buffer begins with a prefix of "/../" or "/..",
8126 * where ".." is a complete path segment, then replace that
8127 * prefix with "/" in the input buffer and remove the last
8128 * segment and its preceding "/" (if any) from the output
8129 * buffer;
8130 */
8131 else if (!strncmp(in, "/../", 4)) {
8132 in += 3;
8133 if (inout != out_end) {
8134 /* remove last segment */
8135 do {
8136 out_end--;
8137 } while ((inout != out_end) && (*out_end != '/'));
8138 }
8139 } else if (!strcmp(in, "/..")) {
8140 in[1] = 0;
8141 if (inout != out_end) {
8142 /* remove last segment */
8143 do {
8144 out_end--;
8145 } while ((inout != out_end) && (*out_end != '/'));
8146 }
8147 }
8148 /* otherwise */
8149 /* Step 2d:
8150 * if the input buffer consists only of "." or "..", then remove
8151 * that from the input buffer;
8152 */
8153 else if (!strcmp(in, ".") || !strcmp(in, "..")) {
8154 *in = 0;
8155 }
8156 /* otherwise */
8157 /* Step 2e:
8158 * move the first path segment in the input buffer to the end of
8159 * the output buffer, including the initial "/" character (if
8160 * any) and any subsequent characters up to, but not including,
8161 * the next "/" character or the end of the input buffer.
8162 */
8163 else {
8164 do {
8165 *out_end = *in;
8166 out_end++;
8167 in++;
8168 } while ((*in != 0) && (*in != '/'));
8169 }
8170 }
8171
8172 /* Step 3:
8173 * Finally, the output buffer is returned as the result of
8174 * remove_dot_segments.
8175 */
8176 /* Terminate output */
8177 *out_end = 0;
8178
8179 /* For Windows, the files/folders "x" and "x." (with a dot but without
8180 * extension) are identical. Replace all "./" by "/" and remove a "." at
8181 * the end. Also replace all "//" by "/". Repeat until there is no "./"
8182 * or "//" anymore.
8183 */
8184 out_end = in = inout;
8185 while (*in) {
8186 if (*in == '.') {
8187 /* remove . at the end or preceding of / */
8188 char *in_ahead = in;
8189 do {
8190 in_ahead++;
8191 } while (*in_ahead == '.');
8192 if (*in_ahead == '/') {
8193 in = in_ahead;
8194 if ((out_end != inout) && (out_end[-1] == '/')) {
8195 /* remove generated // */
8196 out_end--;
8197 }
8198 } else if (*in_ahead == 0) {
8199 in = in_ahead;
8200 } else {
8201 do {
8202 *out_end++ = '.';
8203 in++;
8204 } while (in != in_ahead);
8205 }
8206 } else if (*in == '/') {
8207 /* replace // by / */
8208 *out_end++ = '/';
8209 do {
8210 in++;
8211 } while (*in == '/');
8212 } else {
8213 *out_end++ = *in;
8214 in++;
8215 }
8216 }
8217 *out_end = 0;
8218}

Referenced by dav_move_file(), and handle_request().

◆ reset_per_request_attributes()

static void reset_per_request_attributes ( struct mg_connection * conn)
static

Definition at line 17676 of file civetweb.c.

17677{
17678 if (!conn) {
17679 return;
17680 }
17681
17682 conn->num_bytes_sent = conn->consumed_content = 0;
17683
17684 conn->path_info = NULL;
17685 conn->status_code = -1;
17686 conn->content_len = -1;
17687 conn->is_chunked = 0;
17688 conn->must_close = 0;
17689 conn->request_len = 0;
17690 conn->request_state = 0;
17691 conn->throttle = 0;
17692 conn->accept_gzip = 0;
17693
17698 conn->response_info.status_code = 0;
17699
17703
17704 /* Free cleaned local URI (if any) */
17705 if (conn->request_info.local_uri != conn->request_info.local_uri_raw) {
17706 mg_free((void *)conn->request_info.local_uri);
17707 conn->request_info.local_uri = NULL;
17708 }
17709 conn->request_info.local_uri = NULL;
17710
17711#if defined(USE_SERVER_STATS)
17712 conn->processing_time = 0;
17713#endif
17714}

References mg_connection::accept_gzip, mg_connection::consumed_content, mg_connection::content_len, mg_request_info::content_length, mg_response_info::content_length, mg_request_info::http_version, mg_response_info::http_version, mg_connection::is_chunked, mg_request_info::local_uri, mg_request_info::local_uri_raw, mg_free(), mg_connection::must_close, NULL, mg_connection::num_bytes_sent, mg_request_info::num_headers, mg_response_info::num_headers, mg_connection::path_info, mg_request_info::remote_user, mg_connection::request_info, mg_connection::request_len, mg_request_info::request_method, mg_connection::request_state, mg_request_info::request_uri, mg_connection::response_info, mg_connection::status_code, mg_response_info::status_code, mg_response_info::status_text, and mg_connection::throttle.

Referenced by get_message().

◆ scan_directory()

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

Definition at line 9777 of file civetweb.c.

9781{
9782 char path[UTF8_PATH_MAX];
9783 struct dirent *dp;
9784 DIR *dirp;
9785 struct de de;
9786 int truncated;
9787
9788 if ((dirp = mg_opendir(conn, dir)) == NULL) {
9789 return 0;
9790 } else {
9791
9792 while ((dp = mg_readdir(dirp)) != NULL) {
9793 /* Do not show current dir and hidden files */
9794 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")
9795 || must_hide_file(conn, dp->d_name)) {
9796 continue;
9797 }
9798
9800 conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name);
9801
9802 /* If we don't memset stat structure to zero, mtime will have
9803 * garbage and strftime() will segfault later on in
9804 * print_dir_entry(). memset is required only if mg_stat()
9805 * fails. For more details, see
9806 * http://code.google.com/p/mongoose/issues/detail?id=79 */
9807 memset(&de.file, 0, sizeof(de.file));
9808
9809 if (truncated) {
9810 /* If the path is not complete, skip processing. */
9811 continue;
9812 }
9813
9814 if (!mg_stat(conn, path, &de.file)) {
9815 mg_cry_internal(conn,
9816 "%s: mg_stat(%s) failed: %s",
9817 __func__,
9818 path,
9819 strerror(ERRNO));
9820 }
9821 de.file_name = dp->d_name;
9822 if (cb(&de, data)) {
9823 /* stopped */
9824 break;
9825 }
9826 }
9827 (void)mg_closedir(dirp);
9828 }
9829 return 1;
9830}

References ERRNO, de::file, de::file_name, mg_closedir, mg_cry_internal, mg_opendir, mg_readdir, mg_snprintf(), mg_stat(), must_hide_file(), NULL, and UTF8_PATH_MAX.

Referenced by handle_directory_request(), and handle_propfind().

◆ send_additional_header()

static void send_additional_header ( struct mg_connection * conn)
static

Definition at line 4122 of file civetweb.c.

4123{
4124 const char *header = conn->dom_ctx->config[ADDITIONAL_HEADER];
4125
4126#if !defined(NO_SSL)
4127 if (conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]) {
4128 long max_age = atol(conn->dom_ctx->config[STRICT_HTTPS_MAX_AGE]);
4129 if (max_age >= 0) {
4130 char val[64];
4131 mg_snprintf(conn,
4132 NULL,
4133 val,
4134 sizeof(val),
4135 "max-age=%lu",
4136 (unsigned long)max_age);
4137 mg_response_header_add(conn, "Strict-Transport-Security", val, -1);
4138 }
4139 }
4140#endif
4141
4142 if (header && header[0]) {
4143 mg_response_header_add_lines(conn, header);
4144 }
4145}

References ADDITIONAL_HEADER, mg_domain_context::config, mg_connection::dom_ctx, mg_response_header_add(), mg_response_header_add_lines(), mg_snprintf(), NULL, and STRICT_HTTPS_MAX_AGE.

Referenced by dav_lock_file(), dav_mkcol(), dav_proppatch(), delete_file(), handle_directory_request(), handle_not_modified_static_file_request(), handle_propfind(), handle_ssi_file_request(), handle_static_file_request(), mg_send_http_error_impl(), mg_send_http_ok(), mg_send_http_redirect(), put_file(), send_authorization_request(), and send_options().

◆ send_authorization_request()

static void send_authorization_request ( struct mg_connection * conn,
const char * realm )
static

Definition at line 8974 of file civetweb.c.

8975{
8976 uint64_t nonce = (uint64_t)(conn->phys_ctx->start_time);
8977 int trunc = 0;
8978 char buf[128];
8979
8980 if (!realm) {
8981 realm = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
8982 }
8983
8985 nonce += conn->dom_ctx->nonce_count;
8986 ++conn->dom_ctx->nonce_count;
8988
8989 nonce ^= conn->dom_ctx->auth_nonce_mask;
8990 conn->must_close = 1;
8991
8992 /* Create 401 response */
8993 mg_response_header_start(conn, 401);
8996 mg_response_header_add(conn, "Content-Length", "0", -1);
8997
8998 /* Content for "WWW-Authenticate" header */
8999 mg_snprintf(conn,
9000 &trunc,
9001 buf,
9002 sizeof(buf),
9003 "Digest qop=\"auth\", realm=\"%s\", "
9004 "nonce=\"%" UINT64_FMT "\"",
9005 realm,
9006 nonce);
9007
9008 if (!trunc) {
9009 /* !trunc should always be true */
9010 mg_response_header_add(conn, "WWW-Authenticate", buf, -1);
9011 }
9012
9013 /* Send all headers */
9015}

References mg_domain_context::auth_nonce_mask, AUTHENTICATION_DOMAIN, mg_domain_context::config, mg_connection::dom_ctx, mg_lock_context(), mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_snprintf(), mg_unlock_context(), mg_connection::must_close, mg_domain_context::nonce_count, mg_connection::phys_ctx, send_additional_header(), send_no_cache_header(), mg_context::start_time, and UINT64_FMT.

Referenced by handle_request(), and mg_send_digest_access_authentication_request().

◆ send_cors_header()

static void send_cors_header ( struct mg_connection * conn)
static

Definition at line 4149 of file civetweb.c.

4150{
4151 const char *origin_hdr = mg_get_header(conn, "Origin");
4152 const char *cors_orig_cfg =
4154
4155 if (cors_orig_cfg && *cors_orig_cfg && origin_hdr && *origin_hdr) {
4156 /* Cross-origin resource sharing (CORS), see
4157 * http://www.html5rocks.com/en/tutorials/cors/,
4158 * http://www.html5rocks.com/static/images/cors_server_flowchart.png
4159 * CORS preflight is not supported for files. */
4161 "Access-Control-Allow-Origin",
4162 cors_orig_cfg,
4163 -1);
4164 }
4165}

References ACCESS_CONTROL_ALLOW_ORIGIN, mg_domain_context::config, mg_connection::dom_ctx, mg_get_header(), and mg_response_header_add().

Referenced by handle_ssi_file_request(), handle_static_file_request(), mg_send_http_error_impl(), mg_send_http_ok(), and mg_send_http_redirect().

◆ send_file_data()

static void send_file_data ( struct mg_connection * conn,
struct mg_file * filep,
int64_t offset,
int64_t len,
int no_buffering )
static

Definition at line 10055 of file civetweb.c.

10060{
10061 char buf[MG_BUF_LEN];
10062 int to_read, num_read, num_written;
10063 int64_t size;
10064
10065 if (!filep || !conn) {
10066 return;
10067 }
10068
10069 /* Sanity check the offset */
10070 size = (filep->stat.size > INT64_MAX) ? INT64_MAX
10071 : (int64_t)(filep->stat.size);
10072 offset = (offset < 0) ? 0 : ((offset > size) ? size : offset);
10073
10074 if (len > 0 && filep->access.fp != NULL) {
10075 /* file stored on disk */
10076#if defined(__linux__)
10077 /* sendfile is only available for Linux */
10078 if ((conn->ssl == 0) && (conn->throttle == 0)
10079 && (!mg_strcasecmp(conn->dom_ctx->config[ALLOW_SENDFILE_CALL],
10080 "yes"))) {
10081 off_t sf_offs = (off_t)offset;
10082 ssize_t sf_sent;
10083 int sf_file = fileno(filep->access.fp);
10084 int loop_cnt = 0;
10085
10086 do {
10087 /* 2147479552 (0x7FFFF000) is a limit found by experiment on
10088 * 64 bit Linux (2^31 minus one memory page of 4k?). */
10089 size_t sf_tosend =
10090 (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000);
10091 sf_sent =
10092 sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend);
10093 if (sf_sent > 0) {
10094 len -= sf_sent;
10095 offset += sf_sent;
10096 } else if (loop_cnt == 0) {
10097 /* This file can not be sent using sendfile.
10098 * This might be the case for pseudo-files in the
10099 * /sys/ and /proc/ file system.
10100 * Use the regular user mode copy code instead. */
10101 break;
10102 } else if (sf_sent == 0) {
10103 /* No error, but 0 bytes sent. May be EOF? */
10104 return;
10105 }
10106 loop_cnt++;
10107
10108 } while ((len > 0) && (sf_sent >= 0));
10109
10110 if (sf_sent > 0) {
10111 return; /* OK */
10112 }
10113
10114 /* sf_sent<0 means error, thus fall back to the classic way */
10115 /* This is always the case, if sf_file is not a "normal" file,
10116 * e.g., for sending data from the output of a CGI process. */
10117 offset = (int64_t)sf_offs;
10118 }
10119#endif
10120 if ((offset > 0) && (fseeko(filep->access.fp, offset, SEEK_SET) != 0)) {
10121 mg_cry_internal(conn,
10122 "%s: fseeko() failed: %s",
10123 __func__,
10124 strerror(ERRNO));
10126 conn,
10127 500,
10128 "%s",
10129 "Error: Unable to access file at requested position.");
10130 } else {
10131 while (len > 0) {
10132 /* Calculate how much to read from the file into the buffer. */
10133 /* If no_buffering is set, we should not wait until the
10134 * CGI->Server buffer is filled, but send everything
10135 * immediately. In theory buffering could be turned off using
10136 * setbuf(filep->access.fp, NULL);
10137 * setvbuf(filep->access.fp, NULL, _IONBF, 0);
10138 * but in practice this does not work. A "Linux only" solution
10139 * may be to use select(). The only portable way is to read byte
10140 * by byte, but this is quite inefficient from a performance
10141 * point of view. */
10142 to_read = no_buffering ? 1 : sizeof(buf);
10143 if ((int64_t)to_read > len) {
10144 to_read = (int)len;
10145 }
10146
10147 /* Read from file, exit the loop on error */
10148 if ((num_read =
10149 (int)fread(buf, 1, (size_t)to_read, filep->access.fp))
10150 <= 0) {
10151 break;
10152 }
10153
10154 /* Send read bytes to the client, exit the loop on error */
10155 if ((num_written = mg_write(conn, buf, (size_t)num_read))
10156 != num_read) {
10157 break;
10158 }
10159
10160 /* Both read and were successful, adjust counters */
10161 len -= num_written;
10162 }
10163 }
10164 }
10165}
size_t fread(void *, size_t, size_t, FILE *)

References mg_file::access, mg_connection::client, mg_domain_context::config, mg_connection::dom_ctx, ERRNO, mg_file_access::fp, fread(), INT64_MAX, MG_BUF_LEN, mg_cry_internal, mg_send_http_error(), mg_strcasecmp(), mg_write(), NULL, mg_file_stat::size, socket::sock, mg_connection::ssl, mg_file::stat, and mg_connection::throttle.

Referenced by do_ssi_exec(), do_ssi_include(), handle_cgi_request(), handle_static_file_request(), and mg_send_file_body().

◆ send_no_cache_header()

static void send_no_cache_header ( struct mg_connection * conn)
static

Definition at line 4056 of file civetweb.c.

4057{
4058 /* Send all current and obsolete cache opt-out directives. */
4060 "Cache-Control",
4061 "no-cache, no-store, "
4062 "must-revalidate, private, max-age=0",
4063 -1);
4064 mg_response_header_add(conn, "Expires", "0", -1);
4065
4066 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
4067 /* Obsolete, but still send it for HTTP/1.0 */
4068 mg_response_header_add(conn, "Pragma", "no-cache", -1);
4069 }
4070}

References mg_response_header_add(), mg_connection::protocol_type, and PROTOCOL_TYPE_HTTP1.

Referenced by delete_file(), handle_ssi_file_request(), mg_send_http_error_impl(), mg_send_http_ok(), mg_send_http_redirect(), put_file(), send_authorization_request(), and send_static_cache_header().

◆ send_options()

static void send_options ( struct mg_connection * conn)
static

Definition at line 12632 of file civetweb.c.

12633{
12634 if (!conn || !all_methods) {
12635 return;
12636 }
12637
12638 /* We do not set a "Cache-Control" header here, but leave the default.
12639 * Since browsers do not send an OPTIONS request, we can not test the
12640 * effect anyway. */
12641
12642 mg_response_header_start(conn, 200);
12643 mg_response_header_add(conn, "Content-Type", "text/html", -1);
12644
12645 if (conn->protocol_type == PROTOCOL_TYPE_HTTP1) {
12646 /* Use the same as before */
12647 mg_response_header_add(conn, "Allow", all_methods, -1);
12648 mg_response_header_add(conn, "DAV", "1", -1);
12649 } else {
12650 /* TODO: Check this later for HTTP/2 */
12651 mg_response_header_add(conn, "Allow", "GET, POST", -1);
12652 }
12655}

References all_methods, mg_response_header_add(), mg_response_header_send(), mg_response_header_start(), mg_connection::protocol_type, PROTOCOL_TYPE_HTTP1, and send_additional_header().

Referenced by handle_request().

◆ send_ssi_file()

static void send_ssi_file ( struct mg_connection * conn,
const char * path,
struct mg_file * filep,
int include_level )
static

Definition at line 12482 of file civetweb.c.

12486{
12487 char buf[MG_BUF_LEN];
12488 int ch, len, in_tag, in_ssi_tag;
12489
12490 if (include_level > 10) {
12491 mg_cry_internal(conn, "SSI #include level is too deep (%s)", path);
12492 return;
12493 }
12494
12495 in_tag = in_ssi_tag = len = 0;
12496
12497 /* Read file, byte by byte, and look for SSI include tags */
12498 while ((ch = mg_fgetc(filep)) != EOF) {
12499
12500 if (in_tag) {
12501 /* We are in a tag, either SSI tag or html tag */
12502
12503 if (ch == '>') {
12504 /* Tag is closing */
12505 buf[len++] = '>';
12506
12507 if (in_ssi_tag) {
12508 /* Handle SSI tag */
12509 buf[len] = 0;
12510
12511 if ((len > 12) && !memcmp(buf + 5, "include", 7)) {
12512 do_ssi_include(conn, path, buf + 12, include_level + 1);
12513#if !defined(NO_POPEN)
12514 } else if ((len > 9) && !memcmp(buf + 5, "exec", 4)) {
12515 do_ssi_exec(conn, buf + 9);
12516#endif /* !NO_POPEN */
12517 } else {
12518 mg_cry_internal(conn,
12519 "%s: unknown SSI "
12520 "command: \"%s\"",
12521 path,
12522 buf);
12523 }
12524 len = 0;
12525 in_ssi_tag = in_tag = 0;
12526
12527 } else {
12528 /* Not an SSI tag */
12529 /* Flush buffer */
12530 (void)mg_write(conn, buf, (size_t)len);
12531 len = 0;
12532 in_tag = 0;
12533 }
12534
12535 } else {
12536 /* Tag is still open */
12537 buf[len++] = (char)(ch & 0xff);
12538
12539 if ((len == 5) && !memcmp(buf, "<!--#", 5)) {
12540 /* All SSI tags start with <!--# */
12541 in_ssi_tag = 1;
12542 }
12543
12544 if ((len + 2) > (int)sizeof(buf)) {
12545 /* Tag to long for buffer */
12546 mg_cry_internal(conn, "%s: tag is too large", path);
12547 return;
12548 }
12549 }
12550
12551 } else {
12552
12553 /* We are not in a tag yet. */
12554 if (ch == '<') {
12555 /* Tag is opening */
12556 in_tag = 1;
12557
12558 if (len > 0) {
12559 /* Flush current buffer.
12560 * Buffer is filled with "len" bytes. */
12561 (void)mg_write(conn, buf, (size_t)len);
12562 }
12563 /* Store the < */
12564 len = 1;
12565 buf[0] = '<';
12566
12567 } else {
12568 /* No Tag */
12569 /* Add data to buffer */
12570 buf[len++] = (char)(ch & 0xff);
12571 /* Flush if buffer is full */
12572 if (len == (int)sizeof(buf)) {
12573 mg_write(conn, buf, (size_t)len);
12574 len = 0;
12575 }
12576 }
12577 }
12578 }
12579
12580 /* Send the rest of buffered data */
12581 if (len > 0) {
12582 mg_write(conn, buf, (size_t)len);
12583 }
12584}
static void do_ssi_exec(struct mg_connection *conn, char *tag)
Definition civetweb.c:12443
static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level)
Definition civetweb.c:12358
static int mg_fgetc(struct mg_file *filep)
Definition civetweb.c:12467

References do_ssi_exec(), do_ssi_include(), MG_BUF_LEN, mg_cry_internal, mg_fgetc(), and mg_write().

Referenced by do_ssi_include(), and handle_ssi_file_request().

◆ send_static_cache_header()

static void send_static_cache_header ( struct mg_connection * conn)
static

Definition at line 4074 of file civetweb.c.

4075{
4076#if !defined(NO_CACHING)
4077 int max_age;
4078 char val[64];
4079
4080 const char *cache_control =
4082
4083 /* If there is a full cache-control option configured,0 use it */
4084 if (cache_control != NULL) {
4085 mg_response_header_add(conn, "Cache-Control", cache_control, -1);
4086 return;
4087 }
4088
4089 /* Read the server config to check how long a file may be cached.
4090 * The configuration is in seconds. */
4091 max_age = atoi(conn->dom_ctx->config[STATIC_FILE_MAX_AGE]);
4092 if (max_age <= 0) {
4093 /* 0 means "do not cache". All values <0 are reserved
4094 * and may be used differently in the future. */
4095 /* If a file should not be cached, do not only send
4096 * max-age=0, but also pragmas and Expires headers. */
4098 return;
4099 }
4100
4101 /* Use "Cache-Control: max-age" instead of "Expires" header.
4102 * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */
4103 /* See also https://www.mnot.net/cache_docs/ */
4104 /* According to RFC 2616, Section 14.21, caching times should not exceed
4105 * one year. A year with 365 days corresponds to 31536000 seconds, a
4106 * leap
4107 * year to 31622400 seconds. For the moment, we just send whatever has
4108 * been configured, still the behavior for >1 year should be considered
4109 * as undefined. */
4111 conn, NULL, val, sizeof(val), "max-age=%lu", (unsigned long)max_age);
4112 mg_response_header_add(conn, "Cache-Control", val, -1);
4113
4114#else /* NO_CACHING */
4115
4117#endif /* !NO_CACHING */
4118}

References mg_domain_context::config, mg_connection::dom_ctx, mg_response_header_add(), mg_snprintf(), NULL, send_no_cache_header(), STATIC_FILE_CACHE_CONTROL, and STATIC_FILE_MAX_AGE.

Referenced by dav_lock_file(), dav_mkcol(), dav_proppatch(), handle_directory_request(), handle_not_modified_static_file_request(), handle_propfind(), handle_static_file_request(), and mg_send_http_redirect().

◆ set_acl_option()

static int set_acl_option ( struct mg_context * phys_ctx)
static

Definition at line 17662 of file civetweb.c.

17663{
17664 union usa sa;
17665 memset(&sa, 0, sizeof(sa));
17666#if defined(USE_IPV6)
17667 sa.sin6.sin6_family = AF_INET6;
17668#else
17669 sa.sin.sin_family = AF_INET;
17670#endif
17671 return check_acl(phys_ctx, &sa) != -1;
17672}

References check_acl(), and usa::sa.

Referenced by mg_start2().

◆ set_blocking_mode()

static int set_blocking_mode ( SOCKET sock)
static

Definition at line 5890 of file civetweb.c.

5891{
5892 int flags = fcntl(sock, F_GETFL, 0);
5893 if (flags < 0) {
5894 return -1;
5895 }
5896
5897 if (fcntl(sock, F_SETFL, flags & (~(int)(O_NONBLOCK))) < 0) {
5898 return -1;
5899 }
5900 return 0;
5901}

Referenced by close_socket_gracefully().

◆ set_close_on_exec()

static void set_close_on_exec ( int fd,
const struct mg_connection * conn,
struct mg_context * ctx )
static

Definition at line 5677 of file civetweb.c.

5680{
5681#if defined(__ZEPHYR__)
5682 (void)fd;
5683 (void)conn;
5684 (void)ctx;
5685#else
5686 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
5687 if (conn || ctx) {
5688 struct mg_connection fc;
5689 mg_cry_internal((conn ? conn : fake_connection(&fc, ctx)),
5690 "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s",
5691 __func__,
5692 strerror(ERRNO));
5693 }
5694 }
5695#endif
5696}

References ERRNO, fake_connection(), and mg_cry_internal.

Referenced by accept_new_connection(), connect_socket(), set_ports_option(), and spawn_process().

◆ set_gpass_option()

static int set_gpass_option ( struct mg_context * phys_ctx,
struct mg_domain_context * dom_ctx )
static

Definition at line 17636 of file civetweb.c.

17637{
17638 if (phys_ctx) {
17639 struct mg_file file = STRUCT_FILE_INITIALIZER;
17640 const char *path;
17641 struct mg_connection fc;
17642 if (!dom_ctx) {
17643 dom_ctx = &(phys_ctx->dd);
17644 }
17646 if ((path != NULL)
17647 && !mg_stat(fake_connection(&fc, phys_ctx), path, &file.stat)) {
17649 "Cannot open %s: %s",
17650 path,
17651 strerror(ERRNO));
17652 return 0;
17653 }
17654 return 1;
17655 }
17656 return 0;
17657}

References mg_domain_context::config, mg_context::dd, mg_connection::dom_ctx, ERRNO, fake_connection(), GLOBAL_PASSWORDS_FILE, mg_cry_ctx_internal, mg_stat(), NULL, mg_connection::phys_ctx, mg_file::stat, and STRUCT_FILE_INITIALIZER.

Referenced by mg_start2().

◆ set_non_blocking_mode()

static int set_non_blocking_mode ( SOCKET sock)
static

Definition at line 5876 of file civetweb.c.

5877{
5878 int flags = fcntl(sock, F_GETFL, 0);
5879 if (flags < 0) {
5880 return -1;
5881 }
5882
5883 if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) < 0) {
5884 return -1;
5885 }
5886 return 0;
5887}

Referenced by accept_new_connection(), and connect_socket().

◆ set_ports_option()

static int set_ports_option ( struct mg_context * phys_ctx)
static

Definition at line 15736 of file civetweb.c.

15737{
15738 const char *list;
15739 int on = 1;
15740#if defined(USE_IPV6)
15741 int off = 0;
15742#endif
15743 struct vec vec;
15744 struct socket so, *ptr;
15745
15746 struct mg_pollfd *pfd;
15747 union usa usa;
15748 socklen_t len;
15749 int ip_version;
15750
15751 int portsTotal = 0;
15752 int portsOk = 0;
15753
15754 const char *opt_txt;
15755 long opt_listen_backlog;
15756
15757 if (!phys_ctx) {
15758 return 0;
15759 }
15760
15761 memset(&so, 0, sizeof(so));
15762 memset(&usa, 0, sizeof(usa));
15763 len = sizeof(usa);
15764 list = phys_ctx->dd.config[LISTENING_PORTS];
15765
15766 while ((list = next_option(list, &vec, NULL)) != NULL) {
15767
15768 portsTotal++;
15769
15770 if (!parse_port_string(&vec, &so, &ip_version)) {
15772 phys_ctx,
15773 "%.*s: invalid port spec (entry %i). Expecting list of: %s",
15774 (int)vec.len,
15775 vec.ptr,
15776 portsTotal,
15777 "[IP_ADDRESS:]PORT[s|r]");
15778 continue;
15779 }
15780
15781#if !defined(NO_SSL)
15782 if (so.is_ssl && phys_ctx->dd.ssl_ctx == NULL) {
15783
15784 mg_cry_ctx_internal(phys_ctx,
15785 "Cannot add SSL socket (entry %i)",
15786 portsTotal);
15787 continue;
15788 }
15789#endif
15790 /* Create socket. */
15791 /* For a list of protocol numbers (e.g., TCP==6) see:
15792 * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
15793 */
15794 if ((so.sock =
15795 socket(so.lsa.sa.sa_family,
15796 SOCK_STREAM,
15797 (ip_version == 99) ? (/* LOCAL */ 0) : (/* TCP */ 6)))
15798 == INVALID_SOCKET) {
15799
15800 mg_cry_ctx_internal(phys_ctx,
15801 "cannot create socket (entry %i)",
15802 portsTotal);
15803 continue;
15804 }
15805
15806#if defined(_WIN32)
15807 /* Windows SO_REUSEADDR lets many procs binds to a
15808 * socket, SO_EXCLUSIVEADDRUSE makes the bind fail
15809 * if someone already has the socket -- DTL */
15810 /* NOTE: If SO_EXCLUSIVEADDRUSE is used,
15811 * Windows might need a few seconds before
15812 * the same port can be used again in the
15813 * same process, so a short Sleep may be
15814 * required between mg_stop and mg_start.
15815 */
15816 if (setsockopt(so.sock,
15817 SOL_SOCKET,
15818 SO_EXCLUSIVEADDRUSE,
15819 (SOCK_OPT_TYPE)&on,
15820 sizeof(on))
15821 != 0) {
15822
15823 /* Set reuse option, but don't abort on errors. */
15825 phys_ctx,
15826 "cannot set socket option SO_EXCLUSIVEADDRUSE (entry %i)",
15827 portsTotal);
15828 }
15829#else
15830 if (setsockopt(so.sock,
15831 SOL_SOCKET,
15832 SO_REUSEADDR,
15833 (SOCK_OPT_TYPE)&on,
15834 sizeof(on))
15835 != 0) {
15836
15837 /* Set reuse option, but don't abort on errors. */
15839 phys_ctx,
15840 "cannot set socket option SO_REUSEADDR (entry %i)",
15841 portsTotal);
15842 }
15843#endif
15844
15845#if defined(USE_X_DOM_SOCKET)
15846 if (ip_version == 99) {
15847 /* Unix domain socket */
15848 } else
15849#endif
15850
15851 if (ip_version > 4) {
15852 /* Could be 6 for IPv6 onlyor 10 (4+6) for IPv4+IPv6 */
15853#if defined(USE_IPV6)
15854 if (ip_version > 6) {
15855 if (so.lsa.sa.sa_family == AF_INET6
15856 && setsockopt(so.sock,
15857 IPPROTO_IPV6,
15858 IPV6_V6ONLY,
15859 (void *)&off,
15860 sizeof(off))
15861 != 0) {
15862
15863 /* Set IPv6 only option, but don't abort on errors. */
15864 mg_cry_ctx_internal(phys_ctx,
15865 "cannot set socket option "
15866 "IPV6_V6ONLY=off (entry %i)",
15867 portsTotal);
15868 }
15869 } else {
15870 if (so.lsa.sa.sa_family == AF_INET6
15871 && setsockopt(so.sock,
15872 IPPROTO_IPV6,
15873 IPV6_V6ONLY,
15874 (void *)&on,
15875 sizeof(on))
15876 != 0) {
15877
15878 /* Set IPv6 only option, but don't abort on errors. */
15879 mg_cry_ctx_internal(phys_ctx,
15880 "cannot set socket option "
15881 "IPV6_V6ONLY=on (entry %i)",
15882 portsTotal);
15883 }
15884 }
15885#else
15886 mg_cry_ctx_internal(phys_ctx, "%s", "IPv6 not available");
15887 closesocket(so.sock);
15888 so.sock = INVALID_SOCKET;
15889 continue;
15890#endif
15891 }
15892
15893 if (so.lsa.sa.sa_family == AF_INET) {
15894
15895 len = sizeof(so.lsa.sin);
15896 if (bind(so.sock, &so.lsa.sa, len) != 0) {
15897 mg_cry_ctx_internal(phys_ctx,
15898 "cannot bind to %.*s: %d (%s)",
15899 (int)vec.len,
15900 vec.ptr,
15901 (int)ERRNO,
15902 strerror(errno));
15903 closesocket(so.sock);
15904 so.sock = INVALID_SOCKET;
15905 continue;
15906 }
15907 }
15908#if defined(USE_IPV6)
15909 else if (so.lsa.sa.sa_family == AF_INET6) {
15910
15911 len = sizeof(so.lsa.sin6);
15912 if (bind(so.sock, &so.lsa.sa, len) != 0) {
15913 mg_cry_ctx_internal(phys_ctx,
15914 "cannot bind to IPv6 %.*s: %d (%s)",
15915 (int)vec.len,
15916 vec.ptr,
15917 (int)ERRNO,
15918 strerror(errno));
15919 closesocket(so.sock);
15920 so.sock = INVALID_SOCKET;
15921 continue;
15922 }
15923 }
15924#endif
15925#if defined(USE_X_DOM_SOCKET)
15926 else if (so.lsa.sa.sa_family == AF_UNIX) {
15927
15928 len = sizeof(so.lsa.sun);
15929 if (bind(so.sock, &so.lsa.sa, len) != 0) {
15930 mg_cry_ctx_internal(phys_ctx,
15931 "cannot bind to unix socket %s: %d (%s)",
15932 so.lsa.sun.sun_path,
15933 (int)ERRNO,
15934 strerror(errno));
15935 closesocket(so.sock);
15936 so.sock = INVALID_SOCKET;
15937 continue;
15938 }
15939 }
15940#endif
15941 else {
15943 phys_ctx,
15944 "cannot bind: address family not supported (entry %i)",
15945 portsTotal);
15946 closesocket(so.sock);
15947 so.sock = INVALID_SOCKET;
15948 continue;
15949 }
15950
15951 opt_txt = phys_ctx->dd.config[LISTEN_BACKLOG_SIZE];
15952 opt_listen_backlog = strtol(opt_txt, NULL, 10);
15953 if ((opt_listen_backlog > INT_MAX) || (opt_listen_backlog < 1)) {
15954 mg_cry_ctx_internal(phys_ctx,
15955 "%s value \"%s\" is invalid",
15957 opt_txt);
15958 closesocket(so.sock);
15959 so.sock = INVALID_SOCKET;
15960 continue;
15961 }
15962
15963 if (listen(so.sock, (int)opt_listen_backlog) != 0) {
15964
15965 mg_cry_ctx_internal(phys_ctx,
15966 "cannot listen to %.*s: %d (%s)",
15967 (int)vec.len,
15968 vec.ptr,
15969 (int)ERRNO,
15970 strerror(errno));
15971 closesocket(so.sock);
15972 so.sock = INVALID_SOCKET;
15973 continue;
15974 }
15975
15976 if ((getsockname(so.sock, &(usa.sa), &len) != 0)
15977 || (usa.sa.sa_family != so.lsa.sa.sa_family)) {
15978
15979 int err = (int)ERRNO;
15980 mg_cry_ctx_internal(phys_ctx,
15981 "call to getsockname failed %.*s: %d (%s)",
15982 (int)vec.len,
15983 vec.ptr,
15984 err,
15985 strerror(errno));
15986 closesocket(so.sock);
15987 so.sock = INVALID_SOCKET;
15988 continue;
15989 }
15990
15991 /* Update lsa port in case of random free ports */
15992#if defined(USE_IPV6)
15993 if (so.lsa.sa.sa_family == AF_INET6) {
15994 so.lsa.sin6.sin6_port = usa.sin6.sin6_port;
15995 } else
15996#endif
15997 {
15998 so.lsa.sin.sin_port = usa.sin.sin_port;
15999 }
16000
16001 if ((ptr = (struct socket *)
16003 (phys_ctx->num_listening_sockets + 1)
16004 * sizeof(phys_ctx->listening_sockets[0]),
16005 phys_ctx))
16006 == NULL) {
16007
16008 mg_cry_ctx_internal(phys_ctx, "%s", "Out of memory");
16009 closesocket(so.sock);
16010 so.sock = INVALID_SOCKET;
16011 continue;
16012 }
16013
16014 if ((pfd = (struct mg_pollfd *)
16016 (phys_ctx->num_listening_sockets + 1)
16017 * sizeof(phys_ctx->listening_socket_fds[0]),
16018 phys_ctx))
16019 == NULL) {
16020
16021 mg_cry_ctx_internal(phys_ctx, "%s", "Out of memory");
16022 closesocket(so.sock);
16023 so.sock = INVALID_SOCKET;
16024 mg_free(ptr);
16025 continue;
16026 }
16027
16028 set_close_on_exec(so.sock, NULL, phys_ctx);
16029 phys_ctx->listening_sockets = ptr;
16030 phys_ctx->listening_sockets[phys_ctx->num_listening_sockets] = so;
16031 phys_ctx->listening_socket_fds = pfd;
16032 phys_ctx->num_listening_sockets++;
16033 portsOk++;
16034 }
16035
16036 if (portsOk != portsTotal) {
16038 portsOk = 0;
16039 }
16040
16041 return portsOk;
16042}
static int parse_port_string(const struct vec *vec, struct socket *so, int *ip_version)
Definition civetweb.c:15501

References close_all_listening_sockets(), closesocket, mg_domain_context::config, config_options, mg_context::dd, ERRNO, INVALID_SOCKET, socket::is_ssl, vec::len, LISTEN_BACKLOG_SIZE, LISTENING_PORTS, mg_context::listening_socket_fds, mg_context::listening_sockets, socket::lsa, mg_cry_ctx_internal, mg_free(), mg_pollfd, mg_realloc_ctx, name, next_option(), NULL, mg_context::num_listening_sockets, parse_port_string(), vec::ptr, usa::sa, set_close_on_exec(), usa::sin, socket::sock, and mg_domain_context::ssl_ctx.

Referenced by mg_start2().

◆ set_tcp_nodelay()

static int set_tcp_nodelay ( const struct socket * so,
int nodelay_on )
static

Definition at line 17718 of file civetweb.c.

17719{
17720 if ((so->lsa.sa.sa_family == AF_INET)
17721 || (so->lsa.sa.sa_family == AF_INET6)) {
17722 /* Only for TCP sockets */
17723 if (setsockopt(so->sock,
17724 IPPROTO_TCP,
17725 TCP_NODELAY,
17726 (SOCK_OPT_TYPE)&nodelay_on,
17727 sizeof(nodelay_on))
17728 != 0) {
17729 /* Error */
17730 return 1;
17731 }
17732 }
17733 /* OK */
17734 return 0;
17735}

References socket::lsa, usa::sa, and socket::sock.

Referenced by accept_new_connection().

◆ set_throttle()

static int set_throttle ( const char * spec,
const union usa * rsa,
const char * uri )
static

Definition at line 14075 of file civetweb.c.

14076{
14077 int throttle = 0;
14078 struct vec vec, val;
14079 char mult;
14080 double v;
14081
14082 while ((spec = next_option(spec, &vec, &val)) != NULL) {
14083 mult = ',';
14084 if ((val.ptr == NULL)
14085 || (sscanf(val.ptr, "%lf%c", &v, &mult)
14086 < 1) // NOLINT(cert-err34-c) 'sscanf' used to convert a string
14087 // to an integer value, but function will not report
14088 // conversion errors; consider using 'strtol' instead
14089 || (v < 0)
14090 || ((lowercase(&mult) != 'k') && (lowercase(&mult) != 'm')
14091 && (mult != ','))) {
14092 continue;
14093 }
14094 v *= (lowercase(&mult) == 'k')
14095 ? 1024
14096 : ((lowercase(&mult) == 'm') ? 1048576 : 1);
14097 if (vec.len == 1 && vec.ptr[0] == '*') {
14098 throttle = (int)v;
14099 } else {
14100 int matched = parse_match_net(&vec, rsa, 0);
14101 if (matched >= 0) {
14102 /* a valid IP subnet */
14103 if (matched) {
14104 throttle = (int)v;
14105 }
14106 } else if (match_prefix(vec.ptr, vec.len, uri) > 0) {
14107 throttle = (int)v;
14108 }
14109 }
14110 }
14111
14112 return throttle;
14113}

References vec::len, lowercase(), next_option(), NULL, parse_match_net(), and vec::ptr.

Referenced by handle_request().

◆ set_uid_option()

static int set_uid_option ( struct mg_context * phys_ctx)
static

Definition at line 16267 of file civetweb.c.

16268{
16269 int success = 0;
16270
16271 if (phys_ctx) {
16272 /* We are currently running as curr_uid. */
16273 const uid_t curr_uid = getuid();
16274 /* If set, we want to run as run_as_user. */
16275 const char *run_as_user = phys_ctx->dd.config[RUN_AS_USER];
16276 const struct passwd *to_pw = NULL;
16277
16278 if ((run_as_user != NULL) && (to_pw = getpwnam(run_as_user)) == NULL) {
16279 /* run_as_user does not exist on the system. We can't proceed
16280 * further. */
16281 mg_cry_ctx_internal(phys_ctx,
16282 "%s: unknown user [%s]",
16283 __func__,
16284 run_as_user);
16285 } else if ((run_as_user == NULL) || (curr_uid == to_pw->pw_uid)) {
16286 /* There was either no request to change user, or we're already
16287 * running as run_as_user. Nothing else to do.
16288 */
16289 success = 1;
16290 } else {
16291 /* Valid change request. */
16292 if (setgid(to_pw->pw_gid) == -1) {
16293 mg_cry_ctx_internal(phys_ctx,
16294 "%s: setgid(%s): %s",
16295 __func__,
16296 run_as_user,
16297 strerror(errno));
16298 } else if (setgroups(0, NULL) == -1) {
16299 mg_cry_ctx_internal(phys_ctx,
16300 "%s: setgroups(): %s",
16301 __func__,
16302 strerror(errno));
16303 } else if (setuid(to_pw->pw_uid) == -1) {
16304 mg_cry_ctx_internal(phys_ctx,
16305 "%s: setuid(%s): %s",
16306 __func__,
16307 run_as_user,
16308 strerror(errno));
16309 } else {
16310 success = 1;
16311 }
16312 }
16313 }
16314
16315 return success;
16316}

References mg_domain_context::config, mg_context::dd, mg_cry_ctx_internal, NULL, and RUN_AS_USER.

Referenced by mg_start2().

◆ should_decode_query_string()

static int should_decode_query_string ( const struct mg_connection * conn)
static

Definition at line 4034 of file civetweb.c.

4035{
4036 if (!conn || !conn->dom_ctx) {
4037 return 0;
4038 }
4039
4040 return (mg_strcasecmp(conn->dom_ctx->config[DECODE_QUERY_STRING], "yes")
4041 == 0);
4042}

References mg_domain_context::config, DECODE_QUERY_STRING, mg_connection::dom_ctx, and mg_strcasecmp().

Referenced by handle_request().

◆ should_decode_url()

static int should_decode_url ( const struct mg_connection * conn)
static

Definition at line 4023 of file civetweb.c.

4024{
4025 if (!conn || !conn->dom_ctx) {
4026 return 0;
4027 }
4028
4029 return (mg_strcasecmp(conn->dom_ctx->config[DECODE_URL], "yes") == 0);
4030}

References mg_domain_context::config, DECODE_URL, mg_connection::dom_ctx, and mg_strcasecmp().

Referenced by handle_request().

◆ should_keep_alive()

static int should_keep_alive ( const struct mg_connection * conn)
static

Definition at line 3984 of file civetweb.c.

3985{
3986 const char *http_version;
3987 const char *header;
3988
3989 /* First satisfy needs of the server */
3990 if ((conn == NULL) || conn->must_close) {
3991 /* Close, if civetweb framework needs to close */
3992 return 0;
3993 }
3994
3995 if (mg_strcasecmp(conn->dom_ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0) {
3996 /* Close, if keep alive is not enabled */
3997 return 0;
3998 }
3999
4000 /* Check explicit wish of the client */
4001 header = mg_get_header(conn, "Connection");
4002 if (header) {
4003 /* If there is a connection header from the client, obey */
4004 if (header_has_option(header, "keep-alive")) {
4005 return 1;
4006 }
4007 return 0;
4008 }
4009
4010 /* Use default of the standard */
4011 http_version = get_http_version(conn);
4012 if (http_version && (0 == strcmp(http_version, "1.1"))) {
4013 /* HTTP 1.1 default is keep alive */
4014 return 1;
4015 }
4016
4017 /* HTTP 1.0 (and earlier) default is to close the connection */
4018 return 0;
4019}
static int header_has_option(const char *header, const char *option)
Definition civetweb.c:3956
static const char * get_http_version(const struct mg_connection *conn)
Definition civetweb.c:3876

References mg_domain_context::config, mg_connection::dom_ctx, ENABLE_KEEP_ALIVE, get_http_version(), header_has_option(), mg_get_header(), mg_strcasecmp(), mg_connection::must_close, and NULL.

Referenced by handle_cgi_request(), process_new_connection(), and suggest_connection_header().

◆ should_switch_to_protocol()

static int should_switch_to_protocol ( const struct mg_connection * conn)
static

Definition at line 13912 of file civetweb.c.

13913{
13914 const char *connection_headers[8];
13915 const char *upgrade_to;
13916 int connection_header_count, i, should_upgrade;
13917
13918 /* A websocket protocol has the following HTTP headers:
13919 *
13920 * Connection: Upgrade
13921 * Upgrade: Websocket
13922 *
13923 * It seems some clients use multiple headers:
13924 * see https://github.com/civetweb/civetweb/issues/1083
13925 */
13926 connection_header_count = get_req_headers(&conn->request_info,
13927 "Connection",
13928 connection_headers,
13929 8);
13930 should_upgrade = 0;
13931 for (i = 0; i < connection_header_count; i++) {
13932 if (0 != mg_strcasestr(connection_headers[i], "upgrade")) {
13933 should_upgrade = 1;
13934 }
13935 }
13936 if (!should_upgrade) {
13937 return PROTOCOL_TYPE_HTTP1;
13938 }
13939
13940 upgrade_to = mg_get_header(conn, "Upgrade");
13941 if (upgrade_to == NULL) {
13942 /* "Connection: Upgrade" without "Upgrade" Header --> Error */
13943 return -1;
13944 }
13945
13946 /* Upgrade to ... */
13947 if (0 != mg_strcasestr(upgrade_to, "websocket")) {
13948 /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and
13949 * "Sec-WebSocket-Version" are also required.
13950 * Don't check them here, since even an unsupported websocket protocol
13951 * request still IS a websocket request (in contrast to a standard HTTP
13952 * request). It will fail later in handle_websocket_request.
13953 */
13954 return PROTOCOL_TYPE_WEBSOCKET; /* Websocket */
13955 }
13956 if (0 != mg_strcasestr(upgrade_to, "h2")) {
13957 return PROTOCOL_TYPE_HTTP2; /* Websocket */
13958 }
13959
13960 /* Upgrade to another protocol */
13961 return -1;
13962}
static int get_req_headers(const struct mg_request_info *ri, const char *name, const char **output, int output_max_size)
Definition civetweb.c:3836

References get_req_headers(), mg_get_header(), mg_strcasestr(), NULL, PROTOCOL_TYPE_HTTP1, PROTOCOL_TYPE_HTTP2, PROTOCOL_TYPE_WEBSOCKET, and mg_connection::request_info.

Referenced by process_new_connection().

◆ skip_quoted()

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

Definition at line 3757 of file civetweb.c.

3761{
3762 char *p, *begin_word, *end_word, *end_whitespace;
3763
3764 begin_word = *buf;
3765 end_word = begin_word + strcspn(begin_word, delimiters);
3766
3767 /* Check for quotechar */
3768 if (end_word > begin_word) {
3769 p = end_word - 1;
3770 while (*p == quotechar) {
3771 /* While the delimiter is quoted, look for the next delimiter. */
3772 /* This happens, e.g., in calls from parse_auth_header,
3773 * if the user name contains a " character. */
3774
3775 /* If there is anything beyond end_word, copy it. */
3776 if (*end_word != '\0') {
3777 size_t end_off = strcspn(end_word + 1, delimiters);
3778 memmove(p, end_word, end_off + 1);
3779 p += end_off; /* p must correspond to end_word - 1 */
3780 end_word += end_off + 1;
3781 } else {
3782 *p = '\0';
3783 break;
3784 }
3785 }
3786 for (p++; p < end_word; p++) {
3787 *p = '\0';
3788 }
3789 }
3790
3791 if (*end_word == '\0') {
3792 *buf = end_word;
3793 } else {
3794
3795#if defined(GCC_DIAGNOSTIC)
3796 /* Disable spurious conversion warning for GCC */
3797#pragma GCC diagnostic push
3798#pragma GCC diagnostic ignored "-Wsign-conversion"
3799#endif /* defined(GCC_DIAGNOSTIC) */
3800
3801 end_whitespace = end_word + strspn(&end_word[1], whitespace) + 1;
3802
3803#if defined(GCC_DIAGNOSTIC)
3804#pragma GCC diagnostic pop
3805#endif /* defined(GCC_DIAGNOSTIC) */
3806
3807 for (p = end_word; p < end_whitespace; p++) {
3808 *p = '\0';
3809 }
3810
3811 *buf = end_whitespace;
3812 }
3813
3814 return begin_word;
3815}

Referenced by parse_auth_header().

◆ skip_to_end_of_word_and_terminate()

static int skip_to_end_of_word_and_terminate ( char ** ppw,
int eol )
static

Definition at line 10680 of file civetweb.c.

10681{
10682 /* Forward until a space is found - use isgraph here */
10683 /* See http://www.cplusplus.com/reference/cctype/ */
10684 while (isgraph((unsigned char)**ppw)) {
10685 (*ppw)++;
10686 }
10687
10688 /* Check end of word */
10689 if (eol) {
10690 /* must be a end of line */
10691 if ((**ppw != '\r') && (**ppw != '\n')) {
10692 return -1;
10693 }
10694 } else {
10695 /* must be a end of a word, but not a line */
10696 if (**ppw != ' ') {
10697 return -1;
10698 }
10699 }
10700
10701 /* Terminate and forward to the next word */
10702 do {
10703 **ppw = 0;
10704 (*ppw)++;
10705 } while (isspace((unsigned char)**ppw));
10706
10707 /* Check after term */
10708 if (!eol) {
10709 /* if it's not the end of line, there must be a next word */
10710 if (!isgraph((unsigned char)**ppw)) {
10711 return -1;
10712 }
10713 }
10714
10715 /* ok */
10716 return 1;
10717}

Referenced by parse_http_request(), and parse_http_response().

◆ sockaddr_to_string()

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

Definition at line 3292 of file civetweb.c.

3293{
3294 buf[0] = '\0';
3295
3296 if (!usa) {
3297 return;
3298 }
3299
3300 if (usa->sa.sa_family == AF_INET) {
3301 getnameinfo(&usa->sa,
3302 sizeof(usa->sin),
3303 buf,
3304 (unsigned)len,
3305 NULL,
3306 0,
3307 NI_NUMERICHOST);
3308 }
3309#if defined(USE_IPV6)
3310 else if (usa->sa.sa_family == AF_INET6) {
3311 getnameinfo(&usa->sa,
3312 sizeof(usa->sin6),
3313 buf,
3314 (unsigned)len,
3315 NULL,
3316 0,
3317 NI_NUMERICHOST);
3318 }
3319#endif
3320#if defined(USE_X_DOM_SOCKET)
3321 else if (usa->sa.sa_family == AF_UNIX) {
3322 /* TODO: Define a remote address for unix domain sockets.
3323 * This code will always return "localhost", identical to http+tcp:
3324 getnameinfo(&usa->sa,
3325 sizeof(usa->sun),
3326 buf,
3327 (unsigned)len,
3328 NULL,
3329 0,
3330 NI_NUMERICHOST);
3331 */
3332 mg_strlcpy(buf, UNIX_DOMAIN_SOCKET_SERVER_NAME, len);
3333 }
3334#endif
3335}

References mg_connection::buf, mg_strlcpy(), NULL, usa::sa, and usa::sin.

Referenced by accept_new_connection(), log_access(), mg_construct_local_link(), mg_cry_internal_impl(), prepare_cgi_environment(), and worker_thread_run().

◆ spawn_process()

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

Definition at line 5768 of file civetweb.c.

5777{
5778 pid_t pid;
5779 const char *interp;
5780
5781 (void)envblk;
5782
5783 if ((pid = fork()) == -1) {
5784 /* Parent */
5785 mg_cry_internal(conn, "%s: fork(): %s", __func__, strerror(ERRNO));
5786 } else if (pid != 0) {
5787 /* Make sure children close parent-side descriptors.
5788 * The caller will close the child-side immediately. */
5789 set_close_on_exec(fdin[1], conn, NULL); /* stdin write */
5790 set_close_on_exec(fdout[0], conn, NULL); /* stdout read */
5791 set_close_on_exec(fderr[0], conn, NULL); /* stderr read */
5792 } else {
5793 /* Child */
5794 if (chdir(dir) != 0) {
5796 conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
5797 } else if (dup2(fdin[0], 0) == -1) {
5798 mg_cry_internal(conn,
5799 "%s: dup2(%d, 0): %s",
5800 __func__,
5801 fdin[0],
5802 strerror(ERRNO));
5803 } else if (dup2(fdout[1], 1) == -1) {
5804 mg_cry_internal(conn,
5805 "%s: dup2(%d, 1): %s",
5806 __func__,
5807 fdout[1],
5808 strerror(ERRNO));
5809 } else if (dup2(fderr[1], 2) == -1) {
5810 mg_cry_internal(conn,
5811 "%s: dup2(%d, 2): %s",
5812 __func__,
5813 fderr[1],
5814 strerror(ERRNO));
5815 } else {
5816 struct sigaction sa;
5817
5818 /* Keep stderr and stdout in two different pipes.
5819 * Stdout will be sent back to the client,
5820 * stderr should go into a server error log. */
5821 (void)close(fdin[0]);
5822 (void)close(fdout[1]);
5823 (void)close(fderr[1]);
5824
5825 /* Close write end fdin and read end fdout and fderr */
5826 (void)close(fdin[1]);
5827 (void)close(fdout[0]);
5828 (void)close(fderr[0]);
5829
5830 /* After exec, all signal handlers are restored to their default
5831 * values, with one exception of SIGCHLD. According to
5832 * POSIX.1-2001 and Linux's implementation, SIGCHLD's handler
5833 * will leave unchanged after exec if it was set to be ignored.
5834 * Restore it to default action. */
5835 memset(&sa, 0, sizeof(sa));
5836 sa.sa_handler = SIG_DFL;
5837 sigaction(SIGCHLD, &sa, NULL);
5838
5839 interp = conn->dom_ctx->config[CGI_INTERPRETER + cgi_config_idx];
5840 if (interp == NULL) {
5841 /* no interpreter configured, call the program directly */
5842 (void)execle(prog, prog, NULL, envp);
5843 mg_cry_internal(conn,
5844 "%s: execle(%s): %s",
5845 __func__,
5846 prog,
5847 strerror(ERRNO));
5848 } else {
5849 /* call the configured interpreter */
5850 const char *interp_args =
5851 conn->dom_ctx
5852 ->config[CGI_INTERPRETER_ARGS + cgi_config_idx];
5853
5854 if ((interp_args != NULL) && (interp_args[0] != 0)) {
5855 (void)execle(interp, interp, interp_args, prog, NULL, envp);
5856 } else {
5857 (void)execle(interp, interp, prog, NULL, envp);
5858 }
5859 mg_cry_internal(conn,
5860 "%s: execle(%s %s): %s",
5861 __func__,
5862 interp,
5863 prog,
5864 strerror(ERRNO));
5865 }
5866 }
5867 exit(EXIT_FAILURE);
5868 }
5869
5870 return pid;
5871}

References CGI_INTERPRETER, CGI_INTERPRETER_ARGS, mg_domain_context::config, mg_connection::dom_ctx, ERRNO, mg_cry_internal, NULL, and set_close_on_exec().

Referenced by handle_cgi_request().

◆ ssl_error()

static const char * ssl_error ( void )
static

Definition at line 16585 of file civetweb.c.

16586{
16587 unsigned long err;
16588 err = ERR_get_error();
16589 return ((err == 0) ? "" : ERR_error_string(err, NULL));
16590}

References NULL.

Referenced by init_ssl_ctx_impl(), initialize_openssl(), mg_connect_client_impl(), refresh_trust(), ssl_use_pem_file(), and sslize().

◆ ssl_get_client_cert_info()

static int ssl_get_client_cert_info ( const struct mg_connection * conn,
struct mg_client_cert * client_cert )
static

Definition at line 16620 of file civetweb.c.

16622{
16623 X509 *cert = SSL_get_peer_certificate(conn->ssl);
16624 if (cert) {
16625 char str_buf[1024];
16626 unsigned char buf[256];
16627 char *str_serial = NULL;
16628 unsigned int ulen;
16629 int ilen;
16630 unsigned char *tmp_buf;
16631 unsigned char *tmp_p;
16632
16633 /* Handle to algorithm used for fingerprint */
16634 const EVP_MD *digest = EVP_get_digestbyname("sha1");
16635
16636 /* Get Subject and issuer */
16637 X509_NAME *subj = X509_get_subject_name(cert);
16638 X509_NAME *iss = X509_get_issuer_name(cert);
16639
16640 /* Get serial number */
16641 ASN1_INTEGER *serial = X509_get_serialNumber(cert);
16642
16643 /* Translate serial number to a hex string */
16644 BIGNUM *serial_bn = ASN1_INTEGER_to_BN(serial, NULL);
16645 if (serial_bn) {
16646 str_serial = BN_bn2hex(serial_bn);
16647 BN_free(serial_bn);
16648 }
16649 client_cert->serial =
16650 str_serial ? mg_strdup_ctx(str_serial, conn->phys_ctx) : NULL;
16651
16652 /* Translate subject and issuer to a string */
16653 (void)X509_NAME_oneline(subj, str_buf, (int)sizeof(str_buf));
16654 client_cert->subject = mg_strdup_ctx(str_buf, conn->phys_ctx);
16655 (void)X509_NAME_oneline(iss, str_buf, (int)sizeof(str_buf));
16656 client_cert->issuer = mg_strdup_ctx(str_buf, conn->phys_ctx);
16657
16658 /* Calculate SHA1 fingerprint and store as a hex string */
16659 ulen = 0;
16660
16661 /* ASN1_digest is deprecated. Do the calculation manually,
16662 * using EVP_Digest. */
16663 ilen = i2d_X509(cert, NULL);
16664 tmp_buf = (ilen > 0)
16665 ? (unsigned char *)mg_malloc_ctx((unsigned)ilen + 1,
16666 conn->phys_ctx)
16667 : NULL;
16668 if (tmp_buf) {
16669 tmp_p = tmp_buf;
16670 (void)i2d_X509(cert, &tmp_p);
16671 if (!EVP_Digest(
16672 tmp_buf, (unsigned)ilen, buf, &ulen, digest, NULL)) {
16673 ulen = 0;
16674 }
16675 mg_free(tmp_buf);
16676 }
16677
16678 if (!hexdump2string(buf, (int)ulen, str_buf, (int)sizeof(str_buf))) {
16679 *str_buf = 0;
16680 }
16681 client_cert->finger = mg_strdup_ctx(str_buf, conn->phys_ctx);
16682
16683 client_cert->peer_cert = (void *)cert;
16684
16685 /* Strings returned from bn_bn2hex must be freed using OPENSSL_free,
16686 * see https://linux.die.net/man/3/bn_bn2hex */
16687 OPENSSL_free(str_serial);
16688 return 1;
16689 }
16690 return 0;
16691}
static int hexdump2string(void *mem, int memlen, char *buf, int buflen)
Definition civetweb.c:16594
const char * issuer
Definition civetweb.h:209
const char * finger
Definition civetweb.h:211
void * peer_cert
Definition civetweb.h:207
const char * subject
Definition civetweb.h:208
const char * serial
Definition civetweb.h:210

References mg_client_cert::finger, hexdump2string(), mg_client_cert::issuer, mg_free(), mg_malloc_ctx, mg_strdup_ctx(), NULL, mg_client_cert::peer_cert, mg_connection::phys_ctx, mg_client_cert::serial, mg_connection::ssl, and mg_client_cert::subject.

Referenced by worker_thread_run().

◆ ssl_get_protocol()

static long ssl_get_protocol ( int version_id)
static

Definition at line 17017 of file civetweb.c.

17018{
17019 unsigned long ret = (unsigned long)SSL_OP_ALL;
17020 if (version_id > 0)
17021 ret |= SSL_OP_NO_SSLv2;
17022 if (version_id > 1)
17023 ret |= SSL_OP_NO_SSLv3;
17024 if (version_id > 2)
17025 ret |= SSL_OP_NO_TLSv1;
17026 if (version_id > 3)
17027 ret |= SSL_OP_NO_TLSv1_1;
17028 if (version_id > 4)
17029 ret |= SSL_OP_NO_TLSv1_2;
17030#if defined(SSL_OP_NO_TLSv1_3)
17031 if (version_id > 5)
17032 ret |= SSL_OP_NO_TLSv1_3;
17033#endif
17034 return (long)ret;
17035}

Referenced by init_ssl_ctx_impl().

◆ ssl_info_callback()

static void ssl_info_callback ( const SSL * ssl,
int what,
int ret )
static

Definition at line 17050 of file civetweb.c.

17051{
17052 (void)ret;
17053
17054 if (what & SSL_CB_HANDSHAKE_START) {
17055 SSL_get_app_data(ssl);
17056 }
17057 if (what & SSL_CB_HANDSHAKE_DONE) {
17058 /* TODO: check for openSSL 1.1 */
17059 //#define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001
17060 // ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
17061 }
17062}

Referenced by init_ssl_ctx_impl().

◆ ssl_locking_callback()

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

Definition at line 16697 of file civetweb.c.

16698{
16699 (void)line;
16700 (void)file;
16701
16702 if (mode & 1) {
16703 /* 1 is CRYPTO_LOCK */
16704 (void)pthread_mutex_lock(&ssl_mutexes[mutex_num]);
16705 } else {
16706 (void)pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
16707 }
16708}

References ssl_mutexes.

Referenced by initialize_openssl().

◆ ssl_servername_callback()

static int ssl_servername_callback ( SSL * ssl,
int * ad,
void * arg )
static

Definition at line 17066 of file civetweb.c.

17067{
17068#if defined(GCC_DIAGNOSTIC)
17069#pragma GCC diagnostic push
17070#pragma GCC diagnostic ignored "-Wcast-align"
17071#endif /* defined(GCC_DIAGNOSTIC) */
17072
17073 /* We used an aligned pointer in SSL_set_app_data */
17074 struct mg_connection *conn = (struct mg_connection *)SSL_get_app_data(ssl);
17075
17076#if defined(GCC_DIAGNOSTIC)
17077#pragma GCC diagnostic pop
17078#endif /* defined(GCC_DIAGNOSTIC) */
17079
17080 const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
17081
17082 (void)ad;
17083 (void)arg;
17084
17085 if ((conn == NULL) || (conn->phys_ctx == NULL)) {
17086 DEBUG_ASSERT(0);
17087 return SSL_TLSEXT_ERR_NOACK;
17088 }
17089 conn->dom_ctx = &(conn->phys_ctx->dd);
17090
17091 /* Old clients (Win XP) will not support SNI. Then, there
17092 * is no server name available in the request - we can
17093 * only work with the default certificate.
17094 * Multiple HTTPS hosts on one IP+port are only possible
17095 * with a certificate containing all alternative names.
17096 */
17097 if ((servername == NULL) || (*servername == 0)) {
17098 DEBUG_TRACE("%s", "SSL connection not supporting SNI");
17100 SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
17102 return SSL_TLSEXT_ERR_NOACK;
17103 }
17104
17105 DEBUG_TRACE("TLS connection to host %s", servername);
17106
17107 while (conn->dom_ctx) {
17108 if (!mg_strcasecmp(servername,
17110 /* Found matching domain */
17111 DEBUG_TRACE("TLS domain %s found",
17113 break;
17114 }
17116 conn->dom_ctx = conn->dom_ctx->next;
17118 }
17119
17120 if (conn->dom_ctx == NULL) {
17121 /* Default domain */
17122 DEBUG_TRACE("TLS default domain %s used",
17124 conn->dom_ctx = &(conn->phys_ctx->dd);
17125 }
17127 SSL_set_SSL_CTX(ssl, conn->dom_ctx->ssl_ctx);
17129 return SSL_TLSEXT_ERR_OK;
17130}

References arg, AUTHENTICATION_DOMAIN, mg_domain_context::config, mg_context::dd, DEBUG_ASSERT, DEBUG_TRACE, mg_connection::dom_ctx, mg_lock_context(), mg_strcasecmp(), mg_unlock_context(), mg_domain_context::next, NULL, mg_connection::phys_ctx, mg_connection::ssl, and mg_domain_context::ssl_ctx.

Referenced by init_ssl_ctx_impl().

◆ ssl_use_pem_file()

static int ssl_use_pem_file ( struct mg_context * phys_ctx,
struct mg_domain_context * dom_ctx,
const char * pem,
const char * chain )
static

Definition at line 16940 of file civetweb.c.

16944{
16945 if (SSL_CTX_use_certificate_file(dom_ctx->ssl_ctx, pem, 1) == 0) {
16946 mg_cry_ctx_internal(phys_ctx,
16947 "%s: cannot open certificate file %s: %s",
16948 __func__,
16949 pem,
16950 ssl_error());
16951 return 0;
16952 }
16953
16954 /* could use SSL_CTX_set_default_passwd_cb_userdata */
16955 if (SSL_CTX_use_PrivateKey_file(dom_ctx->ssl_ctx, pem, 1) == 0) {
16956 mg_cry_ctx_internal(phys_ctx,
16957 "%s: cannot open private key file %s: %s",
16958 __func__,
16959 pem,
16960 ssl_error());
16961 return 0;
16962 }
16963
16964 if (SSL_CTX_check_private_key(dom_ctx->ssl_ctx) == 0) {
16965 mg_cry_ctx_internal(phys_ctx,
16966 "%s: certificate and private key do not match: %s",
16967 __func__,
16968 pem);
16969 return 0;
16970 }
16971
16972 /* In contrast to OpenSSL, wolfSSL does not support certificate
16973 * chain files that contain private keys and certificates in
16974 * SSL_CTX_use_certificate_chain_file.
16975 * The CivetWeb-Server used pem-Files that contained both information.
16976 * In order to make wolfSSL work, it is split in two files.
16977 * One file that contains key and certificate used by the server and
16978 * an optional chain file for the ssl stack.
16979 */
16980 if (chain) {
16981 if (SSL_CTX_use_certificate_chain_file(dom_ctx->ssl_ctx, chain) == 0) {
16982 mg_cry_ctx_internal(phys_ctx,
16983 "%s: cannot use certificate chain file %s: %s",
16984 __func__,
16985 chain,
16986 ssl_error());
16987 return 0;
16988 }
16989 }
16990 return 1;
16991}

References mg_cry_ctx_internal, mg_domain_context::ssl_ctx, and ssl_error().

Referenced by init_ssl_ctx_impl(), mg_connect_client_impl(), and refresh_trust().

◆ sslize()

static int sslize ( struct mg_connection * conn,
int(*)(SSL *) func,
const struct mg_client_options * client_options )
static

Definition at line 16455 of file civetweb.c.

16458{
16459 int ret, err;
16460 int short_trust;
16461 unsigned timeout = 1024;
16462 unsigned i;
16463
16464 if (!conn) {
16465 return 0;
16466 }
16467
16468 short_trust =
16469 (conn->dom_ctx->config[SSL_SHORT_TRUST] != NULL)
16470 && (mg_strcasecmp(conn->dom_ctx->config[SSL_SHORT_TRUST], "yes") == 0);
16471
16472 if (short_trust) {
16473 int trust_ret = refresh_trust(conn);
16474 if (!trust_ret) {
16475 return trust_ret;
16476 }
16477 }
16478
16480 conn->ssl = SSL_new(conn->dom_ctx->ssl_ctx);
16482 if (conn->ssl == NULL) {
16483 mg_cry_internal(conn, "sslize error: %s", ssl_error());
16484 OPENSSL_REMOVE_THREAD_STATE();
16485 return 0;
16486 }
16487 SSL_set_app_data(conn->ssl, (char *)conn);
16488
16489 ret = SSL_set_fd(conn->ssl, conn->client.sock);
16490 if (ret != 1) {
16491 mg_cry_internal(conn, "sslize error: %s", ssl_error());
16492 SSL_free(conn->ssl);
16493 conn->ssl = NULL;
16494 OPENSSL_REMOVE_THREAD_STATE();
16495 return 0;
16496 }
16497
16498 if (client_options) {
16499 if (client_options->host_name) {
16500 SSL_set_tlsext_host_name(conn->ssl, client_options->host_name);
16501 }
16502 }
16503
16504 /* Reuse the request timeout for the SSL_Accept/SSL_connect timeout */
16505 if (conn->dom_ctx->config[REQUEST_TIMEOUT]) {
16506 /* NOTE: The loop below acts as a back-off, so we can end
16507 * up sleeping for more (or less) than the REQUEST_TIMEOUT. */
16508 int to = atoi(conn->dom_ctx->config[REQUEST_TIMEOUT]);
16509 if (to >= 0) {
16510 timeout = (unsigned)to;
16511 }
16512 }
16513
16514 /* SSL functions may fail and require to be called again:
16515 * see https://www.openssl.org/docs/manmaster/ssl/SSL_get_error.html
16516 * Here "func" could be SSL_connect or SSL_accept. */
16517 for (i = 0; i <= timeout; i += 50) {
16518 ERR_clear_error();
16519 /* conn->dom_ctx may be changed here (see ssl_servername_callback) */
16520 ret = func(conn->ssl);
16521 if (ret != 1) {
16522 err = SSL_get_error(conn->ssl, ret);
16523 if ((err == SSL_ERROR_WANT_CONNECT)
16524 || (err == SSL_ERROR_WANT_ACCEPT)
16525 || (err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)
16526 || (err == SSL_ERROR_WANT_X509_LOOKUP)) {
16527 if (!STOP_FLAG_IS_ZERO(&conn->phys_ctx->stop_flag)) {
16528 /* Don't wait if the server is going to be stopped. */
16529 break;
16530 }
16531 if (err == SSL_ERROR_WANT_X509_LOOKUP) {
16532 /* Simply retry the function call. */
16533 mg_sleep(50);
16534 } else {
16535 /* Need to retry the function call "later".
16536 * See https://linux.die.net/man/3/ssl_get_error
16537 * This is typical for non-blocking sockets. */
16538 struct mg_pollfd pfd;
16539 int pollres;
16540 pfd.fd = conn->client.sock;
16541 pfd.events = ((err == SSL_ERROR_WANT_CONNECT)
16542 || (err == SSL_ERROR_WANT_WRITE))
16543 ? POLLOUT
16544 : POLLIN;
16545 pollres =
16546 mg_poll(&pfd, 1, 50, &(conn->phys_ctx->stop_flag));
16547 if (pollres < 0) {
16548 /* Break if error occurred (-1)
16549 * or server shutdown (-2) */
16550 break;
16551 }
16552 }
16553
16554 } else if (err == SSL_ERROR_SYSCALL) {
16555 /* This is an IO error. Look at errno. */
16556 mg_cry_internal(conn, "SSL syscall error %i", ERRNO);
16557 break;
16558
16559 } else {
16560 /* This is an SSL specific error, e.g. SSL_ERROR_SSL */
16561 mg_cry_internal(conn, "sslize error: %s", ssl_error());
16562 break;
16563 }
16564
16565 } else {
16566 /* success */
16567 break;
16568 }
16569 }
16570 ERR_clear_error();
16571
16572 if (ret != 1) {
16573 SSL_free(conn->ssl);
16574 conn->ssl = NULL;
16575 OPENSSL_REMOVE_THREAD_STATE();
16576 return 0;
16577 }
16578
16579 return 1;
16580}
static int refresh_trust(struct mg_connection *conn)
Definition civetweb.c:16377
const char * host_name
Definition civetweb.h:1488

References mg_domain_context::config, mg_connection::dom_ctx, ERRNO, mg_client_options::host_name, mg_cry_internal, mg_lock_context(), mg_poll(), mg_pollfd, mg_sleep, mg_strcasecmp(), mg_unlock_context(), NULL, mg_connection::phys_ctx, refresh_trust(), REQUEST_TIMEOUT, mg_connection::ssl, mg_domain_context::ssl_ctx, ssl_error(), SSL_SHORT_TRUST, and STOP_FLAG_IS_ZERO.

Referenced by mg_connect_client_impl(), and worker_thread_run().

◆ substitute_index_file()

static int substitute_index_file ( struct mg_connection * conn,
char * path,
size_t path_len,
struct mg_file_stat * filestat )
static

Definition at line 7574 of file civetweb.c.

7578{
7579 const char *list = conn->dom_ctx->config[INDEX_FILES];
7580 struct vec filename_vec;
7581 size_t n = strlen(path);
7582 int found = 0;
7583
7584 /* The 'path' given to us points to the directory. Remove all trailing
7585 * directory separator characters from the end of the path, and
7586 * then append single directory separator character. */
7587 while ((n > 0) && (path[n - 1] == '/')) {
7588 n--;
7589 }
7590 path[n] = '/';
7591
7592 /* Traverse index files list. For each entry, append it to the given
7593 * path and see if the file exists. If it exists, break the loop */
7594 while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
7595 /* Ignore too long entries that may overflow path buffer */
7596 if ((filename_vec.len + 1) > (path_len - (n + 1))) {
7597 continue;
7598 }
7599
7600 /* Prepare full path to the index file */
7601 mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
7602
7603 /* Does it exist? */
7604 if (mg_stat(conn, path, filestat)) {
7605 /* Yes it does, break the loop */
7606 found = 1;
7607 break;
7608 }
7609 }
7610
7611 /* If no index file exists, restore directory path */
7612 if (!found) {
7613 path[n] = '\0';
7614 }
7615
7616 return found;
7617}

References mg_domain_context::config, mg_connection::dom_ctx, INDEX_FILES, vec::len, mg_stat(), mg_strlcpy(), next_option(), NULL, and vec::ptr.

Referenced by interpret_uri().

◆ suggest_connection_header()

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

Definition at line 4046 of file civetweb.c.

4047{
4048 return should_keep_alive(conn) ? "keep-alive" : "close";
4049}

References should_keep_alive().

Referenced by handle_request().

◆ switch_domain_context()

static int switch_domain_context ( struct mg_connection * conn)
static

Definition at line 14174 of file civetweb.c.

14175{
14176 struct vec host;
14177
14179
14180 if (host.ptr) {
14181 if (conn->ssl) {
14182 /* This is a HTTPS connection, maybe we have a hostname
14183 * from SNI (set in ssl_servername_callback). */
14184 const char *sslhost = conn->dom_ctx->config[AUTHENTICATION_DOMAIN];
14185 if (sslhost && (conn->dom_ctx != &(conn->phys_ctx->dd))) {
14186 /* We are not using the default domain */
14187 if ((strlen(sslhost) != host.len)
14188 || mg_strncasecmp(host.ptr, sslhost, host.len)) {
14189 /* Mismatch between SNI domain and HTTP domain */
14190 DEBUG_TRACE("Host mismatch: SNI: %s, HTTPS: %.*s",
14191 sslhost,
14192 (int)host.len,
14193 host.ptr);
14194 return 0;
14195 }
14196 }
14197
14198 } else {
14199 struct mg_domain_context *dom = &(conn->phys_ctx->dd);
14200 while (dom) {
14201 const char *domName = dom->config[AUTHENTICATION_DOMAIN];
14202 size_t domNameLen = strlen(domName);
14203 if ((domNameLen == host.len)
14204 && !mg_strncasecmp(host.ptr, domName, host.len)) {
14205
14206 /* Found matching domain */
14207 DEBUG_TRACE("HTTP domain %s found",
14209
14210 /* TODO: Check if this is a HTTP or HTTPS domain */
14211 conn->dom_ctx = dom;
14212 break;
14213 }
14215 dom = dom->next;
14217 }
14218 }
14219
14220 DEBUG_TRACE("HTTP%s Host: %.*s",
14221 conn->ssl ? "S" : "",
14222 (int)host.len,
14223 host.ptr);
14224
14225 } else {
14226 DEBUG_TRACE("HTTP%s Host is not set", conn->ssl ? "S" : "");
14227 return 1;
14228 }
14229
14230 return 1;
14231}
static void get_host_from_request_info(struct vec *host, const struct mg_request_info *ri)
Definition civetweb.c:14136

References AUTHENTICATION_DOMAIN, mg_domain_context::config, mg_context::dd, DEBUG_TRACE, mg_connection::dom_ctx, get_host_from_request_info(), vec::len, mg_lock_context(), mg_strncasecmp(), mg_unlock_context(), mg_domain_context::next, mg_connection::phys_ctx, vec::ptr, mg_connection::request_info, and mg_connection::ssl.

Referenced by get_request().

◆ tls_dtor()

static void tls_dtor ( void * key)
static

Definition at line 16321 of file civetweb.c.

16322{
16323 struct mg_workerTLS *tls = (struct mg_workerTLS *)key;
16324 /* key == pthread_getspecific(sTlsKey); */
16325
16326 if (tls) {
16327 if (tls->is_master == 2) {
16328 tls->is_master = -3; /* Mark memory as dead */
16329 mg_free(tls);
16330 }
16331 }
16332 pthread_setspecific(sTlsKey, NULL);
16333}

References mg_workerTLS::is_master, mg_free(), NULL, and sTlsKey.

Referenced by mg_init_library().

◆ uninitialize_openssl()

static void uninitialize_openssl ( void )
static

Definition at line 17594 of file civetweb.c.

17595{
17596#if defined(OPENSSL_API_1_1) || defined(OPENSSL_API_3_0)
17597
17598 if (mg_atomic_dec(&cryptolib_users) == 0) {
17599
17600 /* Shutdown according to
17601 * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
17602 * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
17603 */
17604 CONF_modules_unload(1);
17605#else
17606 int i;
17607
17608 if (mg_atomic_dec(&cryptolib_users) == 0) {
17609
17610 /* Shutdown according to
17611 * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup
17612 * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl
17613 */
17614 CRYPTO_set_locking_callback(NULL);
17615 CRYPTO_set_id_callback(NULL);
17616 ENGINE_cleanup();
17617 CONF_modules_unload(1);
17618 ERR_free_strings();
17619 EVP_cleanup();
17620 CRYPTO_cleanup_all_ex_data();
17621 OPENSSL_REMOVE_THREAD_STATE();
17622
17623 for (i = 0; i < CRYPTO_num_locks(); i++) {
17624 pthread_mutex_destroy(&ssl_mutexes[i]);
17625 }
17627 ssl_mutexes = NULL;
17628#endif /* OPENSSL_API_1_1 || OPENSSL_API_3_0 */
17629 }
17630}

References cryptolib_users, mg_atomic_dec(), mg_free(), NULL, and ssl_mutexes.

Referenced by mg_exit_library().

◆ url_decode_in_place()

static void url_decode_in_place ( char * buf)
static

Definition at line 7080 of file civetweb.c.

7081{
7082 int len = (int)strlen(buf);
7083 (void)mg_url_decode(buf, len, buf, len + 1, 1);
7084}

References mg_url_decode().

Referenced by handle_request(), and mg_split_form_urlencoded().

◆ worker_thread()

static void * worker_thread ( void * thread_func_param)
static

Definition at line 19937 of file civetweb.c.

19938{
19939#if !defined(__ZEPHYR__)
19940 struct sigaction sa;
19941
19942 /* Ignore SIGPIPE */
19943 memset(&sa, 0, sizeof(sa));
19944 sa.sa_handler = SIG_IGN;
19945 sigaction(SIGPIPE, &sa, NULL);
19946#endif
19947
19948 worker_thread_run((struct mg_connection *)thread_func_param);
19949 return NULL;
19950}
static void worker_thread_run(struct mg_connection *conn)
Definition civetweb.c:19704

References NULL, and worker_thread_run().

Referenced by mg_start2().

◆ worker_thread_run()

static void worker_thread_run ( struct mg_connection * conn)
static

Definition at line 19704 of file civetweb.c.

19705{
19706 struct mg_context *ctx = conn->phys_ctx;
19707 int thread_index;
19708 struct mg_workerTLS tls;
19709
19710 mg_set_thread_name("worker");
19711
19712 tls.is_master = 0;
19713 tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
19714#if defined(_WIN32)
19715 tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL);
19716#endif
19717
19718 /* Initialize thread local storage before calling any callback */
19719 pthread_setspecific(sTlsKey, &tls);
19720
19721 /* Check if there is a user callback */
19722 if (ctx->callbacks.init_thread) {
19723 /* call init_thread for a worker thread (type 1), and store the
19724 * return value */
19725 tls.user_ptr = ctx->callbacks.init_thread(ctx, 1);
19726 } else {
19727 /* No callback: set user pointer to NULL */
19728 tls.user_ptr = NULL;
19729 }
19730
19731 /* Connection structure has been pre-allocated */
19732 thread_index = (int)(conn - ctx->worker_connections);
19733 if ((thread_index < 0)
19734 || ((unsigned)thread_index >= (unsigned)ctx->cfg_worker_threads)) {
19736 "Internal error: Invalid worker index %i",
19737 thread_index);
19738 return;
19739 }
19740
19741 /* Request buffers are not pre-allocated. They are private to the
19742 * request and do not contain any state information that might be
19743 * of interest to anyone observing a server status. */
19744 conn->buf = (char *)mg_malloc_ctx(ctx->max_request_size, conn->phys_ctx);
19745 if (conn->buf == NULL) {
19747 ctx,
19748 "Out of memory: Cannot allocate buffer for worker %i",
19749 thread_index);
19750 return;
19751 }
19752 conn->buf_size = (int)ctx->max_request_size;
19753
19754 conn->dom_ctx = &(ctx->dd); /* Use default domain and default host */
19755
19756 conn->tls_user_ptr = tls.user_ptr; /* store ptr for quick access */
19757
19758 conn->request_info.user_data = ctx->user_data;
19759 /* Allocate a mutex for this connection to allow communication both
19760 * within the request handler and from elsewhere in the application
19761 */
19762 if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
19763 mg_free(conn->buf);
19764 mg_cry_ctx_internal(ctx, "%s", "Cannot create mutex");
19765 return;
19766 }
19767
19768#if defined(USE_SERVER_STATS)
19769 conn->conn_state = 1; /* not consumed */
19770#endif
19771
19772 /* Call consume_socket() even when ctx->stop_flag > 0, to let it
19773 * signal sq_empty condvar to wake up the master waiting in
19774 * produce_socket() */
19775 while (consume_socket(ctx, &conn->client, thread_index)) {
19776
19777 /* New connections must start with new protocol negotiation */
19778 tls.alpn_proto = NULL;
19779
19780#if defined(USE_SERVER_STATS)
19781 conn->conn_close_time = 0;
19782#endif
19783 conn->conn_birth_time = time(NULL);
19784
19785 /* Fill in IP, port info early so even if SSL setup below fails,
19786 * error handler would have the corresponding info.
19787 * Thanks to Johannes Winkelmann for the patch.
19788 */
19790 ntohs(USA_IN_PORT_UNSAFE(&conn->client.rsa));
19791
19793 ntohs(USA_IN_PORT_UNSAFE(&conn->client.lsa));
19794
19796 sizeof(conn->request_info.remote_addr),
19797 &conn->client.rsa);
19798
19799 DEBUG_TRACE("Incoming %sconnection from %s",
19800 (conn->client.is_ssl ? "SSL " : ""),
19801 conn->request_info.remote_addr);
19802
19803 conn->request_info.is_ssl = conn->client.is_ssl;
19804
19805 if (conn->client.is_ssl) {
19806
19807#if defined(USE_MBEDTLS)
19808 /* HTTPS connection */
19809 if (mbed_ssl_accept(&(conn->ssl),
19810 conn->dom_ctx->ssl_ctx,
19811 (int *)&(conn->client.sock),
19812 conn->phys_ctx)
19813 == 0) {
19814 /* conn->dom_ctx is set in get_request */
19815 /* process HTTPS connection */
19816 init_connection(conn);
19820 } else {
19821 /* make sure the connection is cleaned up on SSL failure */
19822 close_connection(conn);
19823 }
19824
19825#elif !defined(NO_SSL)
19826 /* HTTPS connection */
19827 if (sslize(conn, SSL_accept, NULL)) {
19828 /* conn->dom_ctx is set in get_request */
19829
19830 /* Get SSL client certificate information (if set) */
19831 struct mg_client_cert client_cert;
19832 if (ssl_get_client_cert_info(conn, &client_cert)) {
19833 conn->request_info.client_cert = &client_cert;
19834 }
19835
19836 /* process HTTPS connection */
19837#if defined(USE_HTTP2)
19838 if ((tls.alpn_proto != NULL)
19839 && (!memcmp(tls.alpn_proto, "\x02h2", 3))) {
19840 /* process HTTPS/2 connection */
19841 init_connection(conn);
19844 conn->content_len =
19845 -1; /* content length is not predefined */
19846 conn->is_chunked = 0; /* HTTP2 is never chunked */
19847 process_new_http2_connection(conn);
19848 } else
19849#endif
19850 {
19851 /* process HTTPS/1.x or WEBSOCKET-SECURE connection */
19852 init_connection(conn);
19854 /* Start with HTTP, WS will be an "upgrade" request later */
19857 }
19858
19859 /* Free client certificate info */
19860 if (conn->request_info.client_cert) {
19861 mg_free((void *)(conn->request_info.client_cert->subject));
19862 mg_free((void *)(conn->request_info.client_cert->issuer));
19863 mg_free((void *)(conn->request_info.client_cert->serial));
19864 mg_free((void *)(conn->request_info.client_cert->finger));
19865 /* Free certificate memory */
19866 X509_free(
19867 (X509 *)conn->request_info.client_cert->peer_cert);
19869 conn->request_info.client_cert->subject = 0;
19870 conn->request_info.client_cert->issuer = 0;
19871 conn->request_info.client_cert->serial = 0;
19872 conn->request_info.client_cert->finger = 0;
19873 conn->request_info.client_cert = 0;
19874 }
19875 } else {
19876 /* make sure the connection is cleaned up on SSL failure */
19877 close_connection(conn);
19878 }
19879#endif
19880
19881 } else {
19882 /* process HTTP connection */
19883 init_connection(conn);
19885 /* Start with HTTP, WS will be an "upgrade" request later */
19888 }
19889
19890 DEBUG_TRACE("%s", "Connection closed");
19891
19892#if defined(USE_SERVER_STATS)
19893 conn->conn_close_time = time(NULL);
19894#endif
19895 }
19896
19897 /* Call exit thread user callback */
19898 if (ctx->callbacks.exit_thread) {
19899 ctx->callbacks.exit_thread(ctx, 1, tls.user_ptr);
19900 }
19901
19902 /* delete thread local storage objects */
19903 pthread_setspecific(sTlsKey, NULL);
19904#if defined(_WIN32)
19905 CloseHandle(tls.pthread_cond_helper_mutex);
19906#endif
19907 pthread_mutex_destroy(&conn->mutex);
19908
19909 /* Free the request buffer. */
19910 conn->buf_size = 0;
19911 mg_free(conn->buf);
19912 conn->buf = NULL;
19913
19914 /* Free cleaned URI (if any) */
19915 if (conn->request_info.local_uri != conn->request_info.local_uri_raw) {
19916 mg_free((void *)conn->request_info.local_uri);
19917 conn->request_info.local_uri = NULL;
19918 }
19919
19920#if defined(USE_SERVER_STATS)
19921 conn->conn_state = 9; /* done */
19922#endif
19923
19924 DEBUG_TRACE("%s", "exiting");
19925}
static void process_new_connection(struct mg_connection *conn)
Definition civetweb.c:19368
static int consume_socket(struct mg_context *ctx, struct socket *sp, int thread_index)
Definition civetweb.c:19624
static int ssl_get_client_cert_info(const struct mg_connection *conn, struct mg_client_cert *client_cert)
Definition civetweb.c:16620
static void init_connection(struct mg_connection *conn)
Definition civetweb.c:19329
void * user_data
Definition civetweb.h:175
struct mg_client_cert * client_cert
Definition civetweb.h:182

References mg_workerTLS::alpn_proto, mg_connection::buf, mg_connection::buf_size, mg_context::callbacks, mg_context::cfg_worker_threads, mg_connection::client, mg_request_info::client_cert, close_connection(), mg_connection::conn_birth_time, mg_connection::connection_type, CONNECTION_TYPE_REQUEST, consume_socket(), mg_connection::content_len, mg_context::dd, DEBUG_TRACE, mg_connection::dom_ctx, mg_callbacks::exit_thread, FALSE, mg_client_cert::finger, init_connection(), mg_callbacks::init_thread, mg_connection::is_chunked, mg_workerTLS::is_master, socket::is_ssl, mg_request_info::is_ssl, mg_client_cert::issuer, mg_request_info::local_uri, mg_request_info::local_uri_raw, socket::lsa, mg_context::max_request_size, mg_atomic_inc(), mg_cry_ctx_internal, mg_free(), mg_malloc_ctx, mg_set_thread_name(), mg_connection::mutex, NULL, mg_client_cert::peer_cert, mg_connection::phys_ctx, process_new_connection(), mg_connection::protocol_type, PROTOCOL_TYPE_HTTP1, PROTOCOL_TYPE_HTTP2, pthread_mutex_attr, mg_request_info::remote_addr, mg_request_info::remote_port, mg_connection::request_info, socket::rsa, mg_client_cert::serial, mg_request_info::server_port, socket::sock, sockaddr_to_string(), mg_connection::ssl, mg_domain_context::ssl_ctx, ssl_get_client_cert_info(), sslize(), sTlsKey, mg_client_cert::subject, mg_workerTLS::thread_idx, thread_idx_max, mg_connection::tls_user_ptr, USA_IN_PORT_UNSAFE, mg_context::user_data, mg_request_info::user_data, mg_workerTLS::user_ptr, and mg_context::worker_connections.

Referenced by worker_thread().

Variable Documentation

◆ [struct]

const struct { ... } abs_uri_protocols[]
Initial value:
= {{"http://", 7, 80},
{"https://", 8, 443},
{"ws://", 5, 80},
{"wss://", 6, 443},
{NULL, 0, 0}}

Referenced by get_rel_url_at_current_server(), and get_uri_type().

◆ all_methods

char* all_methods = NULL
static

Definition at line 10876 of file civetweb.c.

Referenced by mg_exit_library(), mg_init_library(), and send_options().

◆ [struct]

const struct { ... } builtin_mime_types[]

◆ config_options

const struct mg_option config_options[]
static

Definition at line 2062 of file civetweb.c.

2062 {
2063
2064 /* Once for each server */
2065 {"listening_ports", MG_CONFIG_TYPE_STRING_LIST, "8080"},
2066 {"num_threads", MG_CONFIG_TYPE_NUMBER, "50"},
2067 {"run_as_user", MG_CONFIG_TYPE_STRING, NULL},
2068 {"tcp_nodelay", MG_CONFIG_TYPE_NUMBER, "0"},
2069 {"max_request_size", MG_CONFIG_TYPE_NUMBER, "16384"},
2070 {"linger_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2071 {"connection_queue", MG_CONFIG_TYPE_NUMBER, "20"},
2072 {"listen_backlog", MG_CONFIG_TYPE_NUMBER, "200"},
2073#if defined(__linux__)
2074 {"allow_sendfile_call", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2075#endif
2076#if defined(_WIN32)
2077 {"case_sensitive", MG_CONFIG_TYPE_BOOLEAN, "no"},
2078#endif
2079 {"throttle", MG_CONFIG_TYPE_STRING_LIST, NULL},
2080 {"enable_keep_alive", MG_CONFIG_TYPE_BOOLEAN, "no"},
2081 {"request_timeout_ms", MG_CONFIG_TYPE_NUMBER, "30000"},
2082 {"keep_alive_timeout_ms", MG_CONFIG_TYPE_NUMBER, "500"},
2083#if defined(USE_WEBSOCKET)
2084 {"websocket_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2085 {"enable_websocket_ping_pong", MG_CONFIG_TYPE_BOOLEAN, "no"},
2086#endif
2087 {"decode_url", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2088 {"decode_query_string", MG_CONFIG_TYPE_BOOLEAN, "no"},
2089#if defined(USE_LUA)
2090 {"lua_background_script", MG_CONFIG_TYPE_FILE, NULL},
2091 {"lua_background_script_params", MG_CONFIG_TYPE_STRING_LIST, NULL},
2092#endif
2093#if defined(USE_HTTP2)
2094 {"enable_http2", MG_CONFIG_TYPE_BOOLEAN, "no"},
2095#endif
2096
2097 /* Once for each domain */
2098 {"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2099
2100 {"access_log_file", MG_CONFIG_TYPE_FILE, NULL},
2101 {"error_log_file", MG_CONFIG_TYPE_FILE, NULL},
2102
2103 {"cgi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
2104 {"cgi_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2105 {"cgi_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2106 {"cgi_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2107#if defined(USE_TIMERS)
2108 {"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2109#endif
2110 {"cgi_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2111
2112 {"cgi2_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2113 {"cgi2_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2114 {"cgi2_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2115 {"cgi2_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2116#if defined(USE_TIMERS)
2117 {"cgi2_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2118#endif
2119 {"cgi2_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2120
2121#if defined(USE_4_CGI)
2122 {"cgi3_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2123 {"cgi3_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2124 {"cgi3_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2125 {"cgi3_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2126#if defined(USE_TIMERS)
2127 {"cgi3_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2128#endif
2129 {"cgi3_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2130
2131 {"cgi4_pattern", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2132 {"cgi4_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
2133 {"cgi4_interpreter", MG_CONFIG_TYPE_FILE, NULL},
2134 {"cgi4_interpreter_args", MG_CONFIG_TYPE_STRING, NULL},
2135#if defined(USE_TIMERS)
2136 {"cgi4_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
2137#endif
2138 {"cgi4_buffering", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2139
2140#endif
2141
2142 {"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL},
2143 {"protect_uri", MG_CONFIG_TYPE_STRING_LIST, NULL},
2144 {"authentication_domain", MG_CONFIG_TYPE_STRING, "mydomain.com"},
2145 {"enable_auth_domain_check", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2146 {"ssi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"},
2147 {"enable_directory_listing", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2148 {"enable_webdav", MG_CONFIG_TYPE_BOOLEAN, "no"},
2149 {"global_auth_file", MG_CONFIG_TYPE_FILE, NULL},
2150 {"index_files",
2152#if defined(USE_LUA)
2153 "index.xhtml,index.html,index.htm,"
2154 "index.lp,index.lsp,index.lua,index.cgi,"
2155 "index.shtml,index.php"},
2156#else
2157 "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
2158#endif
2159 {"access_control_list", MG_CONFIG_TYPE_STRING_LIST, NULL},
2160 {"extra_mime_types", MG_CONFIG_TYPE_STRING_LIST, NULL},
2161 {"ssl_certificate", MG_CONFIG_TYPE_FILE, NULL},
2162 {"ssl_certificate_chain", MG_CONFIG_TYPE_FILE, NULL},
2163 {"url_rewrite_patterns", MG_CONFIG_TYPE_STRING_LIST, NULL},
2164 {"hide_files_patterns", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
2165
2166 {"ssl_verify_peer", MG_CONFIG_TYPE_YES_NO_OPTIONAL, "no"},
2167 {"ssl_cache_timeout", MG_CONFIG_TYPE_NUMBER, "-1"},
2168
2169 {"ssl_ca_path", MG_CONFIG_TYPE_DIRECTORY, NULL},
2170 {"ssl_ca_file", MG_CONFIG_TYPE_FILE, NULL},
2171 {"ssl_verify_depth", MG_CONFIG_TYPE_NUMBER, "9"},
2172 {"ssl_default_verify_paths", MG_CONFIG_TYPE_BOOLEAN, "yes"},
2173 {"ssl_cipher_list", MG_CONFIG_TYPE_STRING, NULL},
2174
2175 /* HTTP2 requires ALPN, and anyway TLS1.2 should be considered
2176 * as a minimum in 2020 */
2177 {"ssl_protocol_version", MG_CONFIG_TYPE_NUMBER, "4"},
2178
2179 {"ssl_short_trust", MG_CONFIG_TYPE_BOOLEAN, "no"},
2180
2181#if defined(USE_LUA)
2182 {"lua_preload_file", MG_CONFIG_TYPE_FILE, NULL},
2183 {"lua_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
2184 {"lua_server_page_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
2185#if defined(MG_EXPERIMENTAL_INTERFACES)
2186 {"lua_debug", MG_CONFIG_TYPE_STRING, NULL},
2187#endif
2188#endif
2189#if defined(USE_DUKTAPE)
2190 /* The support for duktape is still in alpha version state.
2191 * The name of this config option might change. */
2192 {"duktape_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
2193#endif
2194
2195#if defined(USE_WEBSOCKET)
2196 {"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
2197#endif
2198#if defined(USE_LUA) && defined(USE_WEBSOCKET)
2199 {"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
2200#endif
2201 {"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"},
2202 {"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"},
2203 {"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"},
2204 {"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL},
2205#if !defined(NO_CACHING)
2206 {"static_file_max_age", MG_CONFIG_TYPE_NUMBER, "3600"},
2207 {"static_file_cache_control", MG_CONFIG_TYPE_STRING, NULL},
2208#endif
2209#if !defined(NO_SSL)
2210 {"strict_transport_security_max_age", MG_CONFIG_TYPE_NUMBER, NULL},
2211#endif
2212 {"additional_header", MG_CONFIG_TYPE_STRING_MULTILINE, NULL},
2213 {"allow_index_script_resource", MG_CONFIG_TYPE_BOOLEAN, "no"},
2214
@ MG_CONFIG_TYPE_UNKNOWN
Definition civetweb.h:694
@ MG_CONFIG_TYPE_FILE
Definition civetweb.h:697
@ MG_CONFIG_TYPE_STRING
Definition civetweb.h:696
@ MG_CONFIG_TYPE_DIRECTORY
Definition civetweb.h:698
@ MG_CONFIG_TYPE_EXT_PATTERN
Definition civetweb.h:700
@ MG_CONFIG_TYPE_STRING_MULTILINE
Definition civetweb.h:702
@ MG_CONFIG_TYPE_STRING_LIST
Definition civetweb.h:701
@ MG_CONFIG_TYPE_YES_NO_OPTIONAL
Definition civetweb.h:703
@ MG_CONFIG_TYPE_NUMBER
Definition civetweb.h:695
@ MG_CONFIG_TYPE_BOOLEAN
Definition civetweb.h:699

Referenced by get_option_index(), handle_cgi_request(), init_ssl_ctx(), legacy_init(), mg_connect_client_impl(), mg_get_valid_options(), mg_start2(), mg_start_domain2(), pull_all(), push_all(), read_message(), and set_ports_option().

◆ cryptolib_dll_handle

void* cryptolib_dll_handle
static

Definition at line 16806 of file civetweb.c.

Referenced by initialize_openssl().

◆ cryptolib_users

volatile ptrdiff_t cryptolib_users
static
Initial value:
=
0

Definition at line 16815 of file civetweb.c.

Referenced by initialize_openssl(), and uninitialize_openssl().

◆ default_port

unsigned default_port

Definition at line 18380 of file civetweb.c.

Referenced by mg_construct_local_link().

◆ ext_len

size_t ext_len

Definition at line 8223 of file civetweb.c.

Referenced by mg_get_builtin_mime_type().

◆ extension

const char* extension

Definition at line 8222 of file civetweb.c.

Referenced by mg_get_builtin_mime_type().

◆ global_lock_mutex

pthread_mutex_t global_lock_mutex
static

Definition at line 1089 of file civetweb.c.

Referenced by mg_exit_library(), mg_global_lock(), mg_global_unlock(), and mg_init_library().

◆ http_methods

const struct mg_http_method_info http_methods[]
static

Definition at line 10813 of file civetweb.c.

10813 {
10814 /* HTTP (RFC 2616) */
10815 {"GET", 0, 1, 1, 1, 1},
10816 {"POST", 1, 1, 0, 0, 0},
10817 {"PUT", 1, 0, 0, 1, 0},
10818 {"DELETE", 0, 0, 0, 1, 0},
10819 {"HEAD", 0, 0, 1, 1, 1},
10820 {"OPTIONS", 0, 0, 1, 1, 0},
10821 {"CONNECT", 1, 1, 0, 0, 0},
10822 /* TRACE method (RFC 2616) is not supported for security reasons */
10823
10824 /* PATCH method (RFC 5789) */
10825 {"PATCH", 1, 0, 0, 0, 0},
10826 /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */
10827
10828 /* WEBDAV (RFC 2518) */
10829 {"PROPFIND", 0, 1, 1, 1, 0},
10830 /* http://www.webdav.org/specs/rfc4918.html, 9.1:
10831 * Some PROPFIND results MAY be cached, with care,
10832 * as there is no cache validation mechanism for
10833 * most properties. This method is both safe and
10834 * idempotent (see Section 9.1 of [RFC2616]). */
10835 {"MKCOL", 0, 0, 0, 1, 0},
10836 /* http://www.webdav.org/specs/rfc4918.html, 9.1:
10837 * When MKCOL is invoked without a request body,
10838 * the newly created collection SHOULD have no
10839 * members. A MKCOL request message may contain
10840 * a message body. The precise behavior of a MKCOL
10841 * request when the body is present is undefined,
10842 * ... ==> We do not support MKCOL with body data.
10843 * This method is idempotent, but not safe (see
10844 * Section 9.1 of [RFC2616]). Responses to this
10845 * method MUST NOT be cached. */
10846
10847 /* Methods for write access to files on WEBDAV (RFC 2518) */
10848 {"LOCK", 1, 1, 0, 0, 0},
10849 {"UNLOCK", 1, 0, 0, 0, 0},
10850 {"PROPPATCH", 1, 1, 0, 0, 0},
10851 {"COPY", 1, 0, 0, 0, 0},
10852 {"MOVE", 1, 1, 0, 0, 0},
10853
10854 /* Unsupported WEBDAV Methods: */
10855 /* + 11 methods from RFC 3253 */
10856 /* ORDERPATCH (RFC 3648) */
10857 /* ACL (RFC 3744) */
10858 /* SEARCH (RFC 5323) */
10859 /* + MicroSoft extensions
10860 * https://msdn.microsoft.com/en-us/library/aa142917.aspx */
10861
10862 /* REPORT method (RFC 3253) */
10863 {"REPORT", 1, 1, 1, 1, 1},
10864 /* REPORT method only allowed for CGI/Lua/LSP and callbacks. */
10865 /* It was defined for WEBDAV in RFC 3253, Sec. 3.6
10866 * (https://tools.ietf.org/html/rfc3253#section-3.6), but seems
10867 * to be useful for REST in case a "GET request with body" is
10868 * required. */
10869
10870 {NULL, 0, 0, 0, 0, 0}
10871 /* end of list */
10872};

Referenced by get_http_method_info(), and mg_init_library().

◆ mg_init_library_called

int mg_init_library_called = 0
static

Definition at line 1552 of file civetweb.c.

Referenced by mg_exit_library(), mg_init_library(), and mg_start2().

◆ mime_type

const char* mime_type

◆ month_names

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

Definition at line 1815 of file civetweb.c.

1815 {"Jan",
1816 "Feb",
1817 "Mar",
1818 "Apr",
1819 "May",
1820 "Jun",
1821 "Jul",
1822 "Aug",
1823 "Sep",
1824 "Oct",
1825 "Nov",
1826 "Dec"};

Referenced by get_month_index().

◆ proto

◆ proto_len

size_t proto_len

Definition at line 18379 of file civetweb.c.

Referenced by get_rel_url_at_current_server(), and get_uri_type().

◆ pthread_mutex_attr

pthread_mutexattr_t pthread_mutex_attr
static

◆ ssl_mutexes

pthread_mutex_t* ssl_mutexes
static

Definition at line 16451 of file civetweb.c.

Referenced by initialize_openssl(), ssl_locking_callback(), and uninitialize_openssl().

◆ ssllib_dll_handle

void* ssllib_dll_handle
static

Definition at line 16805 of file civetweb.c.

Referenced by initialize_openssl().

◆ static_assert_replacement

char static_assert_replacement[1]

Definition at line 125 of file civetweb.c.

◆ sTlsKey

◆ thread_idx_max

volatile ptrdiff_t thread_idx_max = 0
static

Definition at line 1581 of file civetweb.c.

Referenced by mg_current_thread_id(), mg_start2(), and worker_thread_run().