Commit 5425b94f authored by Sergey Lyubka's avatar Sergey Lyubka

introduced struct mg_config. Fixed Range response.

parent 9a5f96e7
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <stddef.h>
#include "mongoose.h" #include "mongoose.h"
...@@ -70,16 +71,6 @@ signal_handler(int sig_num) ...@@ -70,16 +71,6 @@ signal_handler(int sig_num)
} }
} }
/*
* Show usage string and exit.
*/
static void
show_usage_and_exit(void)
{
mg_show_usage_string(stderr);
exit(EXIT_FAILURE);
}
/* /*
* Edit the passwords file. * Edit the passwords file.
*/ */
...@@ -88,18 +79,89 @@ mg_edit_passwords(const char *fname, const char *domain, ...@@ -88,18 +79,89 @@ mg_edit_passwords(const char *fname, const char *domain,
const char *user, const char *pass) const char *user, const char *pass)
{ {
struct mg_context *ctx; struct mg_context *ctx;
struct mg_config config;
int retval; int retval;
ctx = mg_start(); memset(&config, 0, sizeof(config));
(void) mg_set_option(ctx, "auth_realm", domain); config.auth_domain = (char *) domain;
config.num_threads = "0";
config.listening_ports = "";
ctx = mg_start(&config);
retval = mg_modify_passwords_file(ctx, fname, user, pass); retval = mg_modify_passwords_file(ctx, fname, user, pass);
mg_stop(ctx); mg_stop(ctx);
return (retval); return (retval);
} }
#define OFFSET(x) offsetof(struct mg_config, x)
static struct option_descriptor {
const char *name;
const char *description;
size_t offset;
} known_options[] = {
{"root", "\tWeb root directory", OFFSET(document_root)},
{"index_files", "Index files", OFFSET(index_files)},
{"ssl_cert", "SSL certificate file", OFFSET(ssl_certificate)},
{"ports", "Listening ports", OFFSET(listening_ports)},
{"dir_list", "Directory listing", OFFSET(enable_directory_listing)},
{"protect", "URI to htpasswd mapping", OFFSET(protect)},
{"cgi_ext", "CGI extensions", OFFSET(cgi_extensions)},
{"cgi_interp", "CGI interpreter to use", OFFSET(cgi_interpreter)},
{"cgi_env", "Custom CGI enviroment variables", OFFSET(cgi_environment)},
{"ssi_ext", "SSI extensions", OFFSET(ssi_extensions)},
{"auth_realm", "Authentication domain name", OFFSET(auth_domain)},
{"auth_gpass", "Global passwords file", OFFSET(global_passwords_file)},
{"auth_PUT", "PUT,DELETE auth file", OFFSET(put_delete_passwords_file)},
{"uid", "\tRun as user", OFFSET(uid)},
{"access_log", "Access log file", OFFSET(access_log_file)},
{"error_log", "Error log file", OFFSET(error_log_file)},
{"acl", "\tAllow/deny IP addresses/subnets", OFFSET(acl)},
{"num_threads", "Threads to spawn", OFFSET(num_threads)},
{"mime_types", "Extra mime types to use", OFFSET(mime_types)},
{NULL, NULL, 0}
};
static void
show_usage_and_exit(const struct mg_config *config)
{
const struct option_descriptor *o;
const char *value;
(void) fprintf(stderr,
"Mongoose version %s (c) Sergey Lyubka\n"
"usage: mongoose [options] [config_file]\n", mg_version());
fprintf(stderr, " -A <htpasswd_file> <realm> <user> <passwd>\n");
for (o = known_options; o->name != NULL; o++) {
(void) fprintf(stderr, " -%s\t%s", o->name, o->description);
value = * (char **) ((char *) config + o->offset);
if (value != NULL)
fprintf(stderr, " (default: \"%s\")", value);
fputc('\n', stderr);
}
exit(EXIT_FAILURE);
}
static void static void
process_command_line_arguments(struct mg_context *ctx, char *argv[]) set_option(struct mg_config *config, const char *name, char *value)
{
const struct option_descriptor *o;
for (o = known_options; o->name != NULL; o++)
if (strcmp(name, o->name) == 0) {
* (char **) ((char *) config + o->offset) = value;
break;
}
if (o->name == NULL)
show_usage_and_exit(config);
}
static void
process_command_line_arguments(struct mg_config *config, char *argv[])
{ {
const char *config_file = CONFIG_FILE; const char *config_file = CONFIG_FILE;
char line[512], opt[512], *vals[100], char line[512], opt[512], *vals[100],
...@@ -110,11 +172,11 @@ process_command_line_arguments(struct mg_context *ctx, char *argv[]) ...@@ -110,11 +172,11 @@ process_command_line_arguments(struct mg_context *ctx, char *argv[])
/* First find out, which config file to open */ /* First find out, which config file to open */
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2) for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
if (argv[i + 1] == NULL) if (argv[i + 1] == NULL)
show_usage_and_exit(); show_usage_and_exit(config);
if (argv[i] != NULL && argv[i + 1] != NULL) { if (argv[i] != NULL && argv[i + 1] != NULL) {
/* More than one non-option arguments are given */ /* More than one non-option arguments are given */
show_usage_and_exit(); show_usage_and_exit(config);
} else if (argv[i] != NULL) { } else if (argv[i] != NULL) {
/* Just one non-option argument is given, this is config file */ /* Just one non-option argument is given, this is config file */
config_file = argv[i]; config_file = argv[i];
...@@ -140,7 +202,8 @@ process_command_line_arguments(struct mg_context *ctx, char *argv[]) ...@@ -140,7 +202,8 @@ process_command_line_arguments(struct mg_context *ctx, char *argv[])
(void) memset(vals, 0, sizeof(vals)); (void) memset(vals, 0, sizeof(vals));
if (fp != NULL) { if (fp != NULL) {
(void) printf("Loading config file %s\n", config_file); (void) printf("Loading config file %s, "
"ignoring command line arguments\n", config_file);
/* Loop over the lines in config file */ /* Loop over the lines in config file */
while (fgets(line, sizeof(line), fp) != NULL) { while (fgets(line, sizeof(line), fp) != NULL) {
...@@ -156,56 +219,67 @@ process_command_line_arguments(struct mg_context *ctx, char *argv[]) ...@@ -156,56 +219,67 @@ process_command_line_arguments(struct mg_context *ctx, char *argv[])
config_file, (int) line_no); config_file, (int) line_no);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (mg_set_option(ctx, opt, val) != 1) /* TODO(lsm): free this at some point */
exit(EXIT_FAILURE); p = malloc(strlen(val) + 1);
(void) strcpy(p, val);
set_option(config, opt, p);
} }
(void) fclose(fp); (void) fclose(fp);
} } else {
/* Now pass through the command line options */
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2) for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
if (mg_set_option(ctx, &argv[i][1], argv[i + 1]) != 1) set_option(config, &argv[i][1], argv[i + 1]);
exit(EXIT_FAILURE); }
} }
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
struct mg_config config;
struct mg_context *ctx; struct mg_context *ctx;
char ports[1024], web_root[1024];
/* Initialize configuration with default values */
(void) memset(&config, 0, sizeof(config));
config.document_root = ".";
config.enable_directory_listing = "yes";
config.auth_domain = "mydomain.com";
config.num_threads = "20";
config.index_files = "index.html,index.htm,index.cgi";
config.cgi_extensions = ".cgi,.pl,.php";
config.ssi_extensions = ".shtml,.shtm";
config.listening_ports = "8080";
/* Edit passwords file if -A option is specified */
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') { if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') {
if (argc != 6) if (argc != 6)
show_usage_and_exit(); show_usage_and_exit(&config);
exit(mg_edit_passwords(argv[2], argv[3], argv[4], argv[5]) == exit(mg_edit_passwords(argv[2], argv[3], argv[4], argv[5]) ==
MG_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE); MG_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE);
} }
/* Show usage if -h or --help options are specified */
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
show_usage_and_exit(); show_usage_and_exit(&config);
/* Update config based on command line arguments */
process_command_line_arguments(&config, argv);
/* Setup signal handler: quit on Ctrl-C */
#ifndef _WIN32 #ifndef _WIN32
(void) signal(SIGCHLD, signal_handler); (void) signal(SIGCHLD, signal_handler);
#endif /* _WIN32 */ #endif /* _WIN32 */
(void) signal(SIGTERM, signal_handler); (void) signal(SIGTERM, signal_handler);
(void) signal(SIGINT, signal_handler); (void) signal(SIGINT, signal_handler);
if ((ctx = mg_start()) == NULL) { /* Start Mongoose */
if ((ctx = mg_start(&config)) == NULL) {
(void) printf("%s\n", "Cannot initialize Mongoose context"); (void) printf("%s\n", "Cannot initialize Mongoose context");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
process_command_line_arguments(ctx, argv); (void) printf("Mongoose %s started on port(s) %s "
(void) mg_get_option(ctx, "ports", ports, sizeof(ports)); "with web root [%s]\n",
if (ports[0] == '\0' && mg_version(), config.listening_ports, config.document_root);
mg_set_option(ctx, "ports", "8080") != MG_SUCCESS)
exit(EXIT_FAILURE);
(void) mg_get_option(ctx, "ports", ports, sizeof(ports));
(void) mg_get_option(ctx, "root", web_root, sizeof(web_root));
(void) printf("Mongoose %s started on port(s) \"%s\", "
"serving directory \"%s\"\n", mg_version(), ports, web_root);
fflush(stdout); fflush(stdout);
while (exit_flag == 0) while (exit_flag == 0)
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#if defined(_WIN32) #if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
#else #else
#define _XOPEN_SOURCE 600 /* Needed for pthread_rwlock on linux */ #define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
#define _LARGEFILE_SOURCE /* Enable 64-bit file offsets */ #define _LARGEFILE_SOURCE /* Enable 64-bit file offsets */
#endif #endif
...@@ -129,7 +129,6 @@ typedef long off_t; ...@@ -129,7 +129,6 @@ typedef long off_t;
#endif /* !fileno MINGW #defines fileno */ #endif /* !fileno MINGW #defines fileno */
typedef HANDLE pthread_mutex_t; typedef HANDLE pthread_mutex_t;
typedef HANDLE pthread_rwlock_t;
typedef HANDLE pthread_cond_t; typedef HANDLE pthread_cond_t;
typedef DWORD pthread_t; typedef DWORD pthread_t;
#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */ #define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */
...@@ -143,13 +142,6 @@ static int pthread_mutex_lock(pthread_mutex_t *); ...@@ -143,13 +142,6 @@ static int pthread_mutex_lock(pthread_mutex_t *);
static int pthread_mutex_unlock(pthread_mutex_t *); static int pthread_mutex_unlock(pthread_mutex_t *);
static FILE *mg_fopen(const char *path, const char *mode); static FILE *mg_fopen(const char *path, const char *mode);
/* TODO(lsm): Implement these */
#define pthread_rwlock_init pthread_mutex_init
#define pthread_rwlock_destroy pthread_mutex_destroy
#define pthread_rwlock_rdlock pthread_mutex_lock
#define pthread_rwlock_wrlock pthread_mutex_lock
#define pthread_rwlock_unlock pthread_mutex_unlock
#if defined(HAVE_STDINT) #if defined(HAVE_STDINT)
#include <stdint.h> #include <stdint.h>
#else #else
...@@ -367,27 +359,6 @@ struct mgstat { ...@@ -367,27 +359,6 @@ struct mgstat {
time_t mtime; /* Modification time */ time_t mtime; /* Modification time */
}; };
struct mg_option {
const char *name;
const char *description;
const char *default_value;
int index;
enum mg_error_t (*setter)(struct mg_context *, const char *);
};
/*
* Numeric indexes for the option values in context, ctx->options
*/
enum mg_option_index {
OPT_ROOT, OPT_INDEX_FILES, OPT_PORTS, OPT_DIR_LIST, OPT_CGI_EXTENSIONS,
OPT_CGI_INTERPRETER, OPT_CGI_ENV, OPT_SSI_EXTENSIONS, OPT_AUTH_DOMAIN,
OPT_AUTH_GPASSWD, OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG,
OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_UID, OPT_PROTECT,
OPT_SERVICE, OPT_HIDE, OPT_ADMIN_URI, OPT_MAX_THREADS, OPT_IDLE_TIME,
OPT_MIME_TYPES,
NUM_OPTIONS
};
/* /*
* Structure used to describe listening socket, or socket which was * Structure used to describe listening socket, or socket which was
* accept()-ed by the master thread and queued for future handling * accept()-ed by the master thread and queued for future handling
...@@ -407,16 +378,14 @@ struct socket { ...@@ -407,16 +378,14 @@ struct socket {
struct mg_context { struct mg_context {
int stop_flag; /* Should we stop event loop */ int stop_flag; /* Should we stop event loop */
SSL_CTX *ssl_ctx; /* SSL context */ SSL_CTX *ssl_ctx; /* SSL context */
const struct mg_config *config; /* Mongoose configuration */
struct socket *listening_sockets; struct socket *listening_sockets;
char *options[NUM_OPTIONS];
mg_callback_t callbacks[NUM_EVENTS];
int num_threads; /* Number of threads */ int num_threads; /* Number of threads */
int num_idle; /* Number of idle threads */ int num_idle; /* Number of idle threads */
pthread_mutex_t mutex; /* Protects (max|num)_threads */ pthread_mutex_t mutex; /* Protects (max|num)_threads */
pthread_rwlock_t rwlock; /* Protects options, callbacks */
pthread_cond_t thr_cond; /* Condvar for thread sync */ pthread_cond_t thr_cond; /* Condvar for thread sync */
struct socket queue[20]; /* Accepted sockets */ struct socket queue[20]; /* Accepted sockets */
...@@ -461,15 +430,13 @@ cry(struct mg_connection *conn, const char *fmt, ...) ...@@ -461,15 +430,13 @@ cry(struct mg_connection *conn, const char *fmt, ...)
* I suppose this is fine, since function cannot disappear in the * I suppose this is fine, since function cannot disappear in the
* same way string option can. * same way string option can.
*/ */
log_callback = conn->ctx->callbacks[MG_EVENT_LOG]; log_callback = conn->ctx->config->event_log_handler;
conn->request_info.log_message = buf; conn->request_info.log_message = buf;
if (log_callback != NULL) if (log_callback != NULL)
processed = log_callback(conn, &conn->request_info); processed = log_callback(conn, &conn->request_info);
if (processed == MG_ERROR) { if (processed == MG_ERROR) {
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock); fp = conn->ctx->config->error_log_file == NULL ? stderr :
fp = conn->ctx->options[OPT_ERROR_LOG] == NULL ? stderr : mg_fopen(conn->ctx->config->error_log_file, "a+");
mg_fopen(conn->ctx->options[OPT_ERROR_LOG], "a+");
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
if (fp != NULL) { if (fp != NULL) {
flockfile(fp); flockfile(fp);
...@@ -530,7 +497,7 @@ fc(struct mg_context *ctx) ...@@ -530,7 +497,7 @@ fc(struct mg_context *ctx)
const char * const char *
mg_version(void) mg_version(void)
{ {
return ("\"" MONGOOSE_VERSION ", $Rev: 517 $\""); return (MONGOOSE_VERSION);
} }
static void static void
...@@ -634,22 +601,6 @@ mg_snprintf(struct mg_connection *conn, ...@@ -634,22 +601,6 @@ mg_snprintf(struct mg_connection *conn,
return (n); return (n);
} }
/*
* Convert string representing a boolean value to a boolean value
*/
static bool_t
is_true(const char *str)
{
static const char *trues[] = {"1", "yes", "true", "ja", NULL};
int i;
for (i = 0; trues[i] != NULL; i++)
if (str != NULL && mg_strcasecmp(str, trues[i]) == 0)
return (TRUE);
return (FALSE);
}
/* /*
* Skip the characters until one of the delimiters characters found. * Skip the characters until one of the delimiters characters found.
* 0-terminate resulting word. Skip the rest of the delimiters if any. * 0-terminate resulting word. Skip the rest of the delimiters if any.
...@@ -775,7 +726,7 @@ send_error(struct mg_connection *conn, int status, const char *reason, ...@@ -775,7 +726,7 @@ send_error(struct mg_connection *conn, int status, const char *reason,
DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: %d %s", __func__, status, reason)); DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: %d %s", __func__, status, reason));
conn->request_info.status_code = status; conn->request_info.status_code = status;
error_handler = conn->ctx->callbacks[MG_EVENT_HTTP_ERROR]; error_handler = conn->ctx->config->http_error_handler;
handled = error_handler ? handled = error_handler ?
error_handler(conn, &conn->request_info) : FALSE; error_handler(conn, &conn->request_info) : FALSE;
...@@ -1225,7 +1176,6 @@ spawn_process(struct mg_connection *conn, const char *prog, char *envblk, ...@@ -1225,7 +1176,6 @@ spawn_process(struct mg_connection *conn, const char *prog, char *envblk,
&si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
/* If CGI file is a script, try to read the interpreter line */ /* If CGI file is a script, try to read the interpreter line */
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock);
interp = conn->ctx->options[OPT_CGI_INTERPRETER]; interp = conn->ctx->options[OPT_CGI_INTERPRETER];
if (interp == NULL) { if (interp == NULL) {
line[2] = '\0'; line[2] = '\0';
...@@ -1249,7 +1199,6 @@ spawn_process(struct mg_connection *conn, const char *prog, char *envblk, ...@@ -1249,7 +1199,6 @@ spawn_process(struct mg_connection *conn, const char *prog, char *envblk,
(void) mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s%s", (void) mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s%s",
interp, interp[0] == '\0' ? "" : " ", prog); interp, interp[0] == '\0' ? "" : " ", prog);
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
(void) mg_snprintf(conn, line, sizeof(line), "%s", dir); (void) mg_snprintf(conn, line, sizeof(line), "%s", dir);
fix_directory_separators(line); fix_directory_separators(line);
...@@ -1364,7 +1313,7 @@ spawn_process(struct mg_connection *conn, const char *prog, char *envblk, ...@@ -1364,7 +1313,7 @@ spawn_process(struct mg_connection *conn, const char *prog, char *envblk,
(void) close(fd_stdout); (void) close(fd_stdout);
/* Execute CGI program. No need to lock: new process */ /* Execute CGI program. No need to lock: new process */
interp = conn->ctx->options[OPT_CGI_INTERPRETER]; interp = conn->ctx->config->cgi_interpreter;
if (interp == NULL) { if (interp == NULL) {
(void) execle(prog, prog, NULL, envp); (void) execle(prog, prog, NULL, envp);
cry(conn, "%s: execle(%s): %s", cry(conn, "%s: execle(%s): %s",
...@@ -1619,30 +1568,44 @@ mg_get_cookie(const struct mg_connection *conn, ...@@ -1619,30 +1568,44 @@ mg_get_cookie(const struct mg_connection *conn,
} }
/* /*
* Transform URI to the file name. * Get document root for the given URI.
*/ */
static void static int
convert_uri_to_file_name(struct mg_connection *conn, const char *uri, get_document_root(const struct mg_connection *conn, struct vec *document_root)
char *buf, size_t buf_len)
{ {
struct mg_context *ctx = conn->ctx; const char *root, *uri;
int len_of_matched_uri;
struct vec uri_vec, path_vec; struct vec uri_vec, path_vec;
const char *list;
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock); uri = conn->request_info.uri;
mg_snprintf(conn, buf, buf_len, "%s%s", ctx->options[OPT_ROOT], uri); len_of_matched_uri = 0;
root = next_option(conn->ctx->config->document_root,
document_root, NULL);
/* If requested URI has aliased prefix, use alternate root */ while ((root = next_option(root, &uri_vec, &path_vec)) != NULL) {
list = ctx->options[OPT_ALIASES];
while ((list = next_option(list, &uri_vec, &path_vec)) != NULL) {
if (memcmp(uri, uri_vec.ptr, uri_vec.len) == 0) { if (memcmp(uri, uri_vec.ptr, uri_vec.len) == 0) {
(void) mg_snprintf(conn, buf, buf_len, "%.*s%s", *document_root = path_vec;
path_vec.len, path_vec.ptr, uri + uri_vec.len); len_of_matched_uri = uri_vec.len;
break; break;
} }
} }
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
return (len_of_matched_uri);
}
/*
* Transform URI to the file name.
*/
static void
convert_uri_to_file_name(struct mg_connection *conn, const char *uri,
char *buf, size_t buf_len)
{
struct vec vec;
int match_len;
match_len = get_document_root(conn, &vec);
(void) mg_snprintf(conn, buf, buf_len, "%.*s%s",
vec.len, vec.ptr, uri + match_len);
#ifdef _WIN32 #ifdef _WIN32
fix_directory_separators(buf); fix_directory_separators(buf);
...@@ -1873,18 +1836,15 @@ get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec) ...@@ -1873,18 +1836,15 @@ get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec)
* Scan user-defined mime types first, in case user wants to * Scan user-defined mime types first, in case user wants to
* override default mime types. * override default mime types.
*/ */
(void) pthread_rwlock_rdlock(&ctx->rwlock); list = ctx->config->mime_types;
list = ctx->options[OPT_MIME_TYPES];
while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
/* ext now points to the path suffix */ /* ext now points to the path suffix */
ext = path + path_len - ext_vec.len; ext = path + path_len - ext_vec.len;
if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
*vec = mime_vec; *vec = mime_vec;
(void) pthread_rwlock_unlock(&ctx->rwlock);
return; return;
} }
} }
(void) pthread_rwlock_unlock(&ctx->rwlock);
/* Now scan built-in mime types */ /* Now scan built-in mime types */
for (i = 0; mime_types[i].extension != NULL; i++) { for (i = 0; mime_types[i].extension != NULL; i++) {
...@@ -2217,13 +2177,12 @@ open_auth_file(struct mg_connection *conn, const char *path) ...@@ -2217,13 +2177,12 @@ open_auth_file(struct mg_connection *conn, const char *path)
struct mgstat st; struct mgstat st;
FILE *fp; FILE *fp;
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock); if (ctx->config->global_passwords_file != NULL) {
if (ctx->options[OPT_AUTH_GPASSWD] != NULL) {
/* Use global passwords file */ /* Use global passwords file */
fp = mg_fopen(ctx->options[OPT_AUTH_GPASSWD], "r"); fp = mg_fopen(ctx->config->global_passwords_file, "r");
if (fp == NULL) if (fp == NULL)
cry(fc(ctx), "fopen(%s): %s", cry(fc(ctx), "fopen(%s): %s",
ctx->options[OPT_AUTH_GPASSWD], strerror(ERRNO)); ctx->config->global_passwords_file, strerror(ERRNO));
} else if (!mg_stat(path, &st) && st.is_directory) { } else if (!mg_stat(path, &st) && st.is_directory) {
(void) mg_snprintf(conn, name, sizeof(name), "%s%c%s", (void) mg_snprintf(conn, name, sizeof(name), "%s%c%s",
path, DIRSEP, PASSWORDS_FILE_NAME); path, DIRSEP, PASSWORDS_FILE_NAME);
...@@ -2249,7 +2208,6 @@ open_auth_file(struct mg_connection *conn, const char *path) ...@@ -2249,7 +2208,6 @@ open_auth_file(struct mg_connection *conn, const char *path)
(int) (e - p), p, DIRSEP, PASSWORDS_FILE_NAME); (int) (e - p), p, DIRSEP, PASSWORDS_FILE_NAME);
fp = mg_fopen(name, "r"); fp = mg_fopen(name, "r");
} }
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
return (fp); return (fp);
} }
...@@ -2330,24 +2288,20 @@ static bool_t ...@@ -2330,24 +2288,20 @@ static bool_t
authorize(struct mg_connection *conn, FILE *fp) authorize(struct mg_connection *conn, FILE *fp)
{ {
struct ah ah; struct ah ah;
char line[256], f_user[256], domain[256], ha1[256], char line[256], f_user[256], ha1[256],
f_domain[256], buf[MAX_REQUEST_SIZE]; f_domain[256], buf[MAX_REQUEST_SIZE];
if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) if (!parse_auth_header(conn, buf, sizeof(buf), &ah))
return (FALSE); return (FALSE);
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock);
mg_strlcpy(domain, conn->ctx->options[OPT_AUTH_DOMAIN], sizeof(domain));
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
/* Loop over passwords file */ /* Loop over passwords file */
while (fgets(line, sizeof(line), fp) != NULL) { while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3)
continue; continue;
if (!strcmp(ah.user, f_user) && !strcmp(domain, f_domain)) if (!strcmp(ah.user, f_user) &&
!strcmp(conn->ctx->config->auth_domain, f_domain))
return (check_password( return (check_password(
conn->request_info.request_method, conn->request_info.request_method,
ha1, ah.uri, ah.nonce, ah.nc, ah.cnonce, ah.qop, ha1, ah.uri, ah.nonce, ah.nc, ah.cnonce, ah.qop,
...@@ -2372,8 +2326,7 @@ check_authorization(struct mg_connection *conn, const char *path) ...@@ -2372,8 +2326,7 @@ check_authorization(struct mg_connection *conn, const char *path)
fp = NULL; fp = NULL;
authorized = TRUE; authorized = TRUE;
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock); list = conn->ctx->config->protect;
list = conn->ctx->options[OPT_PROTECT];
while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) { while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) { if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
(void) mg_snprintf(conn, fname, sizeof(fname), "%.*s", (void) mg_snprintf(conn, fname, sizeof(fname), "%.*s",
...@@ -2384,7 +2337,6 @@ check_authorization(struct mg_connection *conn, const char *path) ...@@ -2384,7 +2337,6 @@ check_authorization(struct mg_connection *conn, const char *path)
break; break;
} }
} }
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
if (fp == NULL) if (fp == NULL)
fp = open_auth_file(conn, path); fp = open_auth_file(conn, path);
...@@ -2400,18 +2352,13 @@ check_authorization(struct mg_connection *conn, const char *path) ...@@ -2400,18 +2352,13 @@ check_authorization(struct mg_connection *conn, const char *path)
static void static void
send_authorization_request(struct mg_connection *conn) send_authorization_request(struct mg_connection *conn)
{ {
char domain[128];
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock);
mg_strlcpy(domain, conn->ctx->options[OPT_AUTH_DOMAIN], sizeof(domain));
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
conn->request_info.status_code = 401; conn->request_info.status_code = 401;
(void) mg_printf(conn, (void) mg_printf(conn,
"HTTP/1.1 401 Unauthorized\r\n" "HTTP/1.1 401 Unauthorized\r\n"
"WWW-Authenticate: Digest qop=\"auth\", " "WWW-Authenticate: Digest qop=\"auth\", "
"realm=\"%s\", nonce=\"%lu\"\r\n\r\n", "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
domain, (unsigned long) time(NULL)); conn->ctx->config->auth_domain,
(unsigned long) time(NULL));
} }
static bool_t static bool_t
...@@ -2420,10 +2367,8 @@ is_authorized_for_put(struct mg_connection *conn) ...@@ -2420,10 +2367,8 @@ is_authorized_for_put(struct mg_connection *conn)
FILE *fp; FILE *fp;
int ret = FALSE; int ret = FALSE;
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock); fp = conn->ctx->config->put_delete_passwords_file == NULL ? NULL :
fp = conn->ctx->options[OPT_AUTH_PUT] == NULL ? NULL : mg_fopen(conn->ctx->config->put_delete_passwords_file, "r");
mg_fopen(conn->ctx->options[OPT_AUTH_PUT], "r");
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
if (fp != NULL) { if (fp != NULL) {
...@@ -2439,16 +2384,13 @@ mg_modify_passwords_file(struct mg_context *ctx, const char *fname, ...@@ -2439,16 +2384,13 @@ mg_modify_passwords_file(struct mg_context *ctx, const char *fname,
const char *user, const char *pass) const char *user, const char *pass)
{ {
int found; int found;
char line[512], u[512], d[512], domain[512], char line[512], u[512], d[512], ha1[33], tmp[FILENAME_MAX];
ha1[33], tmp[FILENAME_MAX]; const char *domain;
FILE *fp, *fp2; FILE *fp, *fp2;
found = 0; found = 0;
fp = fp2 = NULL; fp = fp2 = NULL;
domain = ctx->config->auth_domain;
(void) pthread_rwlock_rdlock(&ctx->rwlock);
mg_strlcpy(domain, ctx->options[OPT_AUTH_DOMAIN], sizeof(domain));
(void) pthread_rwlock_unlock(&ctx->rwlock);
/* Regard empty password as no password - remove user record. */ /* Regard empty password as no password - remove user record. */
if (pass[0] == '\0') if (pass[0] == '\0')
...@@ -2731,7 +2673,7 @@ send_opened_file_stream(struct mg_connection *conn, FILE *fp, int64_t len) ...@@ -2731,7 +2673,7 @@ send_opened_file_stream(struct mg_connection *conn, FILE *fp, int64_t len)
static int static int
parse_range_header(const char *header, int64_t *a, int64_t *b) parse_range_header(const char *header, int64_t *a, int64_t *b)
{ {
return sscanf(header, "bytes=%" INT64_FMT "u-%" INT64_FMT "u", a, b); return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
} }
/* /*
...@@ -2906,8 +2848,7 @@ substitute_index_file(struct mg_connection *conn, ...@@ -2906,8 +2848,7 @@ substitute_index_file(struct mg_connection *conn,
* Traverse index files list. For each entry, append it to the given * Traverse index files list. For each entry, append it to the given
* path and see if the file exists. If it exists, break the loop * path and see if the file exists. If it exists, break the loop
*/ */
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock); list = conn->ctx->config->index_files;
list = conn->ctx->options[OPT_INDEX_FILES];
found = FALSE; found = FALSE;
while ((list = next_option(list, &filename_vec, NULL)) != NULL) { while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
...@@ -2928,7 +2869,6 @@ substitute_index_file(struct mg_connection *conn, ...@@ -2928,7 +2869,6 @@ substitute_index_file(struct mg_connection *conn,
break; break;
} }
} }
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
/* If no index file exists, restore directory path */ /* If no index file exists, restore directory path */
if (found == FALSE) if (found == FALSE)
...@@ -3100,7 +3040,7 @@ prepare_cgi_environment(struct mg_connection *conn, const char *prog, ...@@ -3100,7 +3040,7 @@ prepare_cgi_environment(struct mg_connection *conn, const char *prog,
struct cgi_env_block *blk) struct cgi_env_block *blk)
{ {
const char *s, *script_filename, *slash; const char *s, *script_filename, *slash;
struct vec var_vec; struct vec var_vec, root;
char *p; char *p;
int i; int i;
...@@ -3112,11 +3052,11 @@ prepare_cgi_environment(struct mg_connection *conn, const char *prog, ...@@ -3112,11 +3052,11 @@ prepare_cgi_environment(struct mg_connection *conn, const char *prog,
if ((s = strrchr(prog, '/')) != NULL) if ((s = strrchr(prog, '/')) != NULL)
script_filename = s + 1; script_filename = s + 1;
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock); get_document_root(conn, &root);
addenv(blk, "SERVER_NAME=%s", conn->ctx->options[OPT_AUTH_DOMAIN]);
addenv(blk, "SERVER_ROOT=%s", conn->ctx->options[OPT_ROOT]); addenv(blk, "SERVER_NAME=%s", conn->ctx->config->auth_domain);
addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->options[OPT_ROOT]); addenv(blk, "SERVER_ROOT=%.*s", root.len, root.ptr);
(void) pthread_rwlock_unlock(&conn->ctx->rwlock); addenv(blk, "DOCUMENT_ROOT=%.*s", root.len, root.ptr);
/* Prepare the environment block */ /* Prepare the environment block */
addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1"); addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
...@@ -3183,11 +3123,9 @@ prepare_cgi_environment(struct mg_connection *conn, const char *prog, ...@@ -3183,11 +3123,9 @@ prepare_cgi_environment(struct mg_connection *conn, const char *prog,
} }
/* Add user-specified variables */ /* Add user-specified variables */
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock); s = conn->ctx->config->cgi_environment;
s = conn->ctx->options[OPT_CGI_ENV];
while ((s = next_option(s, &var_vec, NULL)) != NULL) while ((s = next_option(s, &var_vec, NULL)) != NULL)
addenv(blk, "%.*s", var_vec.len, var_vec.ptr); addenv(blk, "%.*s", var_vec.len, var_vec.ptr);
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
blk->vars[blk->nvars++] = NULL; blk->vars[blk->nvars++] = NULL;
blk->buf[blk->len++] = '\0'; blk->buf[blk->len++] = '\0';
...@@ -3386,19 +3324,20 @@ do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, ...@@ -3386,19 +3324,20 @@ do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag,
int include_level) int include_level)
{ {
char file_name[BUFSIZ], path[FILENAME_MAX], *p; char file_name[BUFSIZ], path[FILENAME_MAX], *p;
struct vec root;
bool_t is_ssi; bool_t is_ssi;
FILE *fp; FILE *fp;
get_document_root(conn, &root);
/* /*
* sscanf() is safe here, since send_ssi_file() also uses buffer * sscanf() is safe here, since send_ssi_file() also uses buffer
* of size BUFSIZ to get the tag. So strlen(tag) is always < BUFSIZ. * of size BUFSIZ to get the tag. So strlen(tag) is always < BUFSIZ.
*/ */
if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) { if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
/* File name is relative to the webserver root */ /* File name is relative to the webserver root */
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock); (void) mg_snprintf(conn, path, sizeof(path), "%.*s%c%s",
(void) mg_snprintf(conn, path, sizeof(path), "%s%c%s", root.len, root.ptr, DIRSEP, file_name);
conn->ctx->options[OPT_ROOT], DIRSEP, file_name);
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
} else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) { } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) {
/* /*
* File name is relative to the webserver working directory * File name is relative to the webserver working directory
...@@ -3422,10 +3361,8 @@ do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, ...@@ -3422,10 +3361,8 @@ do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag,
tag, path, strerror(ERRNO)); tag, path, strerror(ERRNO));
} else { } else {
set_close_on_exec(fileno(fp)); set_close_on_exec(fileno(fp));
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock);
is_ssi = match_extension(path, is_ssi = match_extension(path,
conn->ctx->options[OPT_SSI_EXTENSIONS]); conn->ctx->config->ssi_extensions);
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
if (is_ssi) { if (is_ssi) {
send_ssi_file(conn, path, fp, include_level + 1); send_ssi_file(conn, path, fp, include_level + 1);
} else { } else {
...@@ -3542,7 +3479,7 @@ send_ssi(struct mg_connection *conn, const char *path) ...@@ -3542,7 +3479,7 @@ send_ssi(struct mg_connection *conn, const char *path)
* a directory, or call embedded function, etcetera. * a directory, or call embedded function, etcetera.
*/ */
static void static void
analyze_request(struct mg_connection *conn) handle_request(struct mg_connection *conn)
{ {
struct mg_request_info *ri; struct mg_request_info *ri;
char path[FILENAME_MAX]; char path[FILENAME_MAX];
...@@ -3555,7 +3492,7 @@ analyze_request(struct mg_connection *conn) ...@@ -3555,7 +3492,7 @@ analyze_request(struct mg_connection *conn)
if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL)
* conn->request_info.query_string++ = '\0'; * conn->request_info.query_string++ = '\0';
uri_len = strlen(ri->uri); uri_len = strlen(ri->uri);
new_request_callback = conn->ctx->callbacks[MG_EVENT_NEW_REQUEST]; new_request_callback = conn->ctx->config->new_request_handler;
(void) url_decode(ri->uri, uri_len, ri->uri, uri_len + 1, FALSE); (void) url_decode(ri->uri, uri_len, ri->uri, uri_len + 1, FALSE);
remove_double_dots_and_double_slashes(ri->uri); remove_double_dots_and_double_slashes(ri->uri);
convert_uri_to_file_name(conn, ri->uri, path, sizeof(path)); convert_uri_to_file_name(conn, ri->uri, path, sizeof(path));
...@@ -3567,9 +3504,11 @@ analyze_request(struct mg_connection *conn) ...@@ -3567,9 +3504,11 @@ analyze_request(struct mg_connection *conn)
} else if (strstr(path, PASSWORDS_FILE_NAME)) { } else if (strstr(path, PASSWORDS_FILE_NAME)) {
/* Do not allow to view passwords files */ /* Do not allow to view passwords files */
send_error(conn, 403, "Forbidden", "Access Forbidden"); send_error(conn, 403, "Forbidden", "Access Forbidden");
} else if (conn->ctx->config->document_root == NULL) {
send_error(conn, 404, "Not Found", "Not Found");
} else if ((!strcmp(ri->request_method, "PUT") || } else if ((!strcmp(ri->request_method, "PUT") ||
!strcmp(ri->request_method, "DELETE")) && !strcmp(ri->request_method, "DELETE")) &&
(conn->ctx->options[OPT_AUTH_PUT] == NULL || (conn->ctx->config->put_delete_passwords_file == NULL ||
!is_authorized_for_put(conn))) { !is_authorized_for_put(conn))) {
send_authorization_request(conn); send_authorization_request(conn);
} else if (!strcmp(ri->request_method, "PUT")) { } else if (!strcmp(ri->request_method, "PUT")) {
...@@ -3588,15 +3527,14 @@ analyze_request(struct mg_connection *conn) ...@@ -3588,15 +3527,14 @@ analyze_request(struct mg_connection *conn)
"Location: %s/\r\n\r\n", ri->uri); "Location: %s/\r\n\r\n", ri->uri);
} else if (st.is_directory && } else if (st.is_directory &&
substitute_index_file(conn, path, sizeof(path), &st) == FALSE) { substitute_index_file(conn, path, sizeof(path), &st) == FALSE) {
if (is_true(conn->ctx->options[OPT_DIR_LIST])) { if (conn->ctx->config->enable_directory_listing) {
send_directory(conn, path); send_directory(conn, path);
} else { } else {
send_error(conn, 403, "Directory Listing Denied", send_error(conn, 403, "Directory Listing Denied",
"Directory listing denied"); "Directory listing denied");
} }
#if !defined(NO_CGI) #if !defined(NO_CGI)
} else if (match_extension(path, } else if (match_extension(path, conn->ctx->config->cgi_extensions)) {
conn->ctx->options[OPT_CGI_EXTENSIONS])) {
if (strcmp(ri->request_method, "POST") && if (strcmp(ri->request_method, "POST") &&
strcmp(ri->request_method, "GET")) { strcmp(ri->request_method, "GET")) {
send_error(conn, 501, "Not Implemented", send_error(conn, 501, "Not Implemented",
...@@ -3606,8 +3544,7 @@ analyze_request(struct mg_connection *conn) ...@@ -3606,8 +3544,7 @@ analyze_request(struct mg_connection *conn)
} }
#endif /* NO_CGI */ #endif /* NO_CGI */
#if !defined(NO_SSI) #if !defined(NO_SSI)
} else if (match_extension(path, } else if (match_extension(path, conn->ctx->config->ssi_extensions)) {
conn->ctx->options[OPT_SSI_EXTENSIONS])) {
send_ssi(conn, path); send_ssi(conn, path);
#endif /* NO_SSI */ #endif /* NO_SSI */
} else if (is_not_modified(conn, &st)) { } else if (is_not_modified(conn, &st)) {
...@@ -3630,12 +3567,13 @@ close_all_listening_sockets(struct mg_context *ctx) ...@@ -3630,12 +3567,13 @@ close_all_listening_sockets(struct mg_context *ctx)
} }
static enum mg_error_t static enum mg_error_t
set_ports_option(struct mg_context *ctx, const char *list) set_ports_option(struct mg_context *ctx)
{ {
SOCKET sock; SOCKET sock;
int is_ssl; int is_ssl;
struct vec vec; struct vec vec;
struct socket *listener; struct socket *listener;
const char *list = ctx->config->listening_ports;
close_all_listening_sockets(ctx); close_all_listening_sockets(ctx);
...@@ -3685,10 +3623,8 @@ log_access(const struct mg_connection *conn) ...@@ -3685,10 +3623,8 @@ log_access(const struct mg_connection *conn)
FILE *fp; FILE *fp;
char date[64]; char date[64];
(void) pthread_rwlock_rdlock(&conn->ctx->rwlock); fp = conn->ctx->config->access_log_file == NULL ? NULL :
fp = conn->ctx->options[OPT_ACCESS_LOG] == NULL ? NULL : mg_fopen(conn->ctx->config->access_log_file, "a+");
mg_fopen(conn->ctx->options[OPT_ACCESS_LOG], "a+");
(void) pthread_rwlock_unlock(&conn->ctx->rwlock);
if (fp == NULL) if (fp == NULL)
return; return;
...@@ -3728,12 +3664,16 @@ isbyte(int n) { ...@@ -3728,12 +3664,16 @@ isbyte(int n) {
* Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
*/ */
static enum mg_error_t static enum mg_error_t
check_acl(struct mg_context *ctx, const char *list, const struct usa *usa) check_acl(struct mg_context *ctx, const struct usa *usa)
{ {
int a, b, c, d, n, mask, allowed; int a, b, c, d, n, mask, allowed;
char flag; char flag;
uint32_t acl_subnet, acl_mask, remote_ip; uint32_t acl_subnet, acl_mask, remote_ip;
struct vec vec; struct vec vec;
const char *list = ctx->config->acl;
if (list == NULL)
return (MG_SUCCESS);
(void) memcpy(&remote_ip, &usa->u.sin.sin_addr, sizeof(remote_ip)); (void) memcpy(&remote_ip, &usa->u.sin.sin_addr, sizeof(remote_ip));
...@@ -3783,48 +3723,17 @@ add_to_set(SOCKET fd, fd_set *set, int *max_fd) ...@@ -3783,48 +3723,17 @@ add_to_set(SOCKET fd, fd_set *set, int *max_fd)
*max_fd = (int) fd; *max_fd = (int) fd;
} }
/*
* Deallocate mongoose context, free up the resources
*/
static void
mg_fini(struct mg_context *ctx)
{
int i;
close_all_listening_sockets(ctx);
/* Wait until all threads finish */
(void) pthread_mutex_lock(&ctx->mutex);
while (ctx->num_threads > 0)
(void) pthread_cond_wait(&ctx->thr_cond, &ctx->mutex);
(void) pthread_mutex_unlock(&ctx->mutex);
/* Deallocate all options */
for (i = 0; i < NUM_OPTIONS; i++)
if (ctx->options[i] != NULL)
free(ctx->options[i]);
/* Deallocate SSL context */
if (ctx->ssl_ctx)
SSL_CTX_free(ctx->ssl_ctx);
(void) pthread_rwlock_destroy(&ctx->rwlock);
(void) pthread_mutex_destroy(&ctx->mutex);
(void) pthread_cond_destroy(&ctx->thr_cond);
(void) pthread_cond_destroy(&ctx->empty_cond);
(void) pthread_cond_destroy(&ctx->full_cond);
/* Signal mg_stop() that we're done */
ctx->stop_flag = 2;
}
#if !defined(_WIN32) #if !defined(_WIN32)
static enum mg_error_t static enum mg_error_t
set_uid_option(struct mg_context *ctx, const char *uid) set_uid_option(struct mg_context *ctx)
{ {
struct passwd *pw; struct passwd *pw;
enum mg_error_t error = MG_ERROR; const char *uid = ctx->config->uid;
enum mg_error_t error;
if (uid == NULL) {
error = MG_SUCCESS;
} else {
if ((pw = getpwnam(uid)) == NULL) if ((pw = getpwnam(uid)) == NULL)
cry(fc(ctx), "%s: unknown user [%s]", __func__, uid); cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
else if (setgid(pw->pw_gid) == -1) else if (setgid(pw->pw_gid) == -1)
...@@ -3835,6 +3744,7 @@ set_uid_option(struct mg_context *ctx, const char *uid) ...@@ -3835,6 +3744,7 @@ set_uid_option(struct mg_context *ctx, const char *uid)
__func__, uid, strerror(errno)); __func__, uid, strerror(errno));
else else
error = MG_SUCCESS; error = MG_SUCCESS;
}
return (error); return (error);
} }
...@@ -3900,10 +3810,14 @@ load_dll(struct mg_context *ctx, const char *dll_name, struct ssl_func *sw) ...@@ -3900,10 +3810,14 @@ load_dll(struct mg_context *ctx, const char *dll_name, struct ssl_func *sw)
* Dynamically load SSL library. Set up ctx->ssl_ctx pointer. * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
*/ */
static enum mg_error_t static enum mg_error_t
set_ssl_option(struct mg_context *ctx, const char *pem) set_ssl_option(struct mg_context *ctx)
{ {
SSL_CTX *CTX; SSL_CTX *CTX;
int i, size; int i, size;
const char *pem = ctx->config->ssl_certificate;
if (pem == NULL)
return (MG_SUCCESS);
if (load_dll(ctx, SSL_LIB, ssl_sw) == FALSE || if (load_dll(ctx, SSL_LIB, ssl_sw) == FALSE ||
load_dll(ctx, CRYPTO_LIB, crypto_sw) == FALSE) load_dll(ctx, CRYPTO_LIB, crypto_sw) == FALSE)
...@@ -3915,9 +3829,9 @@ set_ssl_option(struct mg_context *ctx, const char *pem) ...@@ -3915,9 +3829,9 @@ set_ssl_option(struct mg_context *ctx, const char *pem)
if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL) if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
ssl_cry(fc(ctx), "SSL_CTX_new error"); ssl_cry(fc(ctx), "SSL_CTX_new error");
else if (ctx->callbacks[MG_EVENT_SSL_PASSWORD] != NULL) else if (ctx->config->ssl_password_handler != NULL)
SSL_CTX_set_default_passwd_cb(CTX, SSL_CTX_set_default_passwd_cb(CTX,
ctx->callbacks[MG_EVENT_SSL_PASSWORD]); ctx->config->ssl_password_handler);
if (CTX != NULL && SSL_CTX_use_certificate_file( if (CTX != NULL && SSL_CTX_use_certificate_file(
CTX, pem, SSL_FILETYPE_PEM) == 0) { CTX, pem, SSL_FILETYPE_PEM) == 0) {
...@@ -3953,247 +3867,40 @@ set_ssl_option(struct mg_context *ctx, const char *pem) ...@@ -3953,247 +3867,40 @@ set_ssl_option(struct mg_context *ctx, const char *pem)
#endif /* !NO_SSL */ #endif /* !NO_SSL */
static enum mg_error_t static enum mg_error_t
set_gpass_option(struct mg_context *ctx, const char *path) set_gpass_option(struct mg_context *ctx)
{ {
struct mgstat mgstat; struct mgstat mgstat;
ctx = NULL; const char *path = ctx->config->global_passwords_file;
return (mg_stat(path, &mgstat) == 0 ? MG_SUCCESS : MG_ERROR);
}
static enum mg_error_t return (path == NULL || mg_stat(path, &mgstat) == 0 ?
set_acl_option(struct mg_context *ctx, const char *acl) MG_SUCCESS : MG_ERROR);
{
struct usa fake;
return (check_acl(ctx, acl, &fake));
} }
/*
* Check if the comma-separated list of options has a format of key-value
* pairs: "k1=v1,k2=v2". Return FALSE if any entry has invalid key or value.
*/
static enum mg_error_t static enum mg_error_t
set_kv_list_option(struct mg_context *ctx, const char *str) set_acl_option(struct mg_context *ctx)
{ {
const char *list; struct usa fake;
struct vec key, value; return (check_acl(ctx, &fake));
list = str;
while ((list = next_option(list, &key, &value)) != NULL)
if (key.len == 0 || value.len == 0) {
cry(fc(ctx), "Invalid list specified: [%s], "
"expecting key1=value1,key2=value2,...", str);
return (MG_ERROR);
}
return (MG_SUCCESS);
} }
static enum mg_error_t static bool_t
set_root_option(struct mg_context *ctx, const char *root) verify_document_root(struct mg_context *ctx, const char *root)
{ {
char path[FILENAME_MAX], *p;
struct mgstat buf; struct mgstat buf;
if ( mg_stat(root, &buf) != 0) {
cry(fc(ctx), "Invalid root directory: \"%s\"", root);
return (MG_ERROR);
}
return (MG_SUCCESS);
}
static const struct mg_option known_options[] = {
{"root", "\tWeb root directory", ".", OPT_ROOT, set_root_option},
{"index_files", "Index files", "index.html,index.htm,index.cgi",
OPT_INDEX_FILES, NULL},
#if !defined(NO_SSL)
{"ssl_cert", "SSL certificate file", NULL,
OPT_SSL_CERTIFICATE, &set_ssl_option},
#endif /* !NO_SSL */
{"ports", "Listening ports", NULL,
OPT_PORTS, &set_ports_option},
{"dir_list", "Directory listing", "yes",
OPT_DIR_LIST, NULL},
{"protect", "URI to htpasswd mapping", NULL,
OPT_PROTECT, &set_kv_list_option},
#if !defined(NO_CGI)
{"cgi_ext", "CGI extensions", ".cgi,.pl,.php",
OPT_CGI_EXTENSIONS, NULL},
{"cgi_interp", "CGI interpreter to use with all CGI scripts", NULL,
OPT_CGI_INTERPRETER, NULL},
{"cgi_env", "Custom CGI enviroment variables", NULL,
OPT_CGI_ENV, &set_kv_list_option},
#endif /* NO_CGI */
{"ssi_ext", "SSI extensions", ".shtml,.shtm",
OPT_SSI_EXTENSIONS, NULL},
{"auth_realm", "Authentication domain name", "mydomain.com",
OPT_AUTH_DOMAIN, NULL},
{"auth_gpass", "Global passwords file", NULL,
OPT_AUTH_GPASSWD, &set_gpass_option},
{"auth_PUT", "PUT,DELETE auth file", NULL,
OPT_AUTH_PUT, NULL},
#if !defined(_WIN32)
{"uid", "\tRun as user", NULL, OPT_UID, &set_uid_option},
#endif /* !_WIN32 */
{"access_log", "Access log file", NULL, OPT_ACCESS_LOG, NULL},
{"error_log", "Error log file", NULL, OPT_ERROR_LOG, NULL},
{"aliases", "Path=URI mappings", NULL,
OPT_ALIASES, &set_kv_list_option},
{"admin_uri", "Administration page URI", NULL, OPT_ADMIN_URI, NULL},
{"acl", "\tAllow/deny IP addresses/subnets", NULL,
OPT_ACL, &set_acl_option},
{"max_threads", "Maximum simultaneous threads to spawn", "100",
OPT_MAX_THREADS, NULL},
{"idle_time", "Time in seconds connection stays idle", "10",
OPT_IDLE_TIME, NULL},
{"mime_types", "Comma separated list of ext=mime_type pairs", NULL,
OPT_MIME_TYPES, &set_kv_list_option},
{NULL, NULL, NULL, 0, NULL}
};
static const struct mg_option *
find_opt(const char *opt_name)
{
int i;
for (i = 0; known_options[i].name != NULL; i++)
if (!strcmp(opt_name, known_options[i].name))
return (known_options + i);
return (NULL);
}
enum mg_error_t
mg_set_option(struct mg_context *ctx, const char *opt, const char *val)
{
const struct mg_option *option;
int i, error;
DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: [%s]->[%s]", __func__, opt, val));
if (opt != NULL && (option = find_opt(opt)) != NULL) {
i = (int) (option - known_options);
error = option->setter ? option->setter(ctx, val) : MG_SUCCESS;
/*
* Write lock the option. Free old value, set new value.
* Make sure no calls that may trigger read lock are made.
*/
(void) pthread_rwlock_wrlock(&ctx->rwlock);
if (ctx->options[option->index] != NULL)
free(ctx->options[option->index]);
ctx->options[option->index] = val ? mg_strdup(val) : NULL;
(void) pthread_rwlock_unlock(&ctx->rwlock);
if (error != MG_SUCCESS)
cry(fc(ctx), "%s(%s): failure", __func__, opt);
} else {
cry(fc(ctx), "%s: No such option: [%s]", __func__, opt);
error = MG_NOT_FOUND;
}
return (error);
}
void
mg_show_usage_string(FILE *fp)
{
const struct mg_option *o;
(void) fprintf(stderr,
"Mongoose version %s (c) Sergey Lyubka\n"
"usage: mongoose [options] [config_file]\n", mg_version());
fprintf(fp, " -A <htpasswd_file> <realm> <user> <passwd>\n");
for (o = known_options; o->name != NULL; o++) {
(void) fprintf(fp, " -%s\t%s", o->name, o->description);
if (o->default_value != NULL)
fprintf(fp, " (default: \"%s\")", o->default_value);
fputc('\n', fp);
}
}
enum mg_error_t
mg_get_option(struct mg_context *ctx, const char *option_name,
char *dst, size_t dst_len)
{
const struct mg_option *option;
enum mg_error_t error;
(void) pthread_rwlock_rdlock(&ctx->rwlock);
if ((option = find_opt(option_name)) == NULL) {
error = MG_NOT_FOUND;
} else if (ctx->options[option->index] == NULL) {
error = MG_SUCCESS;
dst[0] = '\0';
} else if (strlen(ctx->options[option->index]) >= dst_len) {
error = MG_BUFFER_TOO_SMALL;
} else {
mg_strlcpy(dst, ctx->options[option->index], dst_len);
error = MG_SUCCESS;
}
(void) pthread_rwlock_unlock(&ctx->rwlock);
return (error);
}
void if ((p = strchr(root, ',')) == NULL) {
mg_set_callback(struct mg_context *ctx, enum mg_event_t event, mg_callback_t cb) mg_strlcpy(path, root, sizeof(path));
{
/* No locking, no sanity checking. Suppose this is fine? */
ctx->callbacks[event] = cb;
}
#if 0
static void
admin_page(struct mg_connection *conn, const struct mg_request_info *ri,
void *user_data)
{
const struct mg_option *option;
const char *option_name, *option_value;
user_data = NULL; /* Unused */
(void) mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n\r\n"
"<html><body><h1>Mongoose v. %s</h1>", mg_version());
if (!strcmp(ri->request_method, "POST")) {
option_name = mg_get_var(conn, "o");
option_value = mg_get_var(conn, "v");
if (mg_set_option(conn->ctx,
option_name, option_value) == -1) {
(void) mg_printf(conn,
"<p style=\"background: red\">Error setting "
"option \"%s\"</p>",
option_name ? option_name : "(null)");
} else { } else {
(void) mg_printf(conn, mg_strlcpy(path, root, p - root + 1);
"<p style=\"color: green\">Saved: %s=%s</p>",
option_name, option_value ? option_value : "NULL");
} }
}
/* Print table with all options */
(void) mg_printf(conn, "%s", "<table border=\"1\""
"<tr><th>Option</th><th>Description</th>"
"<th colspan=2>Value</th></tr>");
for (option = known_options; option->name != NULL; option++) { if (mg_stat(path, &buf) != 0) {
option_value = mg_get_option(conn->ctx, option->name); cry(fc(ctx), "Invalid root directory: \"%s\"", root);
if (option_value == NULL) return (FALSE);
option_value = "";
(void) mg_printf(conn,
"<form method=post><tr><td>%s</td><td>%s</td>"
"<input type=hidden name=o value='%s'>"
"<td><input type=text name=v value='%s'></td>"
"<td><input type=submit value=save></td></form></tr>",
option->name, option->description,
option->name, option_value);
} }
return (TRUE);
(void) mg_printf(conn, "%s", "</table></body></html>");
} }
#endif
static void static void
reset_per_request_attributes(struct mg_connection *conn) reset_per_request_attributes(struct mg_connection *conn)
...@@ -4312,7 +4019,7 @@ process_new_connection(struct mg_connection *conn) ...@@ -4312,7 +4019,7 @@ process_new_connection(struct mg_connection *conn)
ri->post_data = buf + request_len; ri->post_data = buf + request_len;
ri->post_data_len = nread - request_len; ri->post_data_len = nread - request_len;
conn->birth_time = time(NULL); conn->birth_time = time(NULL);
analyze_request(conn); handle_request(conn);
log_access(conn); log_access(conn);
shift_to_next(conn, buf, request_len, &nread); shift_to_next(conn, buf, request_len, &nread);
} }
...@@ -4340,7 +4047,7 @@ get_socket(struct mg_context *ctx, struct socket *sp) ...@@ -4340,7 +4047,7 @@ get_socket(struct mg_context *ctx, struct socket *sp)
ctx->num_idle++; ctx->num_idle++;
while (ctx->sq_head == ctx->sq_tail) { while (ctx->sq_head == ctx->sq_tail) {
ts.tv_nsec = 0; ts.tv_nsec = 0;
ts.tv_sec = time(NULL) + atoi(ctx->options[OPT_IDLE_TIME]) + 1; ts.tv_sec = time(NULL) + 10;
if (pthread_cond_timedwait(&ctx->empty_cond, if (pthread_cond_timedwait(&ctx->empty_cond,
&ctx->mutex, &ts) != 0) { &ctx->mutex, &ts) != 0) {
/* Timeout! release the mutex and return */ /* Timeout! release the mutex and return */
...@@ -4447,7 +4154,7 @@ put_socket(struct mg_context *ctx, const struct socket *sp) ...@@ -4447,7 +4154,7 @@ put_socket(struct mg_context *ctx, const struct socket *sp)
/* If there are no idle threads, start one */ /* If there are no idle threads, start one */
if (ctx->num_idle == 0 && if (ctx->num_idle == 0 &&
ctx->num_threads < atoi(ctx->options[OPT_MAX_THREADS])) { ctx->num_threads < atoi(ctx->config->num_threads)) {
if (start_thread(ctx, if (start_thread(ctx,
(mg_thread_func_t) worker_thread, ctx) != 0) (mg_thread_func_t) worker_thread, ctx) != 0)
cry(fc(ctx), "Cannot start thread: %d", ERRNO); cry(fc(ctx), "Cannot start thread: %d", ERRNO);
...@@ -4471,10 +4178,7 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx) ...@@ -4471,10 +4178,7 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx)
&accepted.rsa.u.sa, &accepted.rsa.len)) == INVALID_SOCKET) &accepted.rsa.u.sa, &accepted.rsa.len)) == INVALID_SOCKET)
return; return;
(void) pthread_rwlock_rdlock(&ctx->rwlock); allowed = check_acl(ctx, &accepted.rsa) == MG_SUCCESS;
allowed = ctx->options[OPT_ACL] == NULL ||
check_acl(ctx, ctx->options[OPT_ACL], &accepted.rsa) == MG_SUCCESS;
(void) pthread_rwlock_unlock(&ctx->rwlock);
if (allowed) { if (allowed) {
/* Put accepted socket structure into the queue */ /* Put accepted socket structure into the queue */
...@@ -4502,10 +4206,8 @@ master_thread(struct mg_context *ctx) ...@@ -4502,10 +4206,8 @@ master_thread(struct mg_context *ctx)
max_fd = -1; max_fd = -1;
/* Add listening sockets to the read set */ /* Add listening sockets to the read set */
(void) pthread_rwlock_rdlock(&ctx->rwlock);
for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next)
add_to_set(sp->sock, &read_set, &max_fd); add_to_set(sp->sock, &read_set, &max_fd);
(void) pthread_rwlock_unlock(&ctx->rwlock);
tv.tv_sec = 1; tv.tv_sec = 1;
tv.tv_usec = 0; tv.tv_usec = 0;
...@@ -4521,17 +4223,33 @@ master_thread(struct mg_context *ctx) ...@@ -4521,17 +4223,33 @@ master_thread(struct mg_context *ctx)
sleep(1); sleep(1);
#endif /* _WIN32 */ #endif /* _WIN32 */
} else { } else {
(void) pthread_rwlock_rdlock(&ctx->rwlock);
for (sp = ctx->listening_sockets; for (sp = ctx->listening_sockets;
sp != NULL; sp = sp->next) sp != NULL; sp = sp->next)
if (FD_ISSET(sp->sock, &read_set)) if (FD_ISSET(sp->sock, &read_set))
accept_new_connection(sp, ctx); accept_new_connection(sp, ctx);
(void) pthread_rwlock_unlock(&ctx->rwlock);
} }
} }
/* Stop signal received: somebody called mg_stop. Quit. */ /* Stop signal received: somebody called mg_stop. Quit. */
mg_fini(ctx); close_all_listening_sockets(ctx);
/* Wait until all threads finish */
(void) pthread_mutex_lock(&ctx->mutex);
while (ctx->num_threads > 0)
(void) pthread_cond_wait(&ctx->thr_cond, &ctx->mutex);
(void) pthread_mutex_unlock(&ctx->mutex);
/* Deallocate SSL context */
if (ctx->ssl_ctx != NULL)
SSL_CTX_free(ctx->ssl_ctx);
(void) pthread_mutex_destroy(&ctx->mutex);
(void) pthread_cond_destroy(&ctx->thr_cond);
(void) pthread_cond_destroy(&ctx->empty_cond);
(void) pthread_cond_destroy(&ctx->full_cond);
/* Signal mg_stop() that we're done */
ctx->stop_flag = 2;
} }
void void
...@@ -4552,48 +4270,57 @@ mg_stop(struct mg_context *ctx) ...@@ -4552,48 +4270,57 @@ mg_stop(struct mg_context *ctx)
} }
struct mg_context * struct mg_context *
mg_start(void) mg_start(struct mg_config *config)
{ {
struct mg_context *ctx; struct mg_context *ctx, fake_ctx;
const struct mg_option *option;
#if defined(_WIN32) #if defined(_WIN32)
WSADATA data; WSADATA data;
WSAStartup(MAKEWORD(2,2), &data); WSAStartup(MAKEWORD(2,2), &data);
#endif /* _WIN32 */ #endif /* _WIN32 */
if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) { fake_ctx.config = config;
cry(fc(ctx), "cannot allocate mongoose context");
if (config->listening_ports == NULL ||
config->num_threads == NULL ||
config->auth_domain == NULL) {
cry(fc(&fake_ctx), "Please specify "
"num_threads, listening_ports, auth_domain");
return (NULL);
} else if (config->document_root != NULL &&
verify_document_root(&fake_ctx, config->document_root) != TRUE) {
cry(fc(&fake_ctx), "Invalid root directory: \"%s\"",
config->document_root);
return (NULL);
} else if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
cry(fc(&fake_ctx), "Cannot allocate mongoose context");
return (NULL); return (NULL);
} }
ctx->config = config;
/* Initialize options. First pass: set default option values */ /*
for (option = known_options; option->name != NULL; option++) * NOTE(lsm): order is important here. SSL certificates must
ctx->options[option->index] = option->default_value == NULL ? * be initialized before listening ports.
NULL : mg_strdup(option->default_value); */
if (set_ssl_option(ctx) == MG_ERROR ||
/* Call setter functions */ set_ports_option(ctx) == MG_ERROR ||
for (option = known_options; option->name != NULL; option++) set_gpass_option(ctx) == MG_ERROR ||
if (option->setter != NULL && #if !defined(_WIN32)
ctx->options[option->index] != NULL) set_uid_option(ctx) == MG_ERROR ||
if (option->setter(ctx, #endif
ctx->options[option->index]) == FALSE) { set_acl_option(ctx) == MG_ERROR) {
mg_fini(ctx); free(ctx);
return (NULL); return (NULL);
} }
DEBUG_TRACE((DEBUG_MGS_PREFIX "%s: root [%s]",
__func__, ctx->options[OPT_ROOT]));
#if !defined(_WIN32) #if !defined(_WIN32)
/* /*
* Ignore SIGPIPE signal, so if browser cancels the request, it * Ignore SIGPIPE signal, so if browser cancels the request, it
* won't kill the whole process. * won't kill the whole process.
*/ */
(void) signal(SIGPIPE, SIG_IGN); (void) signal(SIGPIPE, SIG_IGN);
#endif /* _WIN32 */ #endif /* !_WIN32 */
(void) pthread_rwlock_init(&ctx->rwlock, NULL);
(void) pthread_mutex_init(&ctx->mutex, NULL); (void) pthread_mutex_init(&ctx->mutex, NULL);
(void) pthread_cond_init(&ctx->thr_cond, NULL); (void) pthread_cond_init(&ctx->thr_cond, NULL);
(void) pthread_cond_init(&ctx->empty_cond, NULL); (void) pthread_cond_init(&ctx->empty_cond, NULL);
......
...@@ -59,8 +59,21 @@ struct mg_request_info { ...@@ -59,8 +59,21 @@ struct mg_request_info {
/* /*
* Error codes for all functions that return 'int'. * User-defined handler function. It must return MG_SUCCESS or MG_ERROR.
*
* If handler returns MG_SUCCESS, that means that handler has processed the
* request by sending appropriate HTTP reply to the client. Mongoose treats
* the request as served.
*
* If callback returns MG_ERROR, that means that callback has not processed
* the request. Handler must not send any data to the client in this case.
* Mongoose proceeds with request handling as if nothing happened.
*
* NOTE: ssl_password_handler must have the following prototype:
* int (*)(char *, int, int, void *)
* Refer to OpenSSL documentation for more details.
*/ */
enum mg_error_t { enum mg_error_t {
MG_ERROR, MG_ERROR,
MG_SUCCESS, MG_SUCCESS,
...@@ -68,15 +81,48 @@ enum mg_error_t { ...@@ -68,15 +81,48 @@ enum mg_error_t {
MG_BUFFER_TOO_SMALL MG_BUFFER_TOO_SMALL
}; };
typedef enum mg_error_t (*mg_callback_t)(struct mg_connection *,
const struct mg_request_info *);
/*
* This structure describes Mongoose configuration.
*/
struct mg_config {
char *document_root;
char *index_files;
char *ssl_certificate;
char *listening_ports;
char *cgi_extensions;
char *cgi_interpreter;
char *cgi_environment;
char *ssi_extensions;
char *auth_domain;
char *protect;
char *global_passwords_file;
char *put_delete_passwords_file;
char *access_log_file;
char *error_log_file;
char *acl;
char *uid;
char *mime_types;
char *enable_directory_listing;
char *num_threads;
mg_callback_t new_request_handler;
mg_callback_t http_error_handler;
mg_callback_t event_log_handler;
mg_callback_t ssl_password_handler;
};
/* /*
* Start the web server. * Start the web server.
* *
* This must be the first function called by the application. * This must be the first function called by the application.
* It creates a serving thread, and returns a context structure that * It creates a serving thread, and returns a context structure that
* can be used to alter the configuration, and stop the server. * can be used to stop the server.
*/ */
struct mg_context *mg_start(void); struct mg_context *mg_start(struct mg_config *);
/* /*
...@@ -89,32 +135,6 @@ struct mg_context *mg_start(void); ...@@ -89,32 +135,6 @@ struct mg_context *mg_start(void);
void mg_stop(struct mg_context *); void mg_stop(struct mg_context *);
/*
* Get the current value of a particular option.
*
* Return:
* MG_SUCCESS, MG_NOT_FOUND, MG_BUFFER_TOO_SMALL
*/
enum mg_error_t mg_get_option(struct mg_context *,
const char *option_name, char *buf, size_t buf_len);
/*
* Set a value for a particular option.
*
* Mongoose makes an internal copy of the option value string, which must be
* valid nul-terminated ASCII or UTF-8 string. It is safe to change any option
* at any time. The order of setting various options is also irrelevant with
* one exception: if "ports" option contains SSL listening ports, a "ssl_cert"
* option must be set BEFORE the "ports" option.
*
* Return:
* MG_ERROR, MG_SUCCESS, or MG_NOT_FOUND if option is unknown.
*/
enum mg_error_t mg_set_option(struct mg_context *,
const char *name, const char *value);
/* /*
* Add, edit or delete the entry in the passwords file. * Add, edit or delete the entry in the passwords file.
* *
...@@ -133,36 +153,6 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx, ...@@ -133,36 +153,6 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx,
const char *file_name, const char *user, const char *password); const char *file_name, const char *user, const char *password);
/*
* Attach a callback function to certain event.
* Callback must return MG_SUCCESS or MG_ERROR.
*
* If callback returns MG_SUCCESS, that means that callback has processed the
* request by sending appropriate HTTP reply to the client. Mongoose treats
* the request as served.
*
* If callback returns MG_ERROR, that means that callback has not processed
* the request. Callback must not send any data to client in this case.
* Mongoose proceeds with request handling.
*
* NOTE: for MG_EVENT_SSL_PASSWORD event the callback must have
* int (*)(char *, int, int, void *) prototype. Refer to OpenSSL documentation
* for more details about the SSL password callback.
*/
enum mg_event_t {
MG_EVENT_NEW_REQUEST, /* New HTTP request has arrived */
MG_EVENT_HTTP_ERROR, /* Mongoose is about to send HTTP error */
MG_EVENT_LOG, /* Mongoose is about to log a message */
MG_EVENT_SSL_PASSWORD, /* SSL certificate needs verification */
NUM_EVENTS
};
typedef enum mg_error_t (*mg_callback_t)(struct mg_connection *,
const struct mg_request_info *);
void mg_set_callback(struct mg_context *, enum mg_event_t, mg_callback_t);
/* /*
* Send data to the client. * Send data to the client.
*/ */
...@@ -185,6 +175,7 @@ int mg_printf(struct mg_connection *, const char *fmt, ...); ...@@ -185,6 +175,7 @@ int mg_printf(struct mg_connection *, const char *fmt, ...);
*/ */
int mg_read(struct mg_connection *, void *buf, size_t len); int mg_read(struct mg_connection *, void *buf, size_t len);
/* /*
* Get the value of particular HTTP header. * Get the value of particular HTTP header.
* *
...@@ -247,11 +238,6 @@ const char *mg_version(void); ...@@ -247,11 +238,6 @@ const char *mg_version(void);
void mg_md5(char *buf, ...); void mg_md5(char *buf, ...);
/*
* Print command line usage string.
*/
void mg_show_usage_string(FILE *fp);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */
......
...@@ -13,12 +13,11 @@ sub on_windows { $^O =~ /win32/i; } ...@@ -13,12 +13,11 @@ sub on_windows { $^O =~ /win32/i; }
my $port = 23456; my $port = 23456;
my $pid = undef; my $pid = undef;
my $num_requests; my $num_requests;
my $root = 'test';
my $dir_separator = on_windows() ? '\\' : '/'; my $dir_separator = on_windows() ? '\\' : '/';
my $copy_cmd = on_windows() ? 'copy' : 'cp'; my $copy_cmd = on_windows() ? 'copy' : 'cp';
my $test_dir_uri = "test_dir"; my $test_dir_uri = "test_dir";
my $root = 'test';
my $test_dir = $root . $dir_separator. $test_dir_uri; my $test_dir = $root . $dir_separator. $test_dir_uri;
my $alias = "/aliased=/etc/,/ta=$test_dir";
my $config = 'mongoose.conf'; my $config = 'mongoose.conf';
my $exe = '.' . $dir_separator . 'mongoose'; my $exe = '.' . $dir_separator . 'mongoose';
my $embed_exe = '.' . $dir_separator . 'embed'; my $embed_exe = '.' . $dir_separator . 'embed';
...@@ -89,6 +88,7 @@ sub o { ...@@ -89,6 +88,7 @@ sub o {
# Spawn a server listening on specified port # Spawn a server listening on specified port
sub spawn { sub spawn {
my ($cmdline) = @_; my ($cmdline) = @_;
print 'Executing: ', @_, "\n";
if (on_windows()) { if (on_windows()) {
my @args = split /\s+/, $cmdline; my @args = split /\s+/, $cmdline;
my $executable = $args[0]; my $executable = $args[0];
...@@ -159,7 +159,7 @@ kill_spawned_child(); ...@@ -159,7 +159,7 @@ kill_spawned_child();
my $cmd = "$exe -ports $port -access_log access.log -error_log debug.log ". my $cmd = "$exe -ports $port -access_log access.log -error_log debug.log ".
"-cgi_env CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " . "-cgi_env CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
"-mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " . "-mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
"-root test -aliases $alias -admin_uri /hh"; "-root $root,/aiased=/etc/,/ta=$test_dir";
$cmd .= ' -cgi_interp perl' if on_windows(); $cmd .= ' -cgi_interp perl' if on_windows();
spawn($cmd); spawn($cmd);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment