diff --git a/main.c b/main.c index 0568c26b..7cd27f9a 100644 --- a/main.c +++ b/main.c @@ -57,239 +57,213 @@ static int exit_flag; /* Program termination flag */ #define CONFIG_FILE "mongoose.conf" #endif /* !CONFIG_FILE */ -static void -signal_handler(int sig_num) -{ +#define MAX_OPTIONS 40 + +static void signal_handler(int sig_num) { #if !defined(_WIN32) - if (sig_num == SIGCHLD) { - do { - } while (waitpid(-1, &sig_num, WNOHANG) > 0); - } else + if (sig_num == SIGCHLD) { + do { + } while (waitpid(-1, &sig_num, WNOHANG) > 0); + } else #endif /* !_WIN32 */ - { - exit_flag = sig_num; - } + { + exit_flag = sig_num; + } } /* * Edit the passwords file. */ -static int -mg_edit_passwords(const char *fname, const char *domain, - const char *user, const char *pass) -{ - struct mg_context *ctx; - struct mg_config config; - int retval; +static int mg_edit_passwords(const char *fname, const char *domain, + const char *user, const char *pass) { + struct mg_context *ctx; + const char *options[] = {"authentication_domain", NULL, NULL}; + int success; - memset(&config, 0, sizeof(config)); - 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); - mg_stop(ctx); + options[1] = domain; + ctx = mg_start(NULL, options); + success = mg_modify_passwords_file(ctx, fname, user, pass); + mg_stop(ctx); - return (retval); + return success; } -#define OFFSET(x) offsetof(struct mg_config, x) +static void show_usage_and_exit(void) { + const char **names; + int i, len; -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} -}; + fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka\n", mg_version()); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " mongoose -A \n"); + fprintf(stderr, " mongoose \n"); + fprintf(stderr, " mongoose [-option value ...]\n"); + fprintf(stderr, "OPTIONS:\n "); -static void -show_usage_and_exit(const struct mg_config *config) -{ - const struct option_descriptor *o; - const char *value; + names = mg_get_valid_option_names(); + len = 2; + for (i = 0; names[i] != NULL; i++) { + len += strlen(names[i]) + 1; + if (len >= 80) { + len = strlen(names[i]) + 3; + fprintf(stderr, "%s", "\n "); + } + fprintf(stderr, "%s%c", names[i], names[i + 1] == NULL ? '\n' : ','); + } + fprintf(stderr, "See http://code.google.com/p/mongoose/wiki/MongooseManual" + " for more details.\n"); + fprintf(stderr, "Example:\n mongoose -listening_ports 80,443s " + "-enable_directory_listing no\n"); - (void) fprintf(stderr, - "Mongoose version %s (c) Sergey Lyubka\n" - "usage: mongoose [options] [config_file]\n", mg_version()); - - fprintf(stderr, " -A \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); + exit(EXIT_FAILURE); } -static void -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 char *sdup(const char *str) { + char *p; + if ((p = malloc(strlen(str) + 1)) != NULL) { + strcpy(p, str); + } + return p; } -static void -process_command_line_arguments(struct mg_config *config, char *argv[]) -{ - const char *config_file = CONFIG_FILE; - char line[512], opt[512], *vals[100], - val[512], path[FILENAME_MAX], *p; - FILE *fp; - size_t i, line_no = 0; +static void set_option(char **options, const char *name, const char *value) { + int i; - /* First find out, which config file to open */ - for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2) - if (argv[i + 1] == NULL) - show_usage_and_exit(config); + for (i = 0; i < MAX_OPTIONS - 3; i++) { + if (options[i] == NULL) { + options[i] = sdup(name); + options[i + 1] = sdup(value); + options[i + 2] = NULL; + break; + } + } - if (argv[i] != NULL && argv[i + 1] != NULL) { - /* More than one non-option arguments are given */ - show_usage_and_exit(config); - } else if (argv[i] != NULL) { - /* Just one non-option argument is given, this is config file */ - config_file = argv[i]; - } else { - /* No config file specified. Look for one where binary lives */ - if ((p = strrchr(argv[0], DIRSEP)) != 0) { - (void) snprintf(path, sizeof(path), "%.*s%s", - (int) (p - argv[0]) + 1, argv[0], config_file); - config_file = path; - } - } - - fp = fopen(config_file, "r"); - - /* If config file was set in command line and open failed, exit */ - if (fp == NULL && argv[i] != NULL) { - (void) fprintf(stderr, "cannot open config file %s: %s\n", - config_file, strerror(errno)); - exit(EXIT_FAILURE); - } - - /* Reset temporary value holders */ - (void) memset(vals, 0, sizeof(vals)); - - if (fp != NULL) { - (void) printf("Loading config file %s, " - "ignoring command line arguments\n", config_file); - - /* Loop over the lines in config file */ - while (fgets(line, sizeof(line), fp) != NULL) { - - line_no++; - - /* Ignore empty lines and comments */ - if (line[0] == '#' || line[0] == '\n') - continue; - - if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) { - fprintf(stderr, "%s: line %d is invalid\n", - config_file, (int) line_no); - exit(EXIT_FAILURE); - } - /* TODO(lsm): free this at some point */ - p = malloc(strlen(val) + 1); - (void) strcpy(p, val); - set_option(config, opt, p); - } - - (void) fclose(fp); - } else { - for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2) - set_option(config, &argv[i][1], argv[i + 1]); - } + if (i == MAX_OPTIONS - 3) { + fprintf(stderr, "%s\n", "Too many options specified"); + exit(EXIT_FAILURE); + } } -int -main(int argc, char *argv[]) -{ - struct mg_config config; - struct mg_context *ctx; +static void process_command_line_arguments(char *argv[], char **options) { + const char *config_file = CONFIG_FILE; + char line[512], opt[512], *vals[100], val[512], path[FILENAME_MAX], *p; + FILE *fp; + size_t i, line_no = 0; - /* 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"; + /* First find out, which config file to open */ + for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2) + if (argv[i + 1] == NULL) + show_usage_and_exit(); - /* Edit passwords file if -A option is specified */ - if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') { - if (argc != 6) - show_usage_and_exit(&config); - exit(mg_edit_passwords(argv[2], argv[3], argv[4], argv[5]) == - MG_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE); - } + if (argv[i] != NULL && argv[i + 1] != NULL) { + /* More than one non-option arguments are given */ + show_usage_and_exit(); + } else if (argv[i] != NULL) { + /* Just one non-option argument is given, this is config file */ + config_file = argv[i]; + } else { + /* No config file specified. Look for one where binary lives */ + if ((p = strrchr(argv[0], DIRSEP)) != 0) { + snprintf(path, sizeof(path), "%.*s%s", + (int) (p - argv[0]) + 1, argv[0], config_file); + config_file = path; + } + } - /* Show usage if -h or --help options are specified */ - if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) - show_usage_and_exit(&config); + fp = fopen(config_file, "r"); - /* Update config based on command line arguments */ - process_command_line_arguments(&config, argv); + /* If config file was set in command line and open failed, exit */ + if (fp == NULL && argv[i] != NULL) { + fprintf(stderr, "cannot open config file %s: %s\n", + config_file, strerror(errno)); + exit(EXIT_FAILURE); + } - /* Setup signal handler: quit on Ctrl-C */ + /* Reset temporary value holders */ + (void) memset(vals, 0, sizeof(vals)); + + if (fp != NULL) { + printf("Loading config file %s, ignoring command line arguments\n", + config_file); + + /* Loop over the lines in config file */ + while (fgets(line, sizeof(line), fp) != NULL) { + + line_no++; + + /* Ignore empty lines and comments */ + if (line[0] == '#' || line[0] == '\n') + continue; + + if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) { + fprintf(stderr, "%s: line %d is invalid\n", + config_file, (int) line_no); + exit(EXIT_FAILURE); + } + set_option(options, opt, val); + } + + (void) fclose(fp); + } else { + for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2) + set_option(options, &argv[i][1], argv[i + 1]); + } +} + +int main(int argc, char *argv[]) { + struct mg_context *ctx; + char *options[MAX_OPTIONS]; + int i; + + /* Edit passwords file if -A option is specified */ + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') { + if (argc != 6) { + show_usage_and_exit(); + } + exit(mg_edit_passwords(argv[2], argv[3], argv[4], argv[5]) ? + EXIT_SUCCESS : EXIT_FAILURE); + } + + /* Show usage if -h or --help options are specified */ + if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { + show_usage_and_exit(); + } + + /* Update config based on command line arguments */ + options[0] = NULL; + process_command_line_arguments(argv, options); + + /* Setup signal handler: quit on Ctrl-C */ #ifndef _WIN32 - (void) signal(SIGCHLD, signal_handler); + signal(SIGCHLD, signal_handler); #endif /* _WIN32 */ - (void) signal(SIGTERM, signal_handler); - (void) signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); - /* Start Mongoose */ - if ((ctx = mg_start(&config)) == NULL) { - (void) printf("%s\n", "Cannot initialize Mongoose context"); - exit(EXIT_FAILURE); - } + /* Start Mongoose */ + ctx = mg_start(NULL, (const char **) options); + for (i = 0; options[i] != NULL; i++) { + free(options[i]); + } - (void) printf("Mongoose %s started on port(s) %s " - "with web root [%s]\n", - mg_version(), config.listening_ports, config.document_root); + if (ctx == NULL) { + (void) printf("%s\n", "Cannot initialize Mongoose context"); + exit(EXIT_FAILURE); + } - fflush(stdout); - while (exit_flag == 0) - sleep(1); + printf("Mongoose %s started on port(s) %s with web root [%s]\n", + mg_version(), mg_get_option(ctx, "listening_ports"), + mg_get_option(ctx, "document_root")); - (void) printf("Exiting on signal %d, " - "waiting for all threads to finish...", exit_flag); - fflush(stdout); - mg_stop(ctx); - (void) printf("%s", " done.\n"); + fflush(stdout); + while (exit_flag == 0) { + sleep(1); + } - return (EXIT_SUCCESS); + printf("Exiting on signal %d, waiting for all threads to finish...", + exit_flag); + fflush(stdout); + mg_stop(ctx); + printf("%s", " done.\n"); + + return EXIT_SUCCESS; } diff --git a/mongoose.c b/mongoose.c index 2952f49f..c501c656 100644 --- a/mongoose.c +++ b/mongoose.c @@ -222,7 +222,6 @@ typedef int SOCKET; typedef int socklen_t; #endif // NO_SOCKLEN_T -typedef enum {MG_FALSE, MG_TRUE} bool_t; typedef void * (*mg_thread_func_t)(void *); static const char *http_500_error = "Internal Server Error"; @@ -333,9 +332,9 @@ struct vec { // Structure used by mg_stat() function. Uses 64 bit file length. struct mgstat { - bool_t is_directory; // Directory marker - int64_t size; // File size - time_t mtime; // Modification time + int is_directory; // Directory marker + int64_t size; // File size + time_t mtime; // Modification time }; // Describes listening socket, or socket which was accept()-ed by the master @@ -345,19 +344,39 @@ struct socket { SOCKET sock; // Listening socket struct usa lsa; // Local socket address struct usa rsa; // Remote socket address - bool_t is_ssl; // Is socket SSL-ed + int is_ssl; // Is socket SSL-ed +}; + +enum { + DOCUMENT_ROOT, LISTENING_PORTS, INDEX_FILES, SSL_CERTIFICATE, CGI_EXTENSIONS, + CGI_INTERPRETER, CGI_ENVIRONMENT, SSI_EXTENSIONS, AUTHENTICATION_DOMAIN, + URI_PROTECTION, GLOBAL_PASSWORDS_FILE, PUT_DELETE_PASSWORDS_FILE, + ACCESS_LOG_FILE, ERROR_LOG_FILE, ACCESS_CONTROL_LIST, RUN_AS_USER, + EXTRA_MIME_TYPES, ENABLE_DIRECTORY_LISTING, ENABLE_KEEP_ALIVE, NUM_THREADS, + NUM_OPTIONS +}; + +static const char *option_names[] = { + "document_root", "listening_ports", "index_files", "ssl_certificate", + "cgi_extensions", "cgi_interpreter", "cgi_environment", "ssi_extensions", + "authentication_domain", "protect_uri", "global_passwords_file", + "put_delete_passwords_file", "access_log_file", "error_log_file", + "access_control_list", "run_as_user", "extra_mime_types", + "enable_directory_listing", "enable_keep_alive", "num_threads", + NULL }; struct mg_context { - int stop_flag; // Should we stop event loop - SSL_CTX *ssl_ctx; // SSL context - const struct mg_config *config; // Mongoose configuration + int stop_flag; // Should we stop event loop + SSL_CTX *ssl_ctx; // SSL context + char *config[NUM_OPTIONS]; // Mongoose configuration parameters + mg_callback_t user_callback; // User-defined callback function struct socket *listening_sockets; - int num_threads; // Number of threads - pthread_mutex_t mutex; // Protects (max|num)_threads - pthread_cond_t cond; // Condvar for tracking workers terminations + int num_threads; // Number of threads + pthread_mutex_t mutex; // Protects (max|num)_threads + pthread_cond_t cond; // Condvar for tracking workers terminations struct socket queue[20]; // Accepted sockets int sq_head; // Head of the socket queue @@ -380,11 +399,39 @@ struct mg_connection { int data_len; // Total size of data in a buffer }; +const char **mg_get_valid_option_names(void) { + return option_names; +} + +static void *call_user(struct mg_connection *conn, enum mg_event event) { + return conn->ctx->user_callback == NULL ? NULL : + conn->ctx->user_callback(event, conn, &conn->request_info); +} + +static int get_option_index(const char *name) { + int i; + for (i = 0; option_names[i] != NULL; i++) { + if (strcmp(option_names[i], name) == 0) { + return i; + } + } + return -1; +} + +const char *mg_get_option(const struct mg_context *ctx, const char *name) { + int i; + if ((i = get_option_index(name)) == -1) { + return NULL; + } else if (ctx->config[i] == NULL) { + return ""; + } else { + return ctx->config[i]; + } +} + // Print error message to the opened error log stream. static void cry(struct mg_connection *conn, const char *fmt, ...) { char buf[BUFSIZ]; - mg_callback_t log_callback; - enum mg_error_t processed = MG_ERROR; va_list ap; FILE *fp; time_t timestamp; @@ -396,14 +443,10 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) { // Do not lock when getting the callback value, here and below. // I suppose this is fine, since function cannot disappear in the // same way string option can. - log_callback = conn->ctx->config->event_log_handler; conn->request_info.log_message = buf; - if (log_callback != NULL) { - processed = log_callback(conn, &conn->request_info); - } - if (processed == MG_ERROR) { - fp = conn->ctx->config->error_log_file == NULL ? stderr : - mg_fopen(conn->ctx->config->error_log_file, "a+"); + if (call_user(conn, MG_EVENT_LOG) == NULL) { + fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? stderr : + mg_fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); if (fp != NULL) { flockfile(fp); @@ -611,7 +654,7 @@ static const char *next_option(const char *list, struct vec *val, } #if !defined(NO_CGI) -static bool_t match_extension(const char *path, const char *ext_list) { +static int match_extension(const char *path, const char *ext_list) { struct vec ext_vec; size_t path_len; @@ -621,9 +664,9 @@ static bool_t match_extension(const char *path, const char *ext_list) { if (ext_vec.len < path_len && mg_strncasecmp(path + path_len - ext_vec.len, ext_vec.ptr, ext_vec.len) == 0) - return MG_TRUE; + return 1; - return MG_FALSE; + return 0; } #endif // !NO_CGI @@ -632,17 +675,11 @@ static void send_http_error(struct mg_connection *conn, int status, char buf[BUFSIZ]; va_list ap; int len; - mg_callback_t error_handler; - bool_t handled; DEBUG_TRACE(("%d %s", status, reason)); conn->request_info.status_code = status; - error_handler = conn->ctx->config->http_error_handler; - handled = error_handler ? - error_handler(conn, &conn->request_info) : MG_ERROR; - - if (handled == MG_ERROR) { + if (call_user(conn, MG_HTTP_ERROR) == NULL) { buf[0] = '\0'; len = 0; @@ -668,7 +705,7 @@ static void send_http_error(struct mg_connection *conn, int status, #ifdef _WIN32 static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) { unused = NULL; - *mutex = CreateMutex(NULL, MG_FALSE, NULL); + *mutex = CreateMutex(NULL, FALSE, NULL); return *mutex == NULL ? -1 : 0; } @@ -686,7 +723,7 @@ static int pthread_mutex_unlock(pthread_mutex_t *mutex) { static int pthread_cond_init(pthread_cond_t *cv, const void *unused) { unused = NULL; - *cv = CreateEvent(NULL, MG_FALSE, MG_FALSE, NULL); + *cv = CreateEvent(NULL, FALSE, FALSE, NULL); return *cv == NULL ? -1 : 0; } @@ -1021,9 +1058,9 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, me = GetCurrentProcess(); (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me, - &si.hStdInput, 0, MG_TRUE, DUPLICATE_SAME_ACCESS); + &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS); (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdout), me, - &si.hStdOutput, 0, MG_TRUE, DUPLICATE_SAME_ACCESS); + &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); // If CGI file is a script, try to read the interpreter line interp = conn->ctx->config->cgi_interpreter; @@ -1056,7 +1093,7 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, change_slashes_to_backslashes(line); DEBUG_TRACE(("Running [%s]", cmdline)); - if (CreateProcessA(NULL, cmdline, NULL, NULL, MG_TRUE, + if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) { cry(conn, "%s: CreateProcess(%s): %d", __func__, cmdline, ERRNO); @@ -1139,12 +1176,12 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, } else if (dup2(fd_stdout, 1) == -1) { cry(conn, "%s: dup2(%d, 1): %s", __func__, fd_stdout, strerror(ERRNO)); } else { - (void) dup2(fd_stdout, 2); + //(void) dup2(fd_stdout, 2); (void) close(fd_stdin); (void) close(fd_stdout); // Execute CGI program. No need to lock: new process - interp = conn->ctx->config->cgi_interpreter; + interp = conn->ctx->config[CGI_INTERPRETER]; if (interp == NULL) { (void) execle(prog, prog, NULL, envp); cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO)); @@ -1266,6 +1303,7 @@ int mg_read(struct mg_connection *conn, void *buf, size_t len) { if (n <= 0) { break; } + buf = (char *) buf + n; conn->consumed_content += n; nread += n; len -= n; @@ -1297,7 +1335,7 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) { // uses '+' as character for space, see RFC 1866 section 8.2.1 // http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt static size_t url_decode(const char *src, size_t src_len, char *dst, - size_t dst_len, bool_t is_form_url_encoded) { + size_t dst_len, int is_form_url_encoded) { size_t i, j; int a, b; #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') @@ -1326,72 +1364,58 @@ static size_t url_decode(const char *src, size_t src_len, char *dst, // It can be specified in query string, or in the POST data. // Return NULL if the variable not found, or allocated 0-terminated value. // It is caller's responsibility to free the returned value. -enum mg_error_t mg_get_var(const char *buf, size_t buf_len, - const char *var_name, char *var_value, - size_t var_value_len) { +int mg_get_var(const char *buf, size_t buf_len, const char *name, + char *dst, size_t dst_len) { const char *p, *e, *s; - char *val; - size_t var_len, len; - enum mg_error_t ret_val; + size_t name_len, len; - var_len = strlen(var_name); + name_len = strlen(name); e = buf + buf_len; - val = NULL; - ret_val = MG_NOT_FOUND; - var_value[0] = '\0'; + len = -1; + dst[0] = '\0'; // buf is "var1=val1&var2=val2...". Find variable first - for (p = buf; p + var_len < e; p++) - if ((p == buf || p[-1] == '&') && p[var_len] == '=' && - !mg_strncasecmp(var_name, p, var_len)) { + for (p = buf; p != NULL && p + name_len < e; p++) { + if ((p == buf || p[-1] == '&') && p[name_len] == '=' && + !mg_strncasecmp(name, p, name_len)) { // Point p to variable value - p += var_len + 1; + p += name_len + 1; // Point s to the end of the value s = (const char *) memchr(p, '&', e - p); if (s == NULL) { s = e; } + assert(s >= p); - /* Try to allocate the buffer */ - len = s - p; - if (len >= var_value_len) { - ret_val = MG_BUFFER_TOO_SMALL; - } else { - url_decode(p, len, var_value, len + 1, MG_TRUE); - ret_val = MG_SUCCESS; + // Decode variable into destination buffer + if ((size_t) (s - p) < dst_len) { + len = url_decode(p, s - p, dst, dst_len, 1); } break; } + } - return ret_val; + return len; } -enum mg_error_t mg_get_qsvar(const struct mg_request_info *ri, - const char *var_name, char *var_value, - size_t var_value_len) { - return ri->query_string == NULL ? MG_NOT_FOUND : mg_get_var(ri->query_string, - strlen(ri->query_string), var_name, var_value, var_value_len); -} - -enum mg_error_t mg_get_cookie(const struct mg_connection *conn, - const char *cookie_name, char *dst, - size_t dst_size) { +int mg_get_cookie(const struct mg_connection *conn, const char *cookie_name, + char *dst, size_t dst_size) { const char *s, *p, *end; - int len; + int name_len, len = -1; dst[0] = '\0'; if ((s = mg_get_header(conn, "Cookie")) == NULL) { - return MG_NOT_FOUND; + return 0; } - len = strlen(cookie_name); + name_len = strlen(cookie_name); end = s + strlen(s); - for (; (s = strstr(s, cookie_name)) != NULL; s += len) - if (s[len] == '=') { - s += len + 1; + for (; (s = strstr(s, cookie_name)) != NULL; s += name_len) + if (s[name_len] == '=') { + s += name_len + 1; if ((p = strchr(s, ' ')) == NULL) p = end; if (p[-1] == ';') @@ -1400,15 +1424,14 @@ enum mg_error_t mg_get_cookie(const struct mg_connection *conn, s++; p--; } - if ((size_t) (p - s) >= dst_size) { - return MG_BUFFER_TOO_SMALL; - } else { - mg_strlcpy(dst, s, (p - s) + 1); - return MG_SUCCESS; + if ((size_t) (p - s) < dst_size) { + len = (p - s) + 1; + mg_strlcpy(dst, s, len); } + break; } - return MG_NOT_FOUND; + return len; } // Mongoose allows to specify multiple directories to serve, @@ -1422,7 +1445,7 @@ static int get_document_root(const struct mg_connection *conn, uri = conn->request_info.uri; len_of_matched_uri = 0; - root = next_option(conn->ctx->config->document_root, document_root, NULL); + root = next_option(conn->ctx->config[DOCUMENT_ROOT], document_root, NULL); while ((root = next_option(root, &uri_vec, &path_vec)) != NULL) { if (memcmp(uri, uri_vec.ptr, uri_vec.len) == 0) { @@ -1650,7 +1673,7 @@ static void get_mime_type(struct mg_context *ctx, const char *path, // Scan user-defined mime types first, in case user wants to // override default mime types. - list = ctx->config->mime_types; + list = ctx->config[EXTRA_MIME_TYPES]; while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { // ext now points to the path suffix ext = path + path_len - ext_vec.len; @@ -1896,10 +1919,9 @@ void mg_md5(char *buf, ...) { } // Check the user's password, return 1 if OK -static bool_t check_password(const char *method, const char *ha1, - const char *uri, const char *nonce, const char *nc, - const char *cnonce, const char *qop, - const char *response) { +static int check_password(const char *method, const char *ha1, const char *uri, + const char *nonce, const char *nc, const char *cnonce, + const char *qop, const char *response) { char ha2[32 + 1], expected_response[32 + 1]; // NOTE(lsm): due to a bug in MSIE, we do not compare the URI @@ -1908,7 +1930,7 @@ static bool_t check_password(const char *method, const char *ha1, strlen(response) != 32 // || now - strtoul(dig->nonce, NULL, 10) > 3600 ) { - return MG_FALSE; + return 0; } mg_md5(ha2, method, ":", uri, NULL); @@ -1927,12 +1949,12 @@ static FILE *open_auth_file(struct mg_connection *conn, const char *path) { struct mgstat st; FILE *fp; - if (ctx->config->global_passwords_file != NULL) { + if (ctx->config[GLOBAL_PASSWORDS_FILE] != NULL) { // Use global passwords file - fp = mg_fopen(ctx->config->global_passwords_file, "r"); + fp = mg_fopen(ctx->config[GLOBAL_PASSWORDS_FILE], "r"); if (fp == NULL) cry(fc(ctx), "fopen(%s): %s", - ctx->config->global_passwords_file, strerror(ERRNO)); + ctx->config[GLOBAL_PASSWORDS_FILE], strerror(ERRNO)); } else if (!mg_stat(path, &st) && st.is_directory) { (void) mg_snprintf(conn, name, sizeof(name), "%s%c%s", path, DIRSEP, PASSWORDS_FILE_NAME); @@ -1955,14 +1977,14 @@ struct ah { char *user, *uri, *cnonce, *response, *qop, *nc, *nonce; }; -static bool_t parse_auth_header(struct mg_connection *conn, char *buf, - size_t buf_size, struct ah *ah) { +static int parse_auth_header(struct mg_connection *conn, char *buf, + size_t buf_size, struct ah *ah) { char *name, *value, *s; const char *auth_header; if ((auth_header = mg_get_header(conn, "Authorization")) == NULL || mg_strncasecmp(auth_header, "Digest ", 7) != 0) { - return MG_FALSE; + return 0; } // Make modifiable copy of the auth header @@ -2016,16 +2038,16 @@ static bool_t parse_auth_header(struct mg_connection *conn, char *buf, conn->request_info.remote_user = mg_strdup(ah->user); } - return MG_TRUE; + return 1; } // Authorize against the opened passwords file. Return 1 if authorized. -static bool_t authorize(struct mg_connection *conn, FILE *fp) { +static int authorize(struct mg_connection *conn, FILE *fp) { struct ah ah; char line[256], f_user[256], ha1[256], f_domain[256], buf[MAX_REQUEST_SIZE]; if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) { - return MG_FALSE; + return 0; } // Loop over passwords file @@ -2035,29 +2057,28 @@ static bool_t authorize(struct mg_connection *conn, FILE *fp) { } if (!strcmp(ah.user, f_user) && - !strcmp(conn->ctx->config->auth_domain, f_domain)) + !strcmp(conn->ctx->config[AUTHENTICATION_DOMAIN], f_domain)) return check_password( conn->request_info.request_method, ha1, ah.uri, ah.nonce, ah.nc, ah.cnonce, ah.qop, ah.response); } - return MG_FALSE; + return 0; } -// Return MG_TRUE if request is authorised, MG_FALSE otherwise. -static bool_t check_authorization(struct mg_connection *conn, - const char *path) { +// Return 1 if request is authorised, 0 otherwise. +static int check_authorization(struct mg_connection *conn, const char *path) { FILE *fp; char fname[PATH_MAX]; struct vec uri_vec, filename_vec; const char *list; - bool_t authorized; + int authorized; fp = NULL; - authorized = MG_TRUE; + authorized = 1; - list = conn->ctx->config->protect; + list = conn->ctx->config[URI_PROTECTION]; while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) { if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) { (void) mg_snprintf(conn, fname, sizeof(fname), "%.*s", @@ -2087,16 +2108,16 @@ static void send_authorization_request(struct mg_connection *conn) { "HTTP/1.1 401 Unauthorized\r\n" "WWW-Authenticate: Digest qop=\"auth\", " "realm=\"%s\", nonce=\"%lu\"\r\n\r\n", - conn->ctx->config->auth_domain, + conn->ctx->config[AUTHENTICATION_DOMAIN], (unsigned long) time(NULL)); } -static bool_t is_authorized_for_put(struct mg_connection *conn) { +static int is_authorized_for_put(struct mg_connection *conn) { FILE *fp; - int ret = MG_FALSE; + int ret = 0; - fp = conn->ctx->config->put_delete_passwords_file == NULL ? NULL : - mg_fopen(conn->ctx->config->put_delete_passwords_file, "r"); + fp = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL ? NULL : + mg_fopen(conn->ctx->config[PUT_DELETE_PASSWORDS_FILE], "r"); if (fp != NULL) { @@ -2107,9 +2128,8 @@ static bool_t is_authorized_for_put(struct mg_connection *conn) { return ret; } -enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx, - const char *fname, const char *user, - const char *pass) { +int mg_modify_passwords_file(struct mg_context *ctx, const char *fname, + const char *user, const char *pass) { int found; char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX]; const char *domain; @@ -2117,7 +2137,7 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx, found = 0; fp = fp2 = NULL; - domain = ctx->config->auth_domain; + domain = ctx->config[AUTHENTICATION_DOMAIN]; // Regard empty password as no password - remove user record. if (pass[0] == '\0') { @@ -2134,10 +2154,10 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx, // Open the given file and temporary file if ((fp = mg_fopen(fname, "r")) == NULL) { cry(fc(ctx), "Cannot open %s: %s", fname, strerror(errno)); - return MG_ERROR; + return 0; } else if ((fp2 = mg_fopen(tmp, "w+")) == NULL) { cry(fc(ctx), "Cannot open %s: %s", tmp, strerror(errno)); - return MG_ERROR; + return 0; } // Copy the stuff to temporary file @@ -2171,7 +2191,7 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx, (void) mg_remove(fname); (void) mg_rename(tmp, fname); - return MG_SUCCESS; + return 1; } struct de { @@ -2452,7 +2472,7 @@ static void parse_http_headers(char **buf, struct mg_request_info *ri) { } } -static bool_t is_valid_http_method(const char *method) { +static int is_valid_http_method(const char *method) { return !strcmp(method, "GET") || !strcmp(method, "POST") || !strcmp(method, "HEAD") || @@ -2461,8 +2481,8 @@ static bool_t is_valid_http_method(const char *method) { } // Parse HTTP request, fill in mg_request_info structure. -static bool_t parse_http_request(char *buf, struct mg_request_info *ri) { - int status = MG_FALSE; +static int parse_http_request(char *buf, struct mg_request_info *ri) { + int status = 0; ri->request_method = skip(&buf, " "); ri->uri = skip(&buf, " "); @@ -2473,7 +2493,7 @@ static bool_t parse_http_request(char *buf, struct mg_request_info *ri) { strncmp(ri->http_version, "HTTP/", 5) == 0) { ri->http_version += 5; /* Skip "HTTP/" */ parse_http_headers(&buf, ri); - status = MG_TRUE; + status = 1; } return status; @@ -2505,15 +2525,13 @@ static int read_request(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int bufsiz, // For given directory path, substitute it to valid index file. // Return 0 if index file has been found, -1 if not found. // If the file is found, it's stats is returned in stp. -static bool_t substitute_index_file(struct mg_connection *conn, char *path, - size_t path_len, struct mgstat *stp) { - const char *list; +static int substitute_index_file(struct mg_connection *conn, char *path, + size_t path_len, struct mgstat *stp) { + const char *list = conn->ctx->config[INDEX_FILES]; struct mgstat st; struct vec filename_vec; - size_t n; - bool_t found; - - n = strlen(path); + size_t n = strlen(path); + int found = 0; // The 'path' given to us points to the directory. Remove all trailing // directory separator characters from the end of the path, and @@ -2525,9 +2543,6 @@ static bool_t substitute_index_file(struct mg_connection *conn, char *path, // 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 - list = conn->ctx->config->index_files; - found = MG_FALSE; - while ((list = next_option(list, &filename_vec, NULL)) != NULL) { // Ignore too long entries that may overflow path buffer @@ -2541,13 +2556,13 @@ static bool_t substitute_index_file(struct mg_connection *conn, char *path, if (mg_stat(path, &st) == 0) { // Yes it does, break the loop *stp = st; - found = MG_TRUE; + found = 1; break; } } // If no index file exists, restore directory path - if (found == MG_FALSE) { + if (!found) { path[n] = '\0'; } @@ -2555,17 +2570,17 @@ static bool_t substitute_index_file(struct mg_connection *conn, char *path, } // Return True if we should reply 304 Not Modified. -static bool_t is_not_modified(const struct mg_connection *conn, - const struct mgstat *stp) { +static int is_not_modified(const struct mg_connection *conn, + const struct mgstat *stp) { const char *ims = mg_get_header(conn, "If-Modified-Since"); return ims != NULL && stp->mtime <= parse_date_string(ims); } -static bool_t handle_request_body(struct mg_connection *conn, FILE *fp) { +static int handle_request_body(struct mg_connection *conn, FILE *fp) { const char *expect, *data; char buf[BUFSIZ]; int to_read, nread, data_len; - bool_t status = MG_FALSE; + int success = 0; expect = mg_get_header(conn, "Expect"); assert(fp != NULL); @@ -2583,8 +2598,8 @@ static bool_t handle_request_body(struct mg_connection *conn, FILE *fp) { assert(data_len >= 0); if (conn->content_len <= (int64_t) data_len) { - status = fp == NULL || (push(fp, INVALID_SOCKET, NULL, data, - conn->content_len) == conn->content_len) ? MG_TRUE : MG_FALSE; + success = push(fp, INVALID_SOCKET, NULL, data, + conn->content_len) == conn->content_len; } else { push(fp, INVALID_SOCKET, NULL, data, (int64_t) data_len); conn->consumed_content += data_len; @@ -2601,18 +2616,17 @@ static bool_t handle_request_body(struct mg_connection *conn, FILE *fp) { conn->consumed_content += nread; } if (conn->consumed_content == conn->content_len) { - status = MG_TRUE; + success = 1; } } // Each error code path in this function must send an error - if (status != MG_TRUE) { - send_http_error(conn, 577, http_500_error, - "%s", "Error handling body data"); + if (!success) { + send_http_error(conn, 577, http_500_error, ""); } } - return status; + return success; } #if !defined(NO_CGI) @@ -2681,7 +2695,7 @@ static void prepare_cgi_environment(struct mg_connection *conn, get_document_root(conn, &root); - addenv(blk, "SERVER_NAME=%s", conn->ctx->config->auth_domain); + addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]); addenv(blk, "SERVER_ROOT=%.*s", root.len, root.ptr); addenv(blk, "DOCUMENT_ROOT=%.*s", root.len, root.ptr); @@ -2750,7 +2764,7 @@ static void prepare_cgi_environment(struct mg_connection *conn, } // Add user-specified variables - s = conn->ctx->config->cgi_environment; + s = conn->ctx->config[CGI_ENVIRONMENT]; while ((s = next_option(s, &var_vec, NULL)) != NULL) { addenv(blk, "%.*s", var_vec.len, var_vec.ptr); } @@ -2942,7 +2956,7 @@ static void do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag, int include_level) { char file_name[BUFSIZ], path[PATH_MAX], *p; struct vec root; - bool_t is_ssi; + int is_ssi; FILE *fp; get_document_root(conn, &root); @@ -2975,7 +2989,7 @@ static void do_ssi_include(struct mg_connection *conn, const char *ssi, tag, path, strerror(ERRNO)); } else { set_close_on_exec(fileno(fp)); - is_ssi = match_extension(path, conn->ctx->config->ssi_extensions); + is_ssi = match_extension(path, conn->ctx->config[SSI_EXTENSIONS]); if (is_ssi) { send_ssi_file(conn, path, fp, include_level + 1); } else { @@ -3009,12 +3023,12 @@ static void send_ssi_file(struct mg_connection *conn, const char *path, return; } - in_ssi_tag = MG_FALSE; + in_ssi_tag = 0; len = 0; while ((ch = fgetc(fp)) != EOF) { if (in_ssi_tag && ch == '>') { - in_ssi_tag = MG_FALSE; + in_ssi_tag = 0; buf[len++] = (char) ch; buf[len] = '\0'; assert(len <= (int) sizeof(buf)); @@ -3034,14 +3048,14 @@ static void send_ssi_file(struct mg_connection *conn, const char *path, } else if (in_ssi_tag) { if (len == 5 && memcmp(buf, "