ref: refs/heads/master
commit 09cef514dc511e6d6ca5e11e6e6d2a4691fb298e
Author: Duncan Mac-Vicar P
Date: Thu Feb 5 15:59:29 2009 +0100
Forgot to include mongoose (new shttpd name)
---
vendor/mongoose/CMakeLists.txt | 9 +
vendor/mongoose/mongoose.c | 3758 ++++++++++++++++++++++++++++++++++++++++
vendor/mongoose/mongoose.h | 139 ++
vendor/shttpd/CMakeLists.txt | 29 -
vendor/shttpd/auth.c | 419 -----
vendor/shttpd/cgi.c | 229 ---
vendor/shttpd/compat_rtems.c | 198 ---
vendor/shttpd/compat_rtems.h | 60 -
vendor/shttpd/compat_unix.c | 128 --
vendor/shttpd/compat_unix.h | 35 -
vendor/shttpd/compat_win32.c | 687 --------
vendor/shttpd/compat_win32.h | 83 -
vendor/shttpd/compat_wince.c | 1593 -----------------
vendor/shttpd/compat_wince.h | 145 --
vendor/shttpd/config.h | 30 -
vendor/shttpd/defs.h | 395 -----
vendor/shttpd/io.h | 97 -
vendor/shttpd/io_cgi.c | 127 --
vendor/shttpd/io_dir.c | 153 --
vendor/shttpd/io_emb.c | 291 ----
vendor/shttpd/io_file.c | 157 --
vendor/shttpd/io_socket.c | 39 -
vendor/shttpd/io_ssi.c | 488 ------
vendor/shttpd/io_ssl.c | 85 -
vendor/shttpd/llist.h | 59 -
vendor/shttpd/log.c | 93 -
vendor/shttpd/md5.c | 249 ---
vendor/shttpd/md5.h | 24 -
vendor/shttpd/shttpd.c | 1903 --------------------
vendor/shttpd/shttpd.h | 111 --
vendor/shttpd/ssl.h | 52 -
vendor/shttpd/standalone.c | 72 -
vendor/shttpd/std_includes.h | 40 -
vendor/shttpd/string.c | 95 -
34 files changed, 3906 insertions(+), 8166 deletions(-)
diff --git a/vendor/mongoose/CMakeLists.txt b/vendor/mongoose/CMakeLists.txt
new file mode 100644
index 0000000..ee7268f
--- /dev/null
+++ b/vendor/mongoose/CMakeLists.txt
@@ -0,0 +1,9 @@
+ADD_DEFINITIONS( -DNO_CGI -D_POSIX_SOURCE -D_BSD_SOURCE )
+SET(mongoose_SOURCES
+ mongoose.c
+ mongoose.h
+)
+
+ADD_LIBRARY(mongoose ${mongoose_SOURCES})
+TARGET_LINK_LIBRARIES(mongoose dl pthread)
+
diff --git a/vendor/mongoose/mongoose.c b/vendor/mongoose/mongoose.c
new file mode 100644
index 0000000..8e291c4
--- /dev/null
+++ b/vendor/mongoose/mongoose.c
@@ -0,0 +1,3758 @@
+/*
+ * Copyright (c) 2004-2009 Sergey Lyubka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * $Id$
+ */
+
+#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
+#include
+#include
+#include
+#include
+#include
+#include
+#endif /* _WIN32_WCE */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(_WIN32) /* Windows specific */
+#include
+
+#ifndef _WIN32_WCE
+#include
+#include
+#include
+#else /* _WIN32_WCE */
+/* Windows CE-specific definitions */
+#define NO_CGI /* WinCE has no pipes */
+#define NO_GUI /* temporarily until it is fixed */
+/* WinCE has both Unicode and ANSI versions of GetProcAddress */
+#undef GetProcAddress
+#define GetProcAddress GetProcAddressA
+#endif /* _WIN32_WCE */
+
+/*
+ * Do not allow holes in data structures!
+ * This is needed so when Mongoose DLL is loaded, other languages that
+ * describe struct mg_request_info from mongoose.h, agree with C code.
+ */
+#pragma pack(1)
+
+#define __func__ __FUNCTION__
+#define ERRNO GetLastError()
+#define NO_SOCKLEN_T
+#define SSL_LIB "ssleay32.dll"
+#define DIRSEP '\\'
+#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')
+#define O_NONBLOCK 0
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define dlopen(x,y) LoadLibrary(x)
+#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
+#define _POSIX_
+
+#if !defined(R_OK)
+#define R_OK 04 /* for _access() */
+#endif /* !R_OK MINGW #defines R_OK */
+
+#define SHUT_WR 1
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define popen(x, y) _popen(x, y)
+#define pclose(x) _pclose(x)
+#define access(x, y) _access(x, y)
+#define getcwd(x, y) _getcwd(x, y)
+
+#ifdef HAVE_STRTOUI64
+#define strtoull(x, y, z) _strtoui64(x, y, z)
+#else
+#define strtoull(x, y, z) strtoul(x, y, z)
+#endif /* HAVE_STRTOUI64 */
+
+#define write(x, y, z) _write(x, y, (unsigned) z)
+#define read(x, y, z) _read(x, y, (unsigned) z)
+#define open(x, y, z) _open(x, y, z)
+#define lseek(x, y, z) _lseek(x, y, z)
+#define close(x) _close(x)
+
+#if !defined(fileno)
+#define fileno(x) _fileno(x)
+#endif /* !fileno MINGW #defines fileno */
+
+typedef HANDLE pthread_mutex_t;
+
+#if !defined(S_ISDIR)
+#define S_ISDIR(x) ((x) & _S_IFDIR)
+#endif /* S_ISDIR */
+
+#if defined(HAVE_STDINT)
+#include
+#else
+typedef unsigned int uint32_t;
+typedef unsigned short uint16_t;
+typedef unsigned __int64 uint64_t;
+#endif /* HAVE_STDINT */
+
+/*
+ * POSIX dirent interface
+ */
+struct dirent {
+ char d_name[FILENAME_MAX];
+};
+
+typedef struct DIR {
+ HANDLE handle;
+ WIN32_FIND_DATAW info;
+ struct dirent result;
+} DIR;
+
+#else /* UNIX specific */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#define SSL_LIB "libssl.so"
+#define DIRSEP '/'
+#define IS_DIRSEP_CHAR(c) ((c) == '/')
+#define O_BINARY 0
+#define closesocket(a) close(a)
+#define mg_mkdir(x, y) mkdir(x, y)
+#define mg_open(x, y, z) open(x, y, z)
+#define mg_remove(x) remove(x)
+#define mg_stat(x, y) stat(x, y)
+#define ERRNO errno
+#define INVALID_SOCKET (-1)
+typedef int SOCKET;
+
+#endif /* End of Windows and UNIX specific includes */
+
+#include "mongoose.h"
+
+#define MONGOOSE_VERSION "2.4"
+#define PASSWORDS_FILE_NAME ".htpasswd"
+#define CGI_ENVIRONMENT_SIZE 4096
+#define MAX_CGI_ENVIR_VARS 64
+#define MAX_REQUEST_SIZE 16384
+#define MAX_LISTENING_SOCKETS 10
+#define MAX_CALLBACKS 20
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+#define UNKNOWN_CONTENT_LENGTH ((uint64_t) ~0ULL)
+
+/*
+ * Darwin prior to 7.0 and Win32 do not have socklen_t
+ */
+#ifdef NO_SOCKLEN_T
+typedef int socklen_t;
+#endif /* NO_SOCKLEN_T */
+
+#if !defined(FALSE)
+enum {FALSE, TRUE};
+#endif /* !FALSE */
+
+typedef int bool_t;
+typedef void * (*mg_thread_func_t)(void *);
+static void cry(const char *, ...);
+
+static int tz_offset;
+static const char *http_500_error = "Internal Server Error";
+static FILE *error_log;
+
+/*
+ * Snatched from OpenSSL includes. I put the prototypes here to be independent
+ * from the OpenSSL source installation. Having this, mongoose + SSL can be
+ * built on any system with binary SSL libraries installed.
+ */
+typedef struct ssl_st SSL;
+typedef struct ssl_method_st SSL_METHOD;
+typedef struct ssl_ctx_st SSL_CTX;
+
+#define SSL_ERROR_WANT_READ 2
+#define SSL_ERROR_WANT_WRITE 3
+#define SSL_FILETYPE_PEM 1
+
+/*
+ * Dynamically loaded SSL functionality
+ */
+struct ssl_func {
+ const char *name; /* SSL function name */
+ void (*ptr)(void); /* Function pointer */
+};
+
+#define FUNC(x) ssl_sw[x].ptr
+
+#define SSL_free(x) (* (void (*)(SSL *)) FUNC(0))(x)
+#define SSL_accept(x) (* (int (*)(SSL *)) FUNC(1))(x)
+#define SSL_connect(x) (* (int (*)(SSL *)) FUNC(2))(x)
+#define SSL_read(x,y,z) (* (int (*)(SSL *, void *, int)) FUNC(3))((x),(y),(z))
+#define SSL_write(x,y,z) \
+ (* (int (*)(SSL *, const void *,int)) FUNC(4))((x), (y), (z))
+#define SSL_get_error(x,y)(* (int (*)(SSL *, int)) FUNC(5))((x), (y))
+#define SSL_set_fd(x,y) (* (int (*)(SSL *, SOCKET)) FUNC(6))((x), (y))
+#define SSL_new(x) (* (SSL * (*)(SSL_CTX *)) FUNC(7))(x)
+#define SSL_CTX_new(x) (* (SSL_CTX * (*)(SSL_METHOD *)) FUNC(8))(x)
+#define SSLv23_server_method() (* (SSL_METHOD * (*)(void)) FUNC(9))()
+#define SSL_library_init() (* (int (*)(void)) FUNC(10))()
+#define SSL_CTX_use_PrivateKey_file(x,y,z) (* (int (*)(SSL_CTX *, \
+ const char *, int)) FUNC(11))((x), (y), (z))
+#define SSL_CTX_use_certificate_file(x,y,z) (* (int (*)(SSL_CTX *, \
+ const char *, int)) FUNC(12))((x), (y), (z))
+#define SSL_CTX_set_default_passwd_cb(x,y) \
+ (* (void (*)(SSL_CTX *, mg_spcb_t))FUNC(13))((x),(y))
+
+static struct ssl_func ssl_sw[] = {
+ {"SSL_free", NULL},
+ {"SSL_accept", NULL},
+ {"SSL_connect", NULL},
+ {"SSL_read", NULL},
+ {"SSL_write", NULL},
+ {"SSL_get_error", NULL},
+ {"SSL_set_fd", NULL},
+ {"SSL_new", NULL},
+ {"SSL_CTX_new", NULL},
+ {"SSLv23_server_method", NULL},
+ {"SSL_library_init", NULL},
+ {"SSL_CTX_use_PrivateKey_file", NULL},
+ {"SSL_CTX_use_certificate_file",NULL},
+ {"SSL_CTX_set_default_passwd_cb",NULL},
+ {NULL, NULL}
+};
+
+struct usa {
+ socklen_t len;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ } u;
+};
+
+/*
+ * 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_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_THREADS,
+ NUM_OPTIONS
+};
+
+struct socket {
+ SOCKET sock; /* Listening socket */
+ int is_ssl; /* Should be SSL-ed */
+ struct usa usa; /* Socket address */
+};
+
+/*
+ * Callback function, and where it is bound to
+ */
+struct callback {
+ char *uri_regex; /* URI regex to handle */
+ mg_callback_t func; /* user callback */
+ bool_t is_auth; /* func is auth checker */
+ int status_code; /* error code to handle */
+ void *user_data; /* opaque user data */
+};
+
+/*
+ * Socket pool.
+ * Master thread to enqueue accepted sockets by means of put_socket() function.
+ * Worker threads grab sockets from the queue using get_socket() function.
+ */
+struct socket_pool {
+ struct socket sockets[20]; /* Array of sockets to process */
+
+ int size; /* Ringbuffer pointers */
+ int head;
+ int tail;
+
+ pthread_mutex_t mutex;
+ pthread_cond_t put_cond;
+ pthread_cond_t get_cond;
+};
+
+/*
+ * Mongoose context
+ */
+struct mg_context {
+ int stop_flag; /* Should we stop event loop */
+ SSL_CTX *ssl_ctx; /* SSL context */
+
+ FILE *access_log; /* Opened access log */
+ FILE *error_log; /* Opened error log */
+
+ struct socket listeners[MAX_LISTENING_SOCKETS];
+ int num_listeners;
+
+ struct callback callbacks[MAX_CALLBACKS];
+ int num_callbacks;
+
+ char *options[NUM_OPTIONS]; /* Configured opions */
+ pthread_mutex_t mutex; /* Option setter/getter guard */
+ struct socket_pool socket_pool; /* Socket pool */
+
+ mg_spcb_t ssl_password_callback;
+};
+
+struct mg_connection {
+ struct mg_request_info request_info;
+ struct mg_context *ctx; /* Mongoose context we belong to*/
+ SSL *ssl; /* SSL descriptor */
+ SOCKET sock; /* Connected socket */
+ struct usa rsa; /* Remote socket address */
+ struct usa lsa; /* Local socket address */
+ time_t birth_time; /* Time connection was accepted */
+ bool_t free_post_data; /* post_data was malloc-ed */
+ bool_t keep_alive; /* Keep-Alive flag */
+ uint64_t num_bytes_sent; /* Total bytes sent to client */
+};
+
+/*
+ * In Mongoose, list of values are represented as comma separated
+ * string. For example, list of CGI extensions can be represented as
+ * ".cgi,.php,.pl", FOR_EACH_WORD_IN_LIST macro allows to
+ * loop through the individual values in that list.
+ *
+ * A "const char *" and "int" variables must be passed to the macro.
+ *
+ * In every iteration of the loop, "s" points to the current value, and
+ * "len" specifies its length. Code inside loop must not change "s" and "len".
+ */
+#define FOR_EACH_WORD_IN_LIST(s, len) \
+ for (; s != NULL && (len = strcspn(s, ",")) != 0; \
+ s += len, s+= strspn(s, ","))
+
+/*
+ * Print error message to the opened error log stream.
+ */
+static void
+cry(const char *fmt, ...)
+{
+ FILE *fp;
+ va_list ap;
+
+ fp = error_log == NULL ? stderr : error_log;
+ va_start(ap, fmt);
+ (void) vfprintf(fp, fmt, ap);
+ va_end(ap);
+
+ fputc('\n', fp);
+}
+
+const char *
+mg_version(void)
+{
+ return (MONGOOSE_VERSION);
+}
+
+static void
+mg_strlcpy(register char *dst, register const char *src, size_t n)
+{
+ for (; *src != '\0' && n > 1; n--)
+ *dst++ = *src++;
+ *dst = '\0';
+}
+
+static int
+mg_strncasecmp(const char *str1, const char *str2, size_t len)
+{
+ const unsigned char *s1, *s2, *end;
+
+ s1 = (unsigned char *) str1;
+ s2 = (unsigned char *) str2;
+ end = s1 + len - 1;
+
+ while (s1 < end && *s1 && *s2 && tolower(*s1) == tolower(*s2)) {
+ s1++;
+ s2++;
+ }
+
+ return (tolower(*s1) - tolower(*s2));
+}
+
+static int
+mg_strcasecmp(const char *str1, const char *str2)
+{
+ size_t len = strlen(str1);
+ int res = mg_strncasecmp(str1, str2, len);
+
+ if (res != 0)
+ return (res);
+ else
+ return (-str2[len]); /* If str2[len] == 0, -0 == 0. */
+}
+
+static char *
+mg_strndup(const char *ptr, size_t len)
+{
+ char *p;
+
+ if ((p = (char *) malloc(len + 1)) != NULL)
+ mg_strlcpy(p, ptr, len + 1);
+
+ return (p);
+
+}
+
+static char *
+mg_strdup(const char *str)
+{
+ return (mg_strndup(str, strlen(str)));
+}
+
+/*
+ * Like snprintf(), but never returns negative value, or the value
+ * that is larger than a supplied buffer.
+ * Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
+ * in his audit report.
+ */
+static int
+mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap)
+{
+ int n;
+
+ if (buflen == 0)
+ return (0);
+
+ n = vsnprintf(buf, buflen, fmt, ap);
+
+ if (n < 0) {
+ cry("vsnprintf error");
+ n = 0;
+ } else if (n >= (int) buflen) {
+ cry("truncating vsnprintf buffer");
+ n = (int) buflen - 1;
+ }
+ buf[n] = '\0';
+
+ return (n);
+}
+
+static int
+mg_snprintf(char *buf, size_t buflen, const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = mg_vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+
+ return (n);
+}
+
+static int
+get_pool_space(const struct socket_pool *pool)
+{
+ return (pool->size - (pool->head - pool->tail));
+}
+
+static void
+init_socket_pool(struct socket_pool *pool)
+{
+ pool->size = (int) ARRAY_SIZE(pool->sockets) - 1;
+ pool->head = pool->tail = 0;
+
+ (void) pthread_mutex_init(&pool->mutex, NULL);
+ (void) pthread_cond_init(&pool->put_cond, NULL);
+ (void) pthread_cond_init(&pool->get_cond, NULL);
+}
+
+static void
+destroy_socket_pool(struct socket_pool *pool)
+{
+ int i;
+
+ (void) pthread_mutex_destroy(&pool->mutex);
+ (void) pthread_cond_destroy(&pool->put_cond);
+ (void) pthread_cond_destroy(&pool->get_cond);
+
+ /* TODO: close sockets */
+ for (i = 0; i < get_pool_space(pool); i++)
+ (void) closesocket(pool->sockets[i].sock);
+}
+
+/*
+ * Put socket into the pool
+ */
+static void
+put_socket(struct socket_pool *pool, const struct socket *sp)
+{
+ (void) pthread_mutex_lock(&pool->mutex);
+
+ while (get_pool_space(pool) == 0)
+ (void) pthread_cond_wait(&pool->put_cond, &pool->mutex);
+
+ pool->sockets[pool->head++ % pool->size] = *sp;
+
+ (void) pthread_cond_signal(&pool->get_cond);
+ (void) pthread_mutex_unlock(&pool->mutex);
+}
+
+/*
+ * Get index of the socket to process
+ */
+static void
+get_socket(struct socket_pool *pool, struct socket *sp)
+{
+ pthread_mutex_lock(&pool->mutex);
+
+ while (get_pool_space(pool) == pool->size)
+ (void) pthread_cond_wait(&pool->get_cond, &pool->mutex);
+
+ *sp = pool->sockets[pool->tail++ % pool->size];
+
+ assert(pool->tail <= pool->head);
+
+ /* Wrap pointers if they both are greater than the pool size */
+ if (pool->tail > pool->size) {
+ pool->tail %= pool->size;
+ pool->head %= pool->size;
+ }
+
+ pthread_cond_signal(&pool->put_cond);
+ pthread_mutex_unlock(&pool->mutex);
+}
+
+/*
+ * 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", "jawohl", NULL};
+ const char **p;
+
+ for (p = trues; *p != NULL; p++)
+ if (str && !mg_strcasecmp(str, *p))
+ return (TRUE);
+
+ return (FALSE);
+}
+
+/*
+ * Skip the characters until one of the delimiters characters found.
+ * 0-terminate resulting word. Skip the rest of the delimiters if any.
+ * Advance pointer to buffer to the next word. Return found 0-terminated word.
+ */
+static char *
+skip(char **buf, const char *delimiters)
+{
+ char *p, *begin_word, *end_word, *end_delimiters;
+
+ begin_word = *buf;
+ end_word = begin_word + strcspn(begin_word, delimiters);
+ end_delimiters = end_word + strspn(end_word, delimiters);
+
+ for (p = end_word; p < end_delimiters; p++)
+ *p = '\0';
+
+ *buf = end_delimiters;
+
+ return (begin_word);
+}
+
+/*
+ * Return HTTP header value, or NULL if not found.
+ */
+static const char *
+get_header(const struct mg_request_info *ri, const char *name)
+{
+ int i;
+
+ for (i = 0; i < ri->num_headers; i++)
+ if (!mg_strcasecmp(name, ri->http_headers[i].name))
+ return (ri->http_headers[i].value);
+
+ return (NULL);
+}
+
+const char *
+mg_get_header(const struct mg_connection *conn, const char *name)
+{
+ return (get_header(&conn->request_info, name));
+}
+
+#if !(defined(NO_CGI) && defined(NO_SSI))
+/*
+ * Verify that given file has certain extension
+ */
+static bool_t
+match_extension(const char *path, const char *ext_list)
+{
+ size_t len, path_len;
+
+ path_len = strlen(path);
+
+ FOR_EACH_WORD_IN_LIST(ext_list, len)
+ if (len < path_len && path[path_len - (len + 1)] == '.' &&
+ !mg_strncasecmp(path + path_len - len, ext_list, len))
+ return (TRUE);
+
+ return (FALSE);
+}
+#endif /* !(NO_CGI && NO_SSI) */
+
+static bool_t
+match_regex(const char *uri, const char *regexp)
+{
+ if (*regexp == '\0')
+ return (*uri == '\0');
+
+ if (*regexp == '*')
+ do {
+ if (match_regex(uri, regexp + 1))
+ return (TRUE);
+ } while (*uri++ != '\0');
+
+ if (*uri != '\0' && *regexp == *uri)
+ return (match_regex(uri + 1, regexp + 1));
+
+ return (FALSE);
+}
+
+static const struct callback *
+find_callback(const struct mg_context *ctx, bool_t is_auth,
+ const char *uri, int status_code)
+{
+ const struct callback *cb;
+ int i;
+
+ for (i = 0; i < ctx->num_callbacks; i++) {
+ cb = ctx->callbacks + i;
+ if ((uri != NULL && cb->uri_regex != NULL &&
+ ((is_auth && cb->is_auth) || (!is_auth && !cb->is_auth)) &&
+ match_regex(uri, cb->uri_regex)) || (uri == NULL &&
+ (cb->status_code == 0 || cb->status_code == status_code)))
+ return (cb);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Send error message back to a client.
+ */
+static void
+send_error(struct mg_connection *conn, int status, const char *reason,
+ const char *fmt, ...)
+{
+ const struct callback *cb;
+ char buf[BUFSIZ];
+ va_list ap;
+ int len;
+
+ conn->request_info.status_code = status;
+
+ /* If error handler is set, call it. Otherwise, send error message */
+ if ((cb = find_callback(conn->ctx, FALSE, NULL, status)) != NULL) {
+ cb->func(conn, &conn->request_info, cb->user_data);
+ } else {
+ (void) mg_printf(conn,
+ "HTTP/1.1 %d %s\r\n"
+ "Content-Type: text/plain\r\n"
+ "Connection: close\r\n"
+ "\r\n", status, reason);
+
+ /* Errors 1xx, 204 and 304 MUST NOT send a body */
+ if (status > 199 && status != 204 && status != 304) {
+ conn->num_bytes_sent = mg_printf(conn,
+ "Error %d: %s\n", status, reason);
+
+ va_start(ap, fmt);
+ len = mg_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ conn->num_bytes_sent += mg_write(conn, buf, len);
+ }
+ }
+}
+
+#ifdef _WIN32
+static int
+pthread_mutex_init(pthread_mutex_t *mutex, void *unused) {
+ unused = NULL;
+ *mutex = CreateMutex(NULL, FALSE, NULL);
+ return (*mutex == NULL ? -1 : 0);
+}
+
+static int
+pthread_mutex_destroy(pthread_mutex_t *mutex) {
+ CloseHandle(*mutex);
+ return (0);
+}
+
+static int
+pthread_mutex_lock(pthread_mutex_t *mutex) {
+ return (WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1);
+}
+
+static int
+pthread_mutex_unlock(pthread_mutex_t *mutex) {
+ return (ReleaseMutex(*mutex) == 0 ? -1 : 0);
+}
+
+static void
+fix_directory_separators(char *path)
+{
+ for (; *path != '\0'; path++) {
+ if (*path == '/')
+ *path = '\\';
+ if (*path == '\\')
+ while (path[1] == '\\' || path[1] == '/')
+ (void) memmove(path + 1,
+ path + 2, strlen(path + 2) + 1);
+ }
+}
+
+static void
+to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len)
+{
+ char buf[FILENAME_MAX], *p;
+
+ mg_strlcpy(buf, path, sizeof(buf));
+ fix_directory_separators(buf);
+
+ /* Point p to the end of the file name */
+ p = buf + strlen(buf) - 1;
+
+ /* Trim trailing backslash character */
+ while (p > buf && *p == '\\' && p[-1] != ':')
+ *p-- = '\0';
+
+ /*
+ * Protect from CGI code disclosure.
+ * This is very nasty hole. Windows happily opens files with
+ * some garbage in the end of file name. So fopen("a.cgi ", "r")
+ * actually opens "a.cgi", and does not return an error!
+ */
+ if (*p == 0x20 || *p == 0x2e || *p == 0x2b || (*p & ~0x7f)) {
+ cry("Rejecting suspicious path: [%s]", buf);
+ buf[0] = '\0';
+ }
+
+ MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
+}
+
+static int
+mg_open(const char *path, int flags, int mode)
+{
+ wchar_t wbuf[FILENAME_MAX];
+
+ to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
+
+ return (_wopen(wbuf, flags, mode));
+}
+
+static int
+mg_stat(const char *path, struct stat *stp)
+{
+ wchar_t wbuf[FILENAME_MAX];
+
+ to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
+
+ return (_wstat(wbuf, (struct _stat *) stp));
+}
+
+static int
+mg_remove(const char *path)
+{
+ wchar_t wbuf[FILENAME_MAX];
+
+ to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
+
+ return (_wremove(wbuf));
+}
+
+static DIR *
+opendir(const char *name)
+{
+ DIR *dir = NULL;
+ char path[FILENAME_MAX];
+ wchar_t wpath[FILENAME_MAX];
+
+ if (name == NULL || name[0] == '\0') {
+ errno = EINVAL;
+ } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
+ errno = ENOMEM;
+ } else {
+ mg_snprintf(path, sizeof(path), "%s/*", name);
+ to_unicode(path, wpath, ARRAY_SIZE(wpath));
+ dir->handle = FindFirstFileW(wpath, &dir->info);
+
+ if (dir->handle != INVALID_HANDLE_VALUE) {
+ dir->result.d_name[0] = '\0';
+ } else {
+ free(dir);
+ dir = NULL;
+ }
+ }
+
+ return (dir);
+}
+
+static int
+closedir(DIR *dir)
+{
+ int result = -1;
+
+ if (dir != NULL) {
+ if (dir->handle != INVALID_HANDLE_VALUE)
+ result = FindClose(dir->handle) ? 0 : -1;
+
+ free(dir);
+ }
+
+ if (result == -1)
+ errno = EBADF;
+
+ return (result);
+}
+
+struct dirent *
+readdir(DIR *dir)
+{
+ struct dirent *result = 0;
+
+ if (dir && dir->handle != INVALID_HANDLE_VALUE) {
+ if(!dir->result.d_name ||
+ FindNextFileW(dir->handle, &dir->info)) {
+ result = &dir->result;
+
+ WideCharToMultiByte(CP_UTF8, 0, dir->info.cFileName,
+ -1, result->d_name,
+ sizeof(result->d_name), NULL, NULL);
+ }
+ } else {
+ errno = EBADF;
+ }
+
+ return (result);
+}
+
+#define set_close_on_exec(fd) /* No FD_CLOEXEC on Windows */
+
+static int
+start_thread(void * (*func)(void *), void *param)
+{
+ return (_beginthread((void (__cdecl *)( void *))func, 0, param) == 0);
+}
+
+static bool_t
+spawn_process(struct mg_connection *conn, const char *prog, char *envblk,
+ char *envp[], int fd_stdin, int fd_stdout, const char *dir)
+{
+ HANDLE me;
+ char *p, *interp, cmdline[FILENAME_MAX], line[FILENAME_MAX];
+ FILE *fp;
+ bool_t retval;
+ STARTUPINFOA si;
+ PROCESS_INFORMATION pi;
+
+ envp = NULL; /* Unused */
+
+ (void) memset(&si, 0, sizeof(si));
+ (void) memset(&pi, 0, sizeof(pi));
+
+ /* XXX redirect CGI errors to the error log file */
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_HIDE;
+
+ me = GetCurrentProcess();
+ DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me,
+ &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
+ DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdout), me,
+ &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
+
+ /* If CGI file is a script, try to read the interpreter line */
+ interp = conn->ctx->options[OPT_CGI_INTERPRETER];
+ if (interp == NULL) {
+ line[2] = '\0';
+ (void) mg_snprintf(cmdline, sizeof(cmdline), "%s%c%s",
+ dir, DIRSEP, prog);
+ if ((fp = fopen(cmdline, "r")) != NULL) {
+ (void) fgets(line, sizeof(line), fp);
+ if (memcmp(line, "#!", 2) != 0)
+ line[2] = '\0';
+ /* Trim whitespaces from interpreter name */
+ for (p = &line[strlen(line) - 1]; p > line &&
+ isspace(*p); p--)
+ *p = '\0';
+ (void) fclose(fp);
+ }
+ interp = line + 2;
+ (void) mg_snprintf(cmdline, sizeof(cmdline), "%s%s%s",
+ line + 2, line[2] == '\0' ? "" : " ", prog);
+ }
+
+ if ((p = (char *) strrchr(prog, '/')) != NULL)
+ prog = p + 1;
+
+ (void) mg_snprintf(cmdline, sizeof(cmdline), "%s %s", interp, prog);
+ (void) mg_snprintf(line, sizeof(line), "%s", dir);
+ fix_directory_separators(line);
+ fix_directory_separators(cmdline);
+
+ if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
+ CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) {
+ cry("%s: CreateProcess(%s): %d", __func__, cmdline, ERRNO);
+ retval = FALSE;
+ } else {
+ close(fd_stdin);
+ close(fd_stdout);
+ retval = TRUE;
+ }
+
+ CloseHandle(si.hStdOutput);
+ CloseHandle(si.hStdInput);
+ CloseHandle(pi.hThread);
+ CloseHandle(pi.hProcess);
+
+ return (retval);
+}
+
+static int
+pipe(int *fds)
+{
+ return (_pipe(fds, BUFSIZ, _O_BINARY));
+}
+
+static int
+mg_mkdir(const char *path, int mode)
+{
+ char buf[FILENAME_MAX];
+ wchar_t wbuf[FILENAME_MAX];
+
+ mode = 0; /* Unused */
+ mg_strlcpy(buf, path, sizeof(buf));
+ fix_directory_separators(buf);
+
+ MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
+
+ return (_wmkdir(wbuf));
+}
+
+#else
+
+static void
+set_close_on_exec(int fd)
+{
+ (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+static int
+start_thread(void * (*func)(void *), void *param)
+{
+ pthread_t thread_id;
+ pthread_attr_t attr;
+ int retval;
+
+ (void) pthread_attr_init(&attr);
+ (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ if ((retval = pthread_create(&thread_id, &attr, func, param)) != 0)
+ cry("%s: %s", __func__, strerror(retval));
+
+ return (retval);
+}
+
+#ifndef NO_CGI
+static bool_t
+spawn_process(struct mg_connection *conn, const char *prog, char *envblk,
+ char *envp[], int fd_stdin, int fd_stdout, const char *dir)
+{
+ int ret;
+ pid_t pid;
+ const char *interp;
+
+ envblk = NULL; /* unused */
+ ret = FALSE;
+
+ if ((pid = fork()) == -1) {
+ /* Parent */
+ ret = -1;
+ send_error(conn, 500, http_500_error,
+ "fork(): %s", strerror(ERRNO));
+ } else if (pid == 0) {
+ /* Child */
+ if (chdir(dir) != 0) {
+ cry("chdir(%s): %s", dir, strerror(ERRNO));
+ } else if (dup2(fd_stdin, 0) == -1) {
+ cry("dup2(stdin, %d): %s", fd_stdin, strerror(ERRNO));
+ } else if (dup2(fd_stdout, 1) == -1) {
+ cry("dup2(stdout, %d): %s", fd_stdout, strerror(ERRNO));
+ } else {
+ /* If error file is specified, send errors there */
+ if (error_log != NULL)
+ (void) dup2(fileno(error_log), 2);
+
+ (void) close(fd_stdin);
+ (void) close(fd_stdout);
+
+ /* Execute CGI program */
+ interp = conn->ctx->options[OPT_CGI_INTERPRETER];
+ if (interp == NULL) {
+ (void) execle(prog, prog, NULL, envp);
+ cry("execle(%s): %s", prog, strerror(ERRNO));
+ } else {
+ (void) execle(interp, interp, prog, NULL, envp);
+ cry("execle(%s %s): %s",
+ interp, prog, strerror(ERRNO));
+ }
+ }
+ exit(EXIT_FAILURE);
+ } else {
+ /* Parent. Suspended until child does execle() */
+ (void) close(fd_stdin);
+ (void) close(fd_stdout);
+ ret = TRUE;
+ }
+
+ return (ret);
+}
+#endif /* !NO_CGI */
+#endif /* _WIN32 */
+
+static void
+mg_lock(struct mg_context *ctx)
+{
+ if (pthread_mutex_lock(&ctx->mutex) != 0)
+ cry("pthread_mutex_lock: %s", strerror(ERRNO));
+}
+
+static void
+mg_unlock(struct mg_context *ctx)
+{
+ if (pthread_mutex_unlock(&ctx->mutex) != 0)
+ cry("pthread_mutex_unlock: %s", strerror(ERRNO));
+}
+
+/*
+ * Write data to the IO channel - opened file descriptor, socket or SSL
+ * descriptor. Return number of bytes written.
+ */
+static uint64_t
+push(int fd, SOCKET sock, SSL *ssl, const char *buf, uint64_t len)
+{
+ uint64_t sent;
+ int n, k;
+
+ sent = 0;
+ while (sent < len) {
+
+ /* How many bytes we send in this iteration */
+ k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
+
+ if (ssl != NULL) {
+ n = SSL_write(ssl, buf + sent, k);
+ } else if (fd != -1) {
+ n = write(fd, buf + sent, k);
+ } else {
+ n = send(sock, buf + sent, k, 0);
+ }
+
+ if (n < 0) {
+ cry("%s: %s", __func__, strerror(ERRNO));
+ break;
+ } else {
+ sent += n;
+ }
+ }
+
+ return (sent);
+}
+
+/*
+ * Read from IO channel - opened file descriptor, socket, or SSL descriptor.
+ * Return number of bytes read.
+ */
+static int
+pull(int fd, SOCKET sock, SSL *ssl, char *buf, int len)
+{
+ int nread;
+
+ if (ssl != NULL) {
+ nread = SSL_read(ssl, buf, len);
+ } else if (fd != -1) {
+ nread = read(fd, buf, (size_t) len);
+ } else {
+ nread = recv(sock, buf, (size_t) len, 0);
+ }
+
+ if (nread < 0)
+ cry("%s failed: %s", __func__, strerror(ERRNO));
+
+ return (nread);
+}
+
+int
+mg_write(struct mg_connection *conn, const void *buf, int len)
+{
+ assert(len >= 0);
+ return ((int) push(-1, conn->sock, conn->ssl,
+ (const char *) buf, (uint64_t) len));
+}
+
+int
+mg_printf(struct mg_connection *conn, const char *fmt, ...)
+{
+ char buf[MAX_REQUEST_SIZE];
+ int len;
+ va_list ap;
+
+ va_start(ap, fmt);
+ len = mg_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ return (mg_write(conn, buf, len));
+}
+
+/*
+ * Return content length of the request, or UNKNOWN_CONTENT_LENGTH constant if
+ * Content-Length header is not set.
+ */
+static uint64_t
+get_content_length(const struct mg_connection *conn)
+{
+ const char *cl = mg_get_header(conn, "Content-Length");
+ return (cl == NULL ? UNKNOWN_CONTENT_LENGTH : strtoull(cl, NULL, 10));
+}
+
+/*
+ * URL-decode input buffer into destination buffer.
+ * 0-terminate the destination buffer. Return the length of decoded data.
+ * form-url-encoded data differs from URI encoding in a way that it
+ * 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 i, j;
+ int a, b;
+#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
+
+ for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
+ if (src[i] == '%' &&
+ isxdigit(* (unsigned char *) (src + i + 1)) &&
+ isxdigit(* (unsigned char *) (src + i + 2))) {
+ a = tolower(* (unsigned char *) (src + i + 1));
+ b = tolower(* (unsigned char *) (src + i + 2));
+ dst[j] = ((HEXTOI(a) << 4) | HEXTOI(b)) & 0xff;
+ i += 2;
+ } else if (is_form_url_encoded && src[i] == '+') {
+ dst[j] = ' ';
+ } else {
+ dst[j] = src[i];
+ }
+ }
+
+ dst[j] = '\0'; /* Null-terminate the destination */
+
+ return (j);
+}
+
+/*
+ * Search for a form variable in a given buffer.
+ * Semantic is the same as for mg_get_var().
+ */
+static char *
+get_var(const char *name, const char *buf, size_t buf_len)
+{
+ const char *p, *e, *s;
+ char tmp[BUFSIZ];
+ size_t var_len;
+
+ var_len = strlen(name);
+ e = buf + buf_len;
+
+ /* 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(name, p, var_len)) {
+
+ /* Point p to variable value */
+ p += var_len + 1;
+
+ /* Point s to the end of the value */
+ s = (const char *) memchr(p, '&', e - p);
+ if (s == NULL)
+ s = e;
+
+ /* URL-decode value. Return result length */
+ (void) url_decode(p, s - p, tmp, sizeof(tmp), TRUE);
+ return (mg_strdup(tmp));
+ }
+
+ return (NULL);
+}
+
+/*
+ * Return form data variable.
+ * 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.
+ */
+char *
+mg_get_var(const struct mg_connection *conn, const char *name)
+{
+ const struct mg_request_info *ri = &conn->request_info;
+ char *v1, *v2;
+
+ v1 = v2 = NULL;
+
+ /* Look in both query_string and POST data */
+ if (ri->query_string != NULL)
+ v1 = get_var(name, ri->query_string, strlen(ri->query_string));
+ if (ri->post_data_len > 0)
+ v2 = get_var(name, ri->post_data, ri->post_data_len);
+
+ /* If they both have queried variable, POST data wins */
+ if (v1 != NULL && v2 != NULL)
+ free(v1);
+
+ return (v2 == NULL ? v1 : v2);
+}
+
+/*
+ * Transform URI to the file name.
+ */
+static void
+make_path(struct mg_context *ctx, const char *uri, char *buf, size_t buf_len)
+{
+ char *p, *s;
+ size_t len;
+
+ mg_snprintf(buf, buf_len, "%s%s", ctx->options[OPT_ROOT], uri);
+
+ /* If requested URI has aliased prefix, use alternate root */
+ mg_lock(ctx);
+ s = ctx->options[OPT_ALIASES];
+ FOR_EACH_WORD_IN_LIST(s, len) {
+
+ p = (char *) memchr(s, '=', len);
+ if (p == NULL || p >= s + len || p == s)
+ continue;
+
+ if (memcmp(uri, s, p - s) == 0) {
+ (void) mg_snprintf(buf, buf_len, "%.*s%s",
+ (s + len) - p - 1, p + 1, uri + (p - s));
+ break;
+ }
+ }
+ mg_unlock(ctx);
+
+ /* Remove trailing '/' characters, if directory is requested */
+ for (p = buf + strlen(buf) - 1; p > buf && *p == '/'; p--)
+ *p = '\0';
+
+#ifdef _WIN32
+ for (p = buf; *p != '\0'; p++)
+ if (*p == '/')
+ *p = '\\';
+#endif /* _WIN32 */
+}
+
+/*
+ * Setup listening socket on given port, return socket
+ */
+static SOCKET
+mg_open_listening_port(int port)
+{
+ SOCKET sock;
+ int on = 1;
+ struct usa sa;
+
+ sa.len = sizeof(sa.u.sin);
+ sa.u.sin.sin_family = AF_INET;
+ sa.u.sin.sin_port = htons((uint16_t) port);
+ sa.u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if ((sock = socket(PF_INET, SOCK_STREAM, 6)) != INVALID_SOCKET &&
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &on, sizeof(on)) == 0 &&
+ bind(sock, &sa.u.sa, sa.len) == 0 &&
+ listen(sock, 128) == 0) {
+ /* Success */
+ set_close_on_exec(sock);
+ } else {
+ /* Error */
+ cry("open_listening_port(%d): %s", port, strerror(errno));
+ if (sock != INVALID_SOCKET)
+ (void) closesocket(sock);
+ sock = INVALID_SOCKET;
+ }
+
+ return (sock);
+}
+
+/*
+ * Check whether full request is buffered Return headers length, or 0
+ */
+static int
+get_request_len(const char *buf, size_t buflen)
+{
+ const char *s, *e;
+ int len = 0;
+
+ for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
+ /* Control characters are not allowed but >=128 is. */
+ if (!isprint(* (unsigned char *) s) && *s != '\r' &&
+ *s != '\n' && * (unsigned char *) s < 128)
+ len = -1;
+ else if (s[0] == '\n' && s[1] == '\n')
+ len = (int) (s - buf) + 2;
+ else if (s[0] == '\n' && &s[1] < e &&
+ s[1] == '\r' && s[2] == '\n')
+ len = (int) (s - buf) + 3;
+
+ return (len);
+}
+
+/*
+ * Convert month to the month number. Return -1 on error, or month number
+ */
+static int
+montoi(const char *s)
+{
+ static const char *month_names[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+ size_t i;
+
+ for (i = 0; i < sizeof(month_names) / sizeof(month_names[0]); i++)
+ if (!strcmp(s, month_names[i]))
+ return ((int) i);
+
+ return (-1);
+}
+
+/*
+ * Parse date-time string, and return the corresponding time_t value
+ */
+static time_t
+date_to_epoch(const char *s)
+{
+ struct tm tm, *tmp;
+ time_t current_time;
+ char mon[32];
+ int sec, min, hour, mday, month, year;
+
+ (void) memset(&tm, 0, sizeof(tm));
+ sec = min = hour = mday = month = year = 0;
+
+ if (((sscanf(s, "%d/%3s/%d %d:%d:%d",
+ &mday, mon, &year, &hour, &min, &sec) == 6) ||
+ (sscanf(s, "%d %3s %d %d:%d:%d",
+ &mday, mon, &year, &hour, &min, &sec) == 6) ||
+ (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",
+ &mday, mon, &year, &hour, &min, &sec) == 6) ||
+ (sscanf(s, "%d-%3s-%d %d:%d:%d",
+ &mday, mon, &year, &hour, &min, &sec) == 6)) &&
+ (month = montoi(mon)) != -1) {
+ tm.tm_mday = mday;
+ tm.tm_mon = month;
+ tm.tm_year = year;
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec;
+ }
+
+ if (tm.tm_year > 1900)
+ tm.tm_year -= 1900;
+ else if (tm.tm_year < 70)
+ tm.tm_year += 100;
+
+ /* Set Daylight Saving Time field */
+ current_time = time(NULL);
+ tmp = localtime(¤t_time);
+ tm.tm_isdst = tmp->tm_isdst;
+
+ return (mktime(&tm));
+}
+
+static void
+remove_double_dots(char *s)
+{
+ char *p = s;
+
+ while (*s != '\0') {
+ *p++ = *s++;
+ if (s[-1] == '/' || s[-1] == '\\')
+ while (*s == '.' || *s == '/' || *s == '\\')
+ s++;
+ }
+ *p = '\0';
+}
+
+static const struct {
+ const char *extension;
+ const char *mime_type;
+} mime_types[] = {
+ {"html", "text/html" },
+ {"htm", "text/html" },
+ {"shtm", "text/html" },
+ {"shtml", "text/html" },
+ {"css", "text/css" },
+ {"js", "application/x-javascript" },
+ {"ico", "image/x-icon" },
+ {"gif", "image/gif" },
+ {"jpg", "image/jpeg" },
+ {"jpeg", "image/jpeg" },
+ {"png", "image/png" },
+ {"svg", "image/svg+xml" },
+ {"torrent", "application/x-bittorrent" },
+ {"wav", "audio/x-wav" },
+ {"mp3", "audio/x-mp3" },
+ {"mid", "audio/mid" },
+ {"m3u", "audio/x-mpegurl" },
+ {"ram", "audio/x-pn-realaudio" },
+ {"ra", "audio/x-pn-realaudio" },
+ {"doc", "application/msword", },
+ {"exe", "application/octet-stream" },
+ {"zip", "application/x-zip-compressed" },
+ {"xls", "application/excel" },
+ {"tgz", "application/x-tar-gz" },
+ {"tar.gz", "application/x-tar-gz" },
+ {"tar", "application/x-tar" },
+ {"gz", "application/x-gunzip" },
+ {"arj", "application/x-arj-compressed" },
+ {"rar", "application/x-arj-compressed" },
+ {"rtf", "application/rtf" },
+ {"pdf", "application/pdf" },
+ {"swf", "application/x-shockwave-flash" },
+ {"mpg", "video/mpeg" },
+ {"mpeg", "video/mpeg" },
+ {"asf", "video/x-ms-asf" },
+ {"avi", "video/x-msvideo" },
+ {"bmp", "image/bmp" },
+ {NULL, NULL }
+};
+
+static const char *
+get_mime_type(const char *path)
+{
+ const char *extension;
+ size_t i, ext_len;
+
+ if ((extension = strrchr(path, '.')) != NULL) {
+
+ extension++;
+ ext_len = strlen(extension);
+
+ /* If no luck, try built-in mime types */
+ for (i = 0; mime_types[i].extension != NULL; i++)
+ if (!mg_strcasecmp(extension,
+ mime_types[i].extension))
+ return (mime_types[i].mime_type);
+ }
+
+ return ("text/plain");
+}
+
+#if !defined(NO_AUTH)
+#ifndef HAVE_MD5
+typedef struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+} MD5_CTX;
+
+#if __BYTE_ORDER == 1234
+#define byteReverse(buf, len) /* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void
+byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32_t t;
+ do {
+ t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif /* __BYTE_ORDER */
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+( w += f(x, y, z) + data, w = w<>(32-s), w += x )
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+static void
+MD5Init(MD5_CTX *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void
+MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void
+MD5Final(unsigned char digest[16], MD5_CTX *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+ ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+#endif /* !HAVE_MD5 */
+
+/*
+ * Stringify binary data. Output buffer must be twice as big as input,
+ * because each byte takes 2 bytes in string representation
+ */
+static void
+bin2str(char *to, const unsigned char *p, size_t len)
+{
+ static const char *hex = "0123456789abcdef";
+
+ for (; len--; p++) {
+ *to++ = hex[p[0] >> 4];
+ *to++ = hex[p[0] & 0x0f];
+ }
+ *to = '\0';
+}
+
+/*
+ * Return stringified MD5 hash for list of vectors.
+ * buf must point to 33-bytes long buffer
+ */
+void
+mg_md5(char *buf, ...)
+{
+ unsigned char hash[16];
+ const char *p;
+ va_list ap;
+ MD5_CTX ctx;
+
+ MD5Init(&ctx);
+
+ va_start(ap, buf);
+ while ((p = va_arg(ap, const char *)) != NULL)
+ MD5Update(&ctx, (unsigned char *) p, (int) strlen(p));
+ va_end(ap);
+
+ MD5Final(hash, &ctx);
+ bin2str(buf, hash, sizeof(hash));
+}
+
+/*
+ * 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)
+{
+ char ha2[32 + 1], expected_response[32 + 1];
+
+ /* XXX Due to a bug in MSIE, we do not compare the URI */
+ /* Also, we do not check for authentication timeout */
+ if (/*strcmp(dig->uri, c->ouri) != 0 || */
+ strlen(response) != 32 /*||
+ now - strtoul(dig->nonce, NULL, 10) > 3600 */)
+ return (FALSE);
+
+ mg_md5(ha2, method, ":", uri, NULL);
+ mg_md5(expected_response, ha1, ":", nonce, ":", nc,
+ ":", cnonce, ":", qop, ":", ha2, NULL);
+
+ return (!mg_strcasecmp(response, expected_response));
+}
+
+/*
+ * Use the global passwords file, if specified by auth_gpass option,
+ * or search for .htpasswd in the requested directory.
+ */
+static FILE *
+open_auth_file(struct mg_context *ctx, const char *path)
+{
+ char name[FILENAME_MAX];
+ const char *p, *e;
+ struct stat st;
+ FILE *fp;
+
+ if (ctx->options[OPT_AUTH_GPASSWD] != NULL) {
+ /* Use global passwords file */
+ if ((fp = fopen(ctx->options[OPT_AUTH_GPASSWD], "r")) == NULL)
+ cry("fopen(%s): %s",
+ ctx->options[OPT_AUTH_GPASSWD], strerror(ERRNO));
+ } else if (!mg_stat(path, &st) && S_ISDIR(st.st_mode)) {
+ (void) mg_snprintf(name, sizeof(name), "%s%c%s",
+ path, DIRSEP, PASSWORDS_FILE_NAME);
+ fp = fopen(name, "r");
+ } else {
+ /*
+ * Try to find .htpasswd in requested directory.
+ * Given the path, create the path to .htpasswd file
+ * in the same directory. Find the right-most
+ * directory separator character first. That would be the
+ * directory name. If directory separator character is not
+ * found, 'e' will point to 'p'.
+ */
+ for (p = path, e = p + strlen(p) - 1; e > p; e--)
+ if (IS_DIRSEP_CHAR(*e))
+ break;
+
+ /*
+ * Make up the path by concatenating directory name and
+ * .htpasswd file name.
+ */
+ (void) mg_snprintf(name, sizeof(name), "%.*s%c%s",
+ (int) (e - p), p, DIRSEP, PASSWORDS_FILE_NAME);
+ fp = fopen(name, "r");
+ }
+
+ return (fp);
+}
+
+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)
+{
+ 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 (FALSE);
+
+ /* Make modifiable copy of the auth header */
+ (void) mg_strlcpy(buf, auth_header + 7, buf_size);
+
+ s = buf;
+ (void) memset(ah, 0, sizeof(*ah));
+
+ /* Gobble initial spaces */
+ while (isspace(* (unsigned char *) s))
+ s++;
+
+ /* Parse authorization header */
+ for (;;) {
+ name = skip(&s, "=");
+ value = skip(&s, ", ");
+
+ if (*value == '"') {
+ value++;
+ value[strlen(value) - 1] = '\0';
+ } else if (*value == '\0') {
+ break;
+ }
+
+ if (!strcmp(name, "username")) {
+ ah->user = value;
+ } else if (!strcmp(name, "cnonce")) {
+ ah->cnonce = value;
+ } else if (!strcmp(name, "response")) {
+ ah->response = value;
+ } else if (!strcmp(name, "uri")) {
+ ah->uri = value;
+ } else if (!strcmp(name, "qop")) {
+ ah->qop = value;
+ } else if (!strcmp(name, "nc")) {
+ ah->nc = value;
+ } else if (!strcmp(name, "nonce")) {
+ ah->nonce = value;
+ }
+ }
+
+ /* CGI needs it as REMOTE_USER */
+ if (ah->user != NULL)
+ conn->request_info.remote_user = mg_strdup(ah->user);
+
+ return (TRUE);
+}
+
+/*
+ * Authorize against the opened passwords file. Return 1 if authorized.
+ */
+static bool_t
+authorize(struct mg_connection *conn, FILE *fp)
+{
+ struct ah ah;
+ char line[256], f_user[256], domain[256], ha1[256],
+ buf[MAX_REQUEST_SIZE];
+
+ if (!parse_auth_header(conn, buf, sizeof(buf), &ah))
+ return (FALSE);
+
+ /* Loop over passwords file */
+ while (fgets(line, sizeof(line), fp) != NULL) {
+
+ if (sscanf(line, "%[^:]:%[^:]:%s", f_user, domain, ha1) != 3)
+ continue;
+
+ if (!strcmp(ah.user, f_user) &&
+ !strcmp(domain, conn->ctx->options[OPT_AUTH_DOMAIN]))
+ return (check_password(
+ conn->request_info.request_method, ha1,
+ ah.uri, ah.nonce, ah.nc, ah.cnonce,
+ ah.qop, ah.response));
+ }
+
+ return (FALSE);
+}
+
+/*
+ * Return TRUE if request is authorised, FALSE otherwise.
+ */
+static bool_t
+check_authorization(struct mg_connection *conn, const char *path)
+{
+ FILE *fp;
+ size_t len, n;
+ char protected_path[FILENAME_MAX];
+ const char *p, *s;
+ const struct callback *cb;
+ bool_t authorized;
+
+ fp = NULL;
+ authorized = TRUE;
+
+ mg_lock(conn->ctx);
+ s = conn->ctx->options[OPT_PROTECT];
+ FOR_EACH_WORD_IN_LIST(s, len) {
+
+ p = (const char *) memchr(s, '=', len);
+ if (p == NULL || p >= s + len || p == s)
+ continue;
+
+ if (!memcmp(conn->request_info.uri, s, p - s)) {
+
+ n = (size_t) (s + len - p);
+ if (n > sizeof(protected_path) - 1)
+ n = sizeof(protected_path) - 1;
+
+ mg_strlcpy(protected_path, p + 1, n);
+
+ if ((fp = fopen(protected_path, "r")) == NULL)
+ cry("check_auth: cannot open %s: %s",
+ protected_path, strerror(errno));
+ break;
+ }
+ }
+ mg_unlock(conn->ctx);
+
+ if (fp == NULL)
+ fp = open_auth_file(conn->ctx, path);
+
+ if (fp != NULL) {
+ authorized = authorize(conn, fp);
+ (void) fclose(fp);
+ }
+
+ if ((cb = find_callback(conn->ctx, TRUE,
+ conn->request_info.uri, -1)) != NULL) {
+ struct ah ah;
+ char buf[MAX_REQUEST_SIZE];
+ void *user_data = cb->user_data;
+
+ authorized = FALSE;
+ if (parse_auth_header(conn, buf, sizeof(buf), &ah)) {
+ cb->func(conn, &conn->request_info, &user_data);
+ authorized = (bool_t) (long) user_data;
+ }
+ }
+
+ return (authorized);
+}
+
+static void
+send_authorization_request(struct mg_connection *conn)
+{
+ (void) mg_printf(conn,
+ "HTTP/1.1 401 Unauthorized\r\n"
+ "WWW-Authenticate: Digest qop=\"auth\", "
+ "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
+ conn->ctx->options[OPT_AUTH_DOMAIN], (unsigned long) time(NULL));
+}
+
+static bool_t
+is_authorized_for_put(struct mg_connection *conn)
+{
+ FILE *fp;
+ int ret = FALSE;
+
+ if ((fp = fopen(conn->ctx->options[OPT_AUTH_PUT], "r")) != NULL) {
+ set_close_on_exec(fileno(fp));
+ ret = authorize(conn, fp);
+ (void) fclose(fp);
+ }
+
+ return (ret);
+}
+#endif /* NO_AUTH */
+
+static bool_t
+does_client_want_keep_alive(const struct mg_connection *conn)
+{
+ const char *value = mg_get_header(conn, "Connection");
+
+ /* HTTP/1.1 assumes keep-alive, if Connection header is not set */
+ return ((value == NULL && conn->request_info.http_version_major == 1 &&
+ conn->request_info.http_version_minor == 1) || (value != NULL &&
+ !mg_strcasecmp(value, "keep-alive")));
+}
+
+struct de {
+ struct mg_connection *conn;
+ char *file_name;
+ struct stat st;
+};
+
+static void
+print_dir_entry(struct de *de)
+{
+ char size[64], mod[64];
+
+ if (S_ISDIR(de->st.st_mode)) {
+ (void) mg_snprintf(size, sizeof(size), "%s", "<DIR>");
+ } else {
+ if (de->st.st_size < 1024)
+ (void) mg_snprintf(size, sizeof(size),
+ "%lu", (unsigned long) de->st.st_size);
+ else if (de->st.st_size < 1024 * 1024)
+ (void) mg_snprintf(size, sizeof(size),
+ "%.1fk", (double) de->st.st_size / 1024);
+ else if (de->st.st_size < 1024 * 1024 * 1024)
+ (void) mg_snprintf(size, sizeof(size),
+ "%.1fM", (double) de->st.st_size / 1048576);
+ else
+ (void) mg_snprintf(size, sizeof(size),
+ "%.1fG", (double) de->st.st_size / 1073741824);
+ }
+ (void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
+ localtime(&de->st.st_mtime));
+ de->conn->num_bytes_sent += mg_printf(de->conn,
+ "<tr><td>%s%s</a></td>"
+ "<td> %s</td><td> %s</td></tr>\n",
+ de->conn->request_info.uri, de->file_name, de->file_name,
+ S_ISDIR(de->st.st_mode) ? "/" : "", mod, size);
+}
+
+static int
+compare_dir_entries(const void *p1, const void *p2)
+{
+ const struct de *a = (struct de *) p1, *b = (struct de *) p2;
+ const char *q = a->conn->request_info.query_string;
+ int cmp_result = 0;
+
+ if (*q == 'n') {
+ cmp_result = strcmp(a->file_name, b->file_name);
+ } else if (*q == 's') {
+ cmp_result = a->st.st_size == b->st.st_size ? 0 :
+ a->st.st_size > b->st.st_size ? 1 : -1;
+ } else if (*q == 'd') {
+ cmp_result = a->st.st_mtime == b->st.st_mtime ? 0 :
+ a->st.st_mtime > b->st.st_mtime ? 1 : -1;
+ }
+
+ return (q[1] == 'd' ? -cmp_result : cmp_result);
+}
+
+static void
+send_directory(struct mg_connection *conn, const char *dir)
+{
+ struct dirent *dp;
+ DIR *dirp;
+ struct de *entries = NULL;
+ char path[FILENAME_MAX], sort_direction;
+ int i, num_entries = 0, arr_size = 128;
+
+ if ((dirp = opendir(dir)) == NULL) {
+ send_error(conn, 500, "Cannot open directory",
+ "Error: opendir(%s): %s", path, strerror(ERRNO));
+ return;
+ }
+
+ (void) mg_printf(conn, "%s",
+ "HTTP/1.1 200 OK\r\n"
+ "Connection: close\r\n"
+ "Content-Type: text/html; charset=utf-8\r\n\r\n");
+
+ sort_direction = conn->request_info.query_string != NULL &&
+ conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
+
+ while ((dp = readdir(dirp)) != NULL) {
+
+ /* Do not show current dir and passwords file */
+ if (!strcmp(dp->d_name, ".") ||
+ !strcmp(dp->d_name, "..") ||
+ !strcmp(dp->d_name, PASSWORDS_FILE_NAME))
+ continue;
+
+ if (entries == NULL || num_entries >= arr_size) {
+ arr_size *= 2;
+ entries = (struct de *) realloc(entries,
+ arr_size * sizeof(entries[0]));
+ }
+
+ if (entries == NULL) {
+ send_error(conn, 500, "Cannot open directory",
+ "%s", "Error: cannot allocate memory");
+ return;
+ }
+
+ (void) mg_snprintf(path, sizeof(path), "%s%c%s",
+ dir, DIRSEP, dp->d_name);
+
+ (void) stat(path, &entries[num_entries].st);
+ entries[num_entries].conn = conn;
+ entries[num_entries].file_name = mg_strdup(dp->d_name);
+ num_entries++;
+ }
+ (void) closedir(dirp);
+
+ if (conn->request_info.query_string != NULL)
+ qsort(entries, num_entries,
+ sizeof(entries[0]), compare_dir_entries);
+
+ conn->num_bytes_sent += mg_printf(conn,
+ "<html><head><title>Index of %s</title>"
+ "<style>th {text-align: left;}</style></head>"
+ "<body><h1>Index of %s</h1><pre>"
+ "<tr><th>Name</a></th>"
+ "<th>Modified</a></th>"
+ "<th>Size</a></th></tr>"
+ "<tr><hr></td></tr>",
+ conn->request_info.uri, conn->request_info.uri,
+ sort_direction, sort_direction, sort_direction);
+
+ /* Print first entry - link to a parent directory */
+ conn->num_bytes_sent += mg_printf(conn,
+ "<tr><td>%s</a></td>"
+ "<td> %s</td><td> %s</td></tr>\n",
+ conn->request_info.uri, "..", "Parent directory", "-", "-");
+
+ for (i = 0; i < num_entries; i++) {
+ print_dir_entry(&entries[i]);
+ free(entries[i].file_name);
+ }
+ free(entries);
+
+ conn->num_bytes_sent += mg_printf(conn, "%s", "</table></body></html>");
+ conn->request_info.status_code = 200;
+}
+
+/*
+ * Send len bytes from the opened file to the client.
+ */
+static void
+send_opened_file_stream(struct mg_connection *conn, int fd, uint64_t len)
+{
+ char buf[BUFSIZ];
+ int n;
+
+ while (len > 0) {
+ n = sizeof(buf);
+ if ((uint64_t) n > len)
+ n = (int) len;
+ if ((n = read(fd, buf, n)) <= 0)
+ break;
+ conn->num_bytes_sent += mg_write(conn, buf, n);
+ len -= n;
+ }
+}
+
+static void
+send_file(struct mg_connection *conn, const char *path, struct stat *stp)
+{
+ char date[64], lm[64], etag[64], range[64];
+ const char *fmt = "%a, %d %b %Y %H:%M:%S GMT", *msg = "OK";
+ const char *mime_type, *s;
+ time_t curtime = time(NULL);
+ unsigned long long cl, r1, r2;
+ int fd, n;
+
+ mime_type = get_mime_type(path);
+ cl = stp->st_size;
+ conn->request_info.status_code = 200;
+ range[0] = '\0';
+
+ if ((fd = mg_open(path, O_RDONLY | O_BINARY, 0644)) == -1) {
+ send_error(conn, 500, http_500_error,
+ "fopen(%s): %s", path, strerror(ERRNO));
+ return;
+ }
+ set_close_on_exec(fd);
+
+ /* If Range: header specified, act accordingly */
+ s = mg_get_header(conn, "Range");
+ r1 = r2 = 0;
+ if (s != NULL && (n = sscanf(s,"bytes=%llu-%llu", &r1, &r2)) > 0) {
+ conn->request_info.status_code = 206;
+ (void) lseek(fd, (long) r1, SEEK_SET);
+ cl = n == 2 ? r2 - r1 + 1: cl - r1;
+ (void) mg_snprintf(range, sizeof(range),
+ "Content-Range: bytes %llu-%llu/%llu\r\n",
+ r1, r1 + cl - 1, cl);
+ msg = "Partial Content";
+ }
+
+ /* Prepare Etag, Date, Last-Modified headers */
+ (void) strftime(date, sizeof(date), fmt, localtime(&curtime));
+ (void) strftime(lm, sizeof(lm), fmt, localtime(&stp->st_mtime));
+ (void) mg_snprintf(etag, sizeof(etag), "%lx.%lx",
+ (unsigned long) stp->st_mtime, (unsigned long) stp->st_size);
+
+ /* Since we send Content-Length, we can keep the connection alive */
+ conn->keep_alive = does_client_want_keep_alive(conn);
+
+ (void) mg_printf(conn,
+ "HTTP/1.1 %d %s\r\n"
+ "Date: %s\r\n"
+ "Last-Modified: %s\r\n"
+ "Etag: \"%s\"\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: %llu\r\n"
+ "Connection: %s\r\n"
+ "Accept-Ranges: bytes\r\n"
+ "%s\r\n",
+ conn->request_info.status_code, msg, date, lm, etag, mime_type, cl,
+ conn->keep_alive ? "keep-alive" : "close", range);
+
+ if (strcmp(conn->request_info.request_method, "HEAD") != 0)
+ send_opened_file_stream(conn, fd, cl);
+ (void) close(fd);
+}
+
+static void
+parse_http_headers(char **buf, struct mg_request_info *ri)
+{
+ int i;
+
+ for (i = 0; i < MAX_HTTP_HEADERS; i++) {
+ ri->http_headers[i].name = skip(buf, ": ");
+ ri->http_headers[i].value = skip(buf, "\r\n");
+ if (ri->http_headers[i].name[0] == '\0')
+ break;
+ ri->num_headers = i + 1;
+ }
+}
+
+static bool_t
+is_known_http_method(const char *method)
+{
+ return (!strcmp(method, "GET") ||
+ !strcmp(method, "POST") ||
+ !strcmp(method, "HEAD") ||
+ !strcmp(method, "PUT") ||
+ !strcmp(method, "DELETE"));
+}
+
+static bool_t
+parse_http_request(char *buf, struct mg_request_info *ri, const struct usa *usa)
+{
+ char *http_version;
+ int n, success_code = FALSE;
+
+ ri->request_method = skip(&buf, " ");
+ ri->uri = skip(&buf, " ");
+ http_version = skip(&buf, "\r\n");
+
+ if (is_known_http_method(ri->request_method) &&
+ ri->uri[0] == '/' &&
+ sscanf(http_version, "HTTP/%d.%d%n",
+ &ri->http_version_major, &ri->http_version_minor, &n) == 2 &&
+ http_version[n] == '\0') {
+ parse_http_headers(&buf, ri);
+ ri->remote_port = ntohs(usa->u.sin.sin_port);
+ (void) memcpy(&ri->remote_ip, &usa->u.sin.sin_addr.s_addr, 4);
+ ri->remote_ip = ntohl(ri->remote_ip);
+ success_code = TRUE;
+ }
+
+ return (success_code);
+}
+
+static int
+read_request(int fd, SOCKET sock, SSL *ssl, char *buf, int bufsiz, int *nread)
+{
+ int n, request_len;
+
+ request_len = 0;
+ while (*nread < bufsiz && request_len == 0) {
+ n = pull(fd, sock, ssl, buf + *nread, bufsiz - *nread);
+ if (n <= 0) {
+ break;
+ } else {
+ *nread += n;
+ request_len = get_request_len(buf, (size_t) *nread);
+ }
+ }
+
+ return (request_len);
+}
+
+/*
+ * For given directory path, substitute it to valid index file.
+ * Return 0 if index file has been found, -1 if not found
+ */
+static bool_t
+send_index_file(struct mg_connection *conn,
+ char *buf, size_t buf_len, struct stat *stp)
+{
+ const char *s;
+ size_t len, n;
+
+ n = strlen(buf);
+ buf[n] = DIRSEP;
+
+ mg_lock(conn->ctx);
+ s = conn->ctx->options[OPT_INDEX_FILES];
+ FOR_EACH_WORD_IN_LIST(s, len) {
+ if (len > buf_len - n - 1)
+ continue;
+ (void) mg_strlcpy(buf + n + 1, s, len + 1);
+ if (stat(buf, stp) == 0) {
+ send_file(conn, buf, stp);
+ mg_unlock(conn->ctx);
+ return (TRUE);
+ }
+ }
+ mg_unlock(conn->ctx);
+
+ buf[n] = '\0';
+
+ return (FALSE);
+}
+
+static void
+mg_bind(struct mg_context *ctx, const char *uri_regex, int status_code,
+ mg_callback_t func, bool_t is_auth, void *user_data)
+{
+ struct callback *cb;
+
+ if (ctx->num_callbacks >= (int) ARRAY_SIZE(ctx->callbacks) - 1) {
+ cry("Too many callbacks! Increase MAX_CALLBACKS.");
+ } else {
+ cb = &ctx->callbacks[ctx->num_callbacks];
+ cb->uri_regex = uri_regex ? mg_strdup(uri_regex) : NULL;
+ cb->func = func;
+ cb->is_auth = is_auth;
+ cb->status_code = status_code;
+ cb->user_data = user_data;
+ ctx->num_callbacks++;
+ }
+}
+
+void
+mg_bind_to_uri(struct mg_context *ctx, const char *uri_regex,
+ mg_callback_t func, void *user_data)
+{
+ assert(func != NULL);
+ assert(uri_regex != NULL);
+ mg_bind(ctx, uri_regex, -1, func, FALSE, user_data);
+}
+
+void
+mg_bind_to_error_code(struct mg_context *ctx, int error_code,
+ mg_callback_t func, void *user_data)
+{
+ assert(error_code >= 0 && error_code < 1000);
+ assert(func != NULL);
+ mg_bind(ctx, NULL, error_code, func, FALSE, user_data);
+}
+
+void
+mg_protect_uri(struct mg_context *ctx, const char *uri_regex,
+ mg_callback_t func, void *user_data)
+{
+ assert(func != NULL);
+ assert(uri_regex != NULL);
+ mg_bind(ctx, uri_regex, -1, func, TRUE, user_data);
+}
+
+static int
+not_modified(const struct mg_connection *conn, const struct stat *stp)
+{
+ const char *ims = mg_get_header(conn, "If-Modified-Since");
+ return (ims != NULL && stp->st_mtime < date_to_epoch(ims));
+}
+
+static bool_t
+append_chunk(struct mg_request_info *ri, int fd, const char *buf, int len)
+{
+ bool_t ret_code = TRUE;
+
+ if (fd == -1) {
+ /* TODO: check for NULL here */
+ ri->post_data = (char *) realloc(ri->post_data,
+ ri->post_data_len + len);
+ (void) memcpy(ri->post_data + ri->post_data_len, buf, len);
+ ri->post_data_len += len;
+ } else if (push(fd, INVALID_SOCKET,
+ NULL, buf, (uint64_t) len) != (uint64_t) len) {
+ ret_code = FALSE;
+ }
+
+ return (ret_code);
+}
+
+static bool_t
+handle_request_body(struct mg_connection *conn, int fd)
+{
+ struct mg_request_info *ri = &conn->request_info;
+ const char *expect, *tmp;
+ uint64_t content_len;
+ char buf[BUFSIZ];
+ int to_read, nread, already_read;
+ bool_t success_code = FALSE;
+
+ content_len = get_content_length(conn);
+ expect = mg_get_header(conn, "Expect");
+
+ if (content_len == UNKNOWN_CONTENT_LENGTH) {
+ send_error(conn, 411, "Length Required", "");
+ } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
+ send_error(conn, 417, "Expectation Failed", "");
+ } else {
+ if (expect != NULL)
+ (void) mg_printf(conn, "HTTP/1.1 100 Continue\r\n\r\n");
+
+ already_read = ri->post_data_len;
+ assert(already_read >= 0);
+
+ if (content_len <= (uint64_t) already_read) {
+ ri->post_data_len = (int) content_len;
+ /*
+ * If fd == -1, this is embedded mode, and we do not
+ * have to do anything: POST data is already there,
+ * no need to allocate a buffer and copy it in.
+ * If fd != -1, we need to write the data.
+ */
+ success_code = (fd == -1) || (push(fd, INVALID_SOCKET,
+ NULL, ri->post_data, content_len) == content_len) ?
+ TRUE : FALSE;
+ } else {
+
+ if (fd == -1) {
+ conn->free_post_data = TRUE;
+ tmp = ri->post_data;
+ /* +1 in case if already_read == 0 */
+ ri->post_data = (char*)malloc(already_read + 1);
+ (void) memcpy(ri->post_data, tmp, already_read);
+ } else {
+ (void) push(fd, INVALID_SOCKET, NULL,
+ ri->post_data, (uint64_t) already_read);
+ }
+
+ content_len -= already_read;
+
+ while (content_len > 0) {
+ to_read = sizeof(buf);
+ if ((uint64_t) to_read > content_len)
+ to_read = (int) content_len;
+ nread = pull(-1, conn->sock,
+ conn->ssl, buf, to_read);
+ if (nread <= 0)
+ break;
+ if (!append_chunk(ri, fd, buf, nread))
+ break;
+ content_len -= nread;
+ }
+ success_code = content_len == 0 ? TRUE : FALSE;
+ }
+
+ /* Each error code path in this function must send an error */
+ if (success_code != TRUE)
+ send_error(conn, 577, http_500_error,
+ "%s", "Error handling body data");
+ }
+
+ return (success_code);
+}
+
+#if !defined(NO_CGI)
+struct cgi_env_block {
+ char buf[CGI_ENVIRONMENT_SIZE]; /* Environment buffer */
+ int len; /* Space taken */
+ char *vars[MAX_CGI_ENVIR_VARS]; /* char **envp */
+ int nvars; /* Number of variables */
+};
+
+static char *
+addenv(struct cgi_env_block *block, const char *fmt, ...)
+{
+ int n, space;
+ char *added;
+ va_list ap;
+
+ space = sizeof(block->buf) - block->len - 2;
+ assert(space >= 0);
+ added = block->buf + block->len;
+
+ va_start(ap, fmt);
+ n = mg_vsnprintf(added, (size_t) space, fmt, ap);
+ va_end(ap);
+
+ if (n > 0 && n < space &&
+ block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
+ block->vars[block->nvars++] = block->buf + block->len;
+ block->len += n + 1; /* Include \0 terminator */
+ }
+
+ return (added);
+}
+
+static void
+prepare_cgi_environment(struct mg_connection *conn, const char *prog,
+ struct cgi_env_block *blk)
+{
+ const char *s, *script_filename, *root;
+ char *p;
+ int i;
+
+ blk->len = blk->nvars = 0;
+
+ /* SCRIPT_FILENAME */
+ script_filename = prog;
+ if ((s = strrchr(prog, '/')) != NULL)
+ script_filename = s + 1;
+
+ mg_lock(conn->ctx);
+ root = conn->ctx->options[OPT_ROOT];
+ addenv(blk, "SERVER_NAME=%s", conn->ctx->options[OPT_AUTH_DOMAIN]);
+ mg_unlock(conn->ctx);
+
+ /* Prepare the environment block */
+ addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
+ addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
+ addenv(blk, "%s", "REDIRECT_STATUS=200"); /* PHP */
+ addenv(blk, "SERVER_PORT=%d", ntohs(conn->lsa.u.sin.sin_port));
+ addenv(blk, "SERVER_ROOT=%s", root);
+ addenv(blk, "DOCUMENT_ROOT=%s", root);
+ addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method);
+ addenv(blk, "REMOTE_ADDR=%s", inet_ntoa(conn->rsa.u.sin.sin_addr));
+ addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port);
+ addenv(blk, "REQUEST_URI=%s", conn->request_info.uri);
+ addenv(blk, "SCRIPT_NAME=%s", prog + strlen(root));
+ addenv(blk, "SCRIPT_FILENAME=%s", script_filename); /* PHP */
+ addenv(blk, "PATH_TRANSLATED=%s", prog);
+
+ if ((s = mg_get_header(conn, "Content-Type")) != NULL)
+ addenv(blk, "CONTENT_TYPE=%s", s);
+
+ if (conn->request_info.query_string != NULL)
+ addenv(blk, "QUERY_STRING=%s", conn->request_info.query_string);
+
+ if ((s = mg_get_header(conn, "Content-Length")) != NULL)
+ addenv(blk, "CONTENT_LENGTH=%s", s);
+
+ if ((s = getenv("PATH")) != NULL)
+ addenv(blk, "PATH=%s", s);
+
+#if defined(_WIN32)
+ if ((s = getenv("COMSPEC")) != NULL)
+ addenv(blk, "COMSPEC=%s", s);
+ if ((s = getenv("SYSTEMROOT")) != NULL)
+ addenv(blk, "SYSTEMROOT=%s", s);
+#else
+ if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
+ addenv(blk, "LD_LIBRARY_PATH=%s", s);
+#endif /* _WIN32 */
+
+ if ((s = getenv("PERLLIB")) != NULL)
+ addenv(blk, "PERLLIB=%s", s);
+
+ if (conn->request_info.remote_user != NULL) {
+ addenv(blk, "REMOTE_USER=%s", conn->request_info.remote_user);
+ addenv(blk, "%s", "AUTH_TYPE=Digest");
+ }
+
+ /* Add all headers as HTTP_* variables */
+ for (i = 0; i < conn->request_info.num_headers; i++) {
+ p = addenv(blk, "HTTP_%s=%s",
+ conn->request_info.http_headers[i].name,
+ conn->request_info.http_headers[i].value);
+
+ /* Convert variable name into uppercase, and change - to _ */
+ for (; *p != '=' && *p != '\0'; p++) {
+ if (*p == '-')
+ *p = '_';
+ *p = toupper(* (unsigned char *) p) & 0xff;
+ }
+ }
+
+ blk->vars[blk->nvars++] = NULL;
+ blk->buf[blk->len++] = '\0';
+
+ assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
+ assert(blk->len > 0);
+ assert(blk->len < (int) sizeof(blk->buf));
+}
+
+static void
+send_cgi(struct mg_connection *conn, const char *prog)
+{
+ int headers_len, data_len, i, n;
+ const char *status;
+ char buf[MAX_REQUEST_SIZE], *pbuf;
+ struct mg_request_info ri;
+ struct cgi_env_block blk;
+ char dir[FILENAME_MAX], *p;
+ int fd_stdin[2], fd_stdout[2];
+
+ prepare_cgi_environment(conn, prog, &blk);
+
+ /* CGI must be executed in its own directory */
+ (void) mg_snprintf(dir, sizeof(dir), "%s", prog);
+ if ((p = strrchr(dir, DIRSEP)) != NULL)
+ *p++ = '\0';
+
+ fd_stdin[0] = fd_stdin[1] = fd_stdout[0] = fd_stdout[1] = -1;
+ if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) {
+ send_error(conn, 500, http_500_error,
+ "Cannot create CGI pipe: %s", strerror(ERRNO));
+ goto done;
+ }
+
+ if (!spawn_process(conn, p, blk.buf, blk.vars,
+ fd_stdin[0], fd_stdout[1], dir)) {
+ goto done;
+ }
+
+ /*
+ * spawn_process() must close those!
+ * If we don't mark them as closed, close() attempt before
+ * return from this function throws an exception on Windows.
+ * Windows does not like when closed descriptor is closed again.
+ */
+ fd_stdin[0] = fd_stdout[1] = -1;
+
+ /* Send POST data to the CGI process if needed */
+ if (!strcmp(conn->request_info.request_method, "POST") &&
+ !handle_request_body(conn, fd_stdin[1])) {
+ goto done;
+ }
+
+ /*
+ * Now read CGI reply into a buffer. We need to set correct
+ * status code, thus we need to see all HTTP headers first.
+ * Do not send anything back to client, until we buffer in all
+ * HTTP headers.
+ */
+ data_len = 0;
+ headers_len = read_request(fd_stdout[0], INVALID_SOCKET, NULL,
+ buf, sizeof(buf), &data_len);
+ if (headers_len <= 0) {
+ send_error(conn, 500, http_500_error,
+ "CGI program sent malformed HTTP headers: [%.*s]",
+ data_len, buf);
+ goto done;
+ }
+ pbuf = buf;
+ buf[headers_len - 1] = '\0';
+ parse_http_headers(&pbuf, &ri);
+
+ /* Make up and send the status line */
+ status = get_header(&ri, "Status");
+ conn->request_info.status_code = status == NULL ? 200 : atoi(status);
+ (void) mg_printf(conn, "HTTP/1.1 %d OK\r\n",
+ conn->request_info.status_code);
+
+ /* Send headers */
+ for (i = 0; i < ri.num_headers; i++)
+ (void) mg_printf(conn, "%s: %s\r\n",
+ ri.http_headers[i].name,
+ ri.http_headers[i].value);
+ (void) mg_write(conn, "\r\n", 2);
+
+ /* Send headers, and the rest of the data */
+ conn->num_bytes_sent += mg_write(conn,
+ buf + headers_len, data_len - headers_len);
+ while ((n = pull(fd_stdout[0],
+ INVALID_SOCKET, NULL, buf, sizeof(buf))) > 0)
+ conn->num_bytes_sent += mg_write(conn, buf, n);
+
+done:
+ if (fd_stdin[0] != -1) (void) close(fd_stdin[0]);
+ if (fd_stdin[1] != -1) (void) close(fd_stdin[1]);
+ if (fd_stdout[0] != -1) (void) close(fd_stdout[0]);
+ if (fd_stdout[1] != -1) (void) close(fd_stdout[1]);
+}
+#endif /* !NO_CGI */
+
+#if !defined(NO_AUTH)
+/*
+ * For a given PUT path, create all intermediate subdirectories
+ * for given path. Return 0 if the path itself is a directory,
+ * or -1 on error, 1 if OK.
+ */
+static int
+put_dir(const char *path)
+{
+ char buf[FILENAME_MAX];
+ const char *s, *p;
+ struct stat st;
+ size_t len;
+
+ for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
+ len = p - path;
+ assert(len < sizeof(buf));
+ (void) memcpy(buf, path, len);
+ buf[len] = '\0';
+
+ /* Try to create intermediate directory */
+ if (mg_stat(buf, &st) == -1 && mg_mkdir(buf, 0755) != 0)
+ return (-1);
+
+ /* Is path itself a directory ? */
+ if (p[1] == '\0')
+ return (0);
+ }
+
+ return (1);
+}
+
+static void
+put_file(struct mg_connection *conn, const char *path)
+{
+ struct stat st;
+ int rc, fd;
+
+ conn->request_info.status_code = mg_stat(path, &st) == 0 ? 200 : 201;
+
+ if (mg_get_header(conn, "Range")) {
+ send_error(conn, 501, "Not Implemented",
+ "%s", "Range support for PUT requests is not implemented");
+ } else if ((rc = put_dir(path)) == 0) {
+ send_error(conn, 200, "OK", "");
+ } else if (rc == -1) {
+ send_error(conn, 500, http_500_error,
+ "put_dir(%s): %s", path, strerror(ERRNO));
+ } else if ((fd = mg_open(path,
+ O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0644)) == -1) {
+ send_error(conn, 500, http_500_error,
+ "open(%s): %s", path, strerror(ERRNO));
+ } else {
+ set_close_on_exec(fd);
+ if (handle_request_body(conn, fd))
+ send_error(conn, conn->request_info.status_code,
+ "OK", "");
+ (void) close(fd);
+ }
+}
+#endif /* NO_AUTH */
+
+#if !defined(NO_SSI)
+static void
+do_ssi_include(struct mg_connection *conn, const char *ssi, char *tag)
+{
+ char file_name[BUFSIZ], path[FILENAME_MAX], *p;
+ FILE *fp;
+
+ /*
+ * sscanf() is safe here, since send_ssi_file() also uses buffer
+ * of size BUFSIZ to get the tag. So strlen(tag) is always < BUFSIZ.
+ */
+ if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
+ /* File name is relative to the webserver root */
+ mg_lock(conn->ctx);
+ (void) mg_snprintf(path, sizeof(path), "%s%c%s",
+ conn->ctx->options[OPT_ROOT], DIRSEP, file_name);
+ mg_unlock(conn->ctx);
+ } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) {
+ /*
+ * File name is relative to the webserver working directory
+ * or it is absolute system path
+ */
+ (void) mg_snprintf(path, sizeof(path), "%s", file_name);
+ } else if (sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
+ /* File name is relative to the currect document */
+ (void) mg_snprintf(path, sizeof(path), "%s", ssi);
+ if ((p = strrchr(path, DIRSEP)) != NULL)
+ p[1] = '\0';
+ (void) mg_snprintf(path + strlen(path),
+ sizeof(path) - strlen(path), "%s", file_name);
+ } else {
+ cry("Bad SSI #include: [%s]", tag);
+ return;
+ }
+
+ if ((fp = fopen(path, "rb")) == NULL) {
+ cry("Cannot open SSI #include: [%s]: fopen(%s): %s",
+ tag, path, strerror(ERRNO));
+ } else {
+ set_close_on_exec(fileno(fp));
+ send_opened_file_stream(conn, fileno(fp), ~0ULL);
+ (void) fclose(fp);
+ }
+}
+
+static void
+do_ssi_exec(struct mg_connection *conn, char *tag)
+{
+ char cmd[BUFSIZ];
+ FILE *fp;
+
+ if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
+ cry("Bad SSI #exec: [%s]", tag);
+ } else if ((fp = popen(cmd, "r")) == NULL) {
+ cry("Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
+ } else {
+ send_opened_file_stream(conn, fileno(fp), ~0ULL);
+ (void) pclose(fp);
+ }
+}
+
+static void
+send_ssi_file(struct mg_connection *conn, const char *path, FILE *fp)
+{
+ char buf[BUFSIZ];
+ int ch, len, in_ssi_tag;
+
+ in_ssi_tag = FALSE;
+ len = 0;
+
+ while ((ch = fgetc(fp)) != EOF) {
+ if (in_ssi_tag && ch == '>') {
+ in_ssi_tag = FALSE;
+ buf[len++] = ch & 0xff;
+ buf[len] = '\0';
+ assert(len <= (int) sizeof(buf));
+ if (len < 6 || memcmp(buf, "".
- * That means that when do_command() is called, we can rely
- * on that full command with arguments is buffered in and
- * there is no need for streaming.
- * Restrictions:
- * 1. The command must fit in CMDBUFSIZ
- * 2. HTML comments inside the command ? Not sure about this.
- */
- case SSI_BUF:
- if (inc->nbuf >= sizeof(inc->buf) - 1) {
- pass(inc, buf + n, &n);
- } else if (ch == '>' &&
- !memcmp(inc->buf + inc->nbuf - 2, "--", 2)) {
- do_command(ssi, buf + n, len - n, &n);
- inc = ssi->incs + ssi->nest;
- } else {
- inc->buf[inc->nbuf++] = ch;
-
- /* If not SSI tag, pass it */
- if (inc->nbuf <= (size_t) st.len &&
- memcmp(inc->buf, st.ptr, inc->nbuf) != 0)
- pass(inc, buf + n, &n);
- }
- break;
-
- case SSI_EXEC:
- case SSI_CALL:
- break;
-
- default:
- /* Never happens */
- abort();
- break;
- }
-
- if (ssi->nest > 0 && n + inc->nbuf < len && ch == EOF) {
- (void) fclose(inc->fp);
- inc->fp = NULL;
- ssi->nest--;
- inc--;
- goto again;
- }
-
- return (n);
-}
-
-static void
-close_ssi(struct stream *stream)
-{
- struct ssi *ssi = stream->conn->ssi;
- size_t i;
-
- for (i = 0; i < NELEMS(ssi->incs); i++) {
- if (ssi->incs[i].fp != NULL)
- (void) fclose(ssi->incs[i].fp);
- if (ssi->incs[i].pipe != NULL)
- (void) pclose(ssi->incs[i].pipe);
- }
-
- free(ssi);
-}
-
-void
-_shttpd_do_ssi(struct conn *c)
-{
- char date[64];
- struct ssi *ssi;
-
- (void) strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT",
- localtime(&_shttpd_current_time));
-
- c->loc.io.head = c->loc.headers_len = _shttpd_snprintf(c->loc.io.buf,
- c->loc.io.size,
- "HTTP/1.1 200 OK\r\n"
- "Date: %s\r\n"
- "Content-Type: text/html\r\n"
- "Connection: close\r\n\r\n",
- date);
-
- c->status = 200;
- c->loc.io_class = &_shttpd_io_ssi;
- c->loc.flags |= FLAG_R | FLAG_ALWAYS_READY;
-
- if (c->method == METHOD_HEAD) {
- _shttpd_stop_stream(&c->loc);
- } else if ((ssi = calloc(1, sizeof(struct ssi))) == NULL) {
- _shttpd_send_server_error(c, 500,
- "Cannot allocate SSI descriptor");
- } else {
- ssi->incs[0].fp = fdopen(c->loc.chan.fd, "r");
- ssi->conn = c;
- c->ssi = ssi;
- }
-}
-
-const struct io_class _shttpd_io_ssi = {
- "ssi",
- read_ssi,
- NULL,
- close_ssi
-};
-
-#endif /* !NO_SSI */
diff --git a/vendor/shttpd/io_ssl.c b/vendor/shttpd/io_ssl.c
deleted file mode 100644
index 6de0db2..0000000
--- a/vendor/shttpd/io_ssl.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#include "defs.h"
-
-#if !defined(NO_SSL)
-struct ssl_func ssl_sw[] = {
- {"SSL_free", {0}},
- {"SSL_accept", {0}},
- {"SSL_connect", {0}},
- {"SSL_read", {0}},
- {"SSL_write", {0}},
- {"SSL_get_error", {0}},
- {"SSL_set_fd", {0}},
- {"SSL_new", {0}},
- {"SSL_CTX_new", {0}},
- {"SSLv23_server_method", {0}},
- {"SSL_library_init", {0}},
- {"SSL_CTX_use_PrivateKey_file", {0}},
- {"SSL_CTX_use_certificate_file",{0}},
- {NULL, {0}}
-};
-
-void
-_shttpd_ssl_handshake(struct stream *stream)
-{
- int n;
-
- if ((n = SSL_accept(stream->chan.ssl.ssl)) == 1) {
- DBG(("handshake: SSL accepted"));
- stream->flags |= FLAG_SSL_ACCEPTED;
- } else {
- n = SSL_get_error(stream->chan.ssl.ssl, n);
- if (n != SSL_ERROR_WANT_READ && n != SSL_ERROR_WANT_WRITE)
- stream->flags |= FLAG_CLOSED;
- DBG(("SSL_accept error %d", n));
- }
-}
-
-static int
-read_ssl(struct stream *stream, void *buf, size_t len)
-{
- int nread = -1;
-
- assert(stream->chan.ssl.ssl != NULL);
-
- if (!(stream->flags & FLAG_SSL_ACCEPTED))
- _shttpd_ssl_handshake(stream);
-
- if (stream->flags & FLAG_SSL_ACCEPTED)
- nread = SSL_read(stream->chan.ssl.ssl, buf, len);
-
- return (nread);
-}
-
-static int
-write_ssl(struct stream *stream, const void *buf, size_t len)
-{
- assert(stream->chan.ssl.ssl != NULL);
- return (SSL_write(stream->chan.ssl.ssl, buf, len));
-}
-
-static void
-close_ssl(struct stream *stream)
-{
- assert(stream->chan.ssl.sock != -1);
- assert(stream->chan.ssl.ssl != NULL);
- (void) closesocket(stream->chan.ssl.sock);
- SSL_free(stream->chan.ssl.ssl);
-}
-
-const struct io_class _shttpd_io_ssl = {
- "ssl",
- read_ssl,
- write_ssl,
- close_ssl
-};
-#endif /* !NO_SSL */
diff --git a/vendor/shttpd/llist.h b/vendor/shttpd/llist.h
deleted file mode 100644
index 04e79bb..0000000
--- a/vendor/shttpd/llist.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#ifndef LLIST_HEADER_INCLUDED
-#define LLIST_HEADER_INCLUDED
-
-/*
- * Linked list macros.
- */
-struct llhead {
- struct llhead *prev;
- struct llhead *next;
-};
-
-#define LL_INIT(N) ((N)->next = (N)->prev = (N))
-
-#define LL_HEAD(H) struct llhead H = { &H, &H }
-
-#define LL_ENTRY(P,T,N) ((T *)((char *)(P) - offsetof(T, N)))
-
-#define LL_ADD(H, N) \
- do { \
- ((H)->next)->prev = (N); \
- (N)->next = ((H)->next); \
- (N)->prev = (H); \
- (H)->next = (N); \
- } while (0)
-
-#define LL_TAIL(H, N) \
- do { \
- ((H)->prev)->next = (N); \
- (N)->prev = ((H)->prev); \
- (N)->next = (H); \
- (H)->prev = (N); \
- } while (0)
-
-#define LL_DEL(N) \
- do { \
- ((N)->next)->prev = ((N)->prev); \
- ((N)->prev)->next = ((N)->next); \
- LL_INIT(N); \
- } while (0)
-
-#define LL_EMPTY(N) ((N)->next == (N))
-
-#define LL_FOREACH(H,N) for (N = (H)->next; N != (H); N = (N)->next)
-
-#define LL_FOREACH_SAFE(H,N,T) \
- for (N = (H)->next, T = (N)->next; N != (H); \
- N = (T), T = (N)->next)
-
-#endif /* LLIST_HEADER_INCLUDED */
diff --git a/vendor/shttpd/log.c b/vendor/shttpd/log.c
deleted file mode 100644
index 8c506f4..0000000
--- a/vendor/shttpd/log.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#include "defs.h"
-
-/*
- * Log function
- */
-void
-_shttpd_elog(int flags, struct conn *c, const char *fmt, ...)
-{
- char date[64], buf[URI_MAX];
- int len;
- FILE *fp = c == NULL ? NULL : c->ctx->error_log;
- va_list ap;
-
- /* Print to stderr */
- if (c == NULL || !IS_TRUE(c->ctx, OPT_INETD)) {
- va_start(ap, fmt);
- (void) vfprintf(stderr, fmt, ap);
- (void) fputc('\n', stderr);
- va_end(ap);
- }
-
- strftime(date, sizeof(date), "%a %b %d %H:%M:%S %Y",
- localtime(&_shttpd_current_time));
-
- len = _shttpd_snprintf(buf, sizeof(buf),
- "[%s] [error] [client %s] \"%s\" ",
- date, c ? inet_ntoa(c->sa.u.sin.sin_addr) : "-",
- c && c->request ? c->request : "-");
-
- va_start(ap, fmt);
- (void) vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
- va_end(ap);
-
- buf[sizeof(buf) - 1] = '\0';
-
- if (fp != NULL && (flags & (E_FATAL | E_LOG))) {
- (void) fprintf(fp, "%s\n", buf);
- (void) fflush(fp);
- }
-
- if (flags & E_FATAL)
- exit(EXIT_FAILURE);
-}
-
-void
-_shttpd_log_access(FILE *fp, const struct conn *c)
-{
- static const struct vec dash = {"-", 1};
-
- const struct vec *user = &c->ch.user.v_vec;
- const struct vec *referer = &c->ch.referer.v_vec;
- const struct vec *user_agent = &c->ch.useragent.v_vec;
- char date[64], buf[URI_MAX], *q1 = "\"", *q2 = "\"";
-
- if (user->len == 0)
- user = ‐
-
- if (referer->len == 0) {
- referer = ‐
- q1 = "";
- }
-
- if (user_agent->len == 0) {
- user_agent = ‐
- q2 = "";
- }
-
- (void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S",
- localtime(&c->birth_time));
-
- (void) _shttpd_snprintf(buf, sizeof(buf),
- "%s - %.*s [%s %+05d] \"%s\" %d %lu %s%.*s%s %s%.*s%s",
- inet_ntoa(c->sa.u.sin.sin_addr), user->len, user->ptr,
- date, _shttpd_tz_offset, c->request ? c->request : "-",
- c->status, (unsigned long) c->loc.io.total,
- q1, referer->len, referer->ptr, q1,
- q2, user_agent->len, user_agent->ptr, q2);
-
- if (fp != NULL) {
- (void) fprintf(fp, "%s\n", buf);
- (void) fflush(fp);
- }
-}
diff --git a/vendor/shttpd/md5.c b/vendor/shttpd/md5.c
deleted file mode 100644
index ddd90e1..0000000
--- a/vendor/shttpd/md5.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest. This code was
- * written by Colin Plumb in 1993, no copyright is claimed.
- * This code is in the public domain; do with it what you wish.
- *
- * Equivalent code is available from RSA Data Security, Inc.
- * This code has been tested against that, and is equivalent,
- * except that you don't need to include two pages of legalese
- * with every copy.
- *
- * To compute the message digest of a chunk of bytes, declare an
- * MD5Context structure, pass it to MD5Init, call MD5Update as
- * needed on buffers full of bytes, and then call MD5Final, which
- * will fill a supplied 16-byte array with the digest.
- */
-
-#include "defs.h"
-
-#ifndef HAVE_MD5
-#if __BYTE_ORDER == 1234
-#define byteReverse(buf, len) /* Nothing */
-#else
-/*
- * Note: this code is harmless on little-endian machines.
- */
-static void byteReverse(unsigned char *buf, unsigned longs)
-{
- uint32_t t;
- do {
- t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
- ((unsigned) buf[1] << 8 | buf[0]);
- *(uint32_t *) buf = t;
- buf += 4;
- } while (--longs);
-}
-#endif /* __BYTE_ORDER */
-
-/* The four core functions - F1 is optimized somewhat */
-
-/* #define F1(x, y, z) (x & y | ~x & z) */
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-/* This is the central step in the MD5 algorithm. */
-#define MD5STEP(f, w, x, y, z, data, s) \
-( w += f(x, y, z) + data, w = w<>(32-s), w += x )
-
-/*
- * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
- * initialization constants.
- */
-void MD5Init(MD5_CTX *ctx)
-{
- ctx->buf[0] = 0x67452301;
- ctx->buf[1] = 0xefcdab89;
- ctx->buf[2] = 0x98badcfe;
- ctx->buf[3] = 0x10325476;
-
- ctx->bits[0] = 0;
- ctx->bits[1] = 0;
-}
-
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data. MD5Update blocks
- * the data and converts bytes into longwords for this routine.
- */
-static void MD5Transform(uint32_t buf[4], uint32_t const in[16])
-{
- register uint32_t a, b, c, d;
-
- a = buf[0];
- b = buf[1];
- c = buf[2];
- d = buf[3];
-
- MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
- MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
- MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
- MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
- MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
- MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
- MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
- MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
- MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
- MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
- MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
- MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
- MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
- MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
- MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
- MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
- MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
- MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
- MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
- MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
- MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
- MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
- MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
- MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
- MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
- MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
- MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
- MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
- MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
- MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
- MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
- MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
- MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
- MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
- MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
- MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
- MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
- MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
- MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
- MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
- MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
- MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
- MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
- MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
- MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
- MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
- MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
- MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
- MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
- MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
- MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
- MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
- MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
- MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
- MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
- MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
- MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
- MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
- MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
- MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
- MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
- MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
- MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
- MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
-}
-
-/*
- * Update context to reflect the concatenation of another buffer full
- * of bytes.
- */
-void
-MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)
-{
- uint32_t t;
-
- /* Update bitcount */
-
- t = ctx->bits[0];
- if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
- ctx->bits[1]++; /* Carry from low to high */
- ctx->bits[1] += len >> 29;
-
- t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
-
- /* Handle any leading odd-sized chunks */
-
- if (t) {
- unsigned char *p = (unsigned char *) ctx->in + t;
-
- t = 64 - t;
- if (len < t) {
- memcpy(p, buf, len);
- return;
- }
- memcpy(p, buf, t);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (uint32_t *) ctx->in);
- buf += t;
- len -= t;
- }
- /* Process data in 64-byte chunks */
-
- while (len >= 64) {
- memcpy(ctx->in, buf, 64);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (uint32_t *) ctx->in);
- buf += 64;
- len -= 64;
- }
-
- /* Handle any remaining bytes of data. */
-
- memcpy(ctx->in, buf, len);
-}
-
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern
- * 1 0* (64-bit count of bits processed, MSB-first)
- */
-void
-MD5Final(unsigned char digest[16], MD5_CTX *ctx)
-{
- unsigned count;
- unsigned char *p;
-
- /* Compute number of bytes mod 64 */
- count = (ctx->bits[0] >> 3) & 0x3F;
-
- /* Set the first char of padding to 0x80. This is safe since there is
- always at least one byte free */
- p = ctx->in + count;
- *p++ = 0x80;
-
- /* Bytes of padding needed to make 64 bytes */
- count = 64 - 1 - count;
-
- /* Pad out to 56 mod 64 */
- if (count < 8) {
- /* Two lots of padding: Pad the first block to 64 bytes */
- memset(p, 0, count);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (uint32_t *) ctx->in);
-
- /* Now fill the next block with 56 bytes */
- memset(ctx->in, 0, 56);
- } else {
- /* Pad block to 56 bytes */
- memset(p, 0, count - 8);
- }
- byteReverse(ctx->in, 14);
-
- /* Append length in bits and transform */
- ((uint32_t *) ctx->in)[14] = ctx->bits[0];
- ((uint32_t *) ctx->in)[15] = ctx->bits[1];
-
- MD5Transform(ctx->buf, (uint32_t *) ctx->in);
- byteReverse((unsigned char *) ctx->buf, 4);
- memcpy(digest, ctx->buf, 16);
- memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */
-}
-
-#endif /* !HAVE_MD5 */
diff --git a/vendor/shttpd/md5.h b/vendor/shttpd/md5.h
deleted file mode 100644
index fcca00e..0000000
--- a/vendor/shttpd/md5.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#ifndef MD5_HEADER_INCLUDED
-#define MD5_HEADER_INCLUDED
-
-typedef struct MD5Context {
- uint32_t buf[4];
- uint32_t bits[2];
- unsigned char in[64];
-} MD5_CTX;
-
-extern void MD5Init(MD5_CTX *ctx);
-extern void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len);
-extern void MD5Final(unsigned char digest[16], MD5_CTX *ctx);
-
-#endif /*MD5_HEADER_INCLUDED */
diff --git a/vendor/shttpd/shttpd.c b/vendor/shttpd/shttpd.c
deleted file mode 100644
index 8b66243..0000000
--- a/vendor/shttpd/shttpd.c
+++ /dev/null
@@ -1,1903 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-/*
- * Small and portable HTTP server, http://shttpd.sourceforge.net
- * $Id: shttpd.c,v 1.57 2008/08/23 21:00:38 drozd Exp $
- */
-
-#include "defs.h"
-
-time_t _shttpd_current_time; /* Current UTC time */
-int _shttpd_tz_offset; /* Time zone offset from UTC */
-int _shttpd_exit_flag; /* Program exit flag */
-
-const struct vec _shttpd_known_http_methods[] = {
- {"GET", 3},
- {"POST", 4},
- {"PUT", 3},
- {"DELETE", 6},
- {"HEAD", 4},
- {NULL, 0}
-};
-
-/*
- * This structure tells how HTTP headers must be parsed.
- * Used by parse_headers() function.
- */
-#define OFFSET(x) offsetof(struct headers, x)
-static const struct http_header http_headers[] = {
- {16, HDR_INT, OFFSET(cl), "Content-Length: " },
- {14, HDR_STRING, OFFSET(ct), "Content-Type: " },
- {12, HDR_STRING, OFFSET(useragent), "User-Agent: " },
- {19, HDR_DATE, OFFSET(ims), "If-Modified-Since: " },
- {15, HDR_STRING, OFFSET(auth), "Authorization: " },
- {9, HDR_STRING, OFFSET(referer), "Referer: " },
- {8, HDR_STRING, OFFSET(cookie), "Cookie: " },
- {10, HDR_STRING, OFFSET(location), "Location: " },
- {8, HDR_INT, OFFSET(status), "Status: " },
- {7, HDR_STRING, OFFSET(range), "Range: " },
- {12, HDR_STRING, OFFSET(connection), "Connection: " },
- {19, HDR_STRING, OFFSET(transenc), "Transfer-Encoding: " },
- {0, HDR_INT, 0, NULL }
-};
-
-struct shttpd_ctx *init_ctx(const char *config_file, int argc, char *argv[]);
-static void process_connection(struct conn *, int, int);
-
-int
-_shttpd_is_true(const char *str)
-{
- static const char *trues[] = {"1", "yes", "true", "jawohl", NULL};
- const char **p;
-
- for (p = trues; *p != NULL; p++)
- if (str && !strcmp(str, *p))
- return (TRUE);
-
- return (FALSE);
-}
-
-static void
-free_list(struct llhead *head, void (*dtor)(struct llhead *))
-{
- struct llhead *lp, *tmp;
-
- LL_FOREACH_SAFE(head, lp, tmp) {
- LL_DEL(lp);
- dtor(lp);
- }
-}
-
-static void
-listener_destructor(struct llhead *lp)
-{
- struct listener *listener = LL_ENTRY(lp, struct listener, link);
-
- (void) closesocket(listener->sock);
- free(listener);
-}
-
-static void
-registered_uri_destructor(struct llhead *lp)
-{
- struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
-
- free((void *) ruri->uri);
- free(ruri);
-}
-
-static void
-acl_destructor(struct llhead *lp)
-{
- struct acl *acl = LL_ENTRY(lp, struct acl, link);
- free(acl);
-}
-
-int
-_shttpd_url_decode(const char *src, int src_len, char *dst, int dst_len)
-{
- int i, j, a, b;
-#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
-
- for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++)
- switch (src[i]) {
- case '%':
- if (isxdigit(((unsigned char *) src)[i + 1]) &&
- isxdigit(((unsigned char *) src)[i + 2])) {
- a = tolower(((unsigned char *)src)[i + 1]);
- b = tolower(((unsigned char *)src)[i + 2]);
- dst[j] = (HEXTOI(a) << 4) | HEXTOI(b);
- i += 2;
- } else {
- dst[j] = '%';
- }
- break;
- default:
- dst[j] = src[i];
- break;
- }
-
- dst[j] = '\0'; /* Null-terminate the destination */
-
- return (j);
-}
-
-static const char *
-is_alias(struct shttpd_ctx *ctx, const char *uri,
- struct vec *a_uri, struct vec *a_path)
-{
- const char *p, *s = ctx->options[OPT_ALIASES];
- size_t len;
-
- DBG(("is_alias: aliases [%s]", s == NULL ? "" : s));
-
- FOR_EACH_WORD_IN_LIST(s, len) {
-
- if ((p = memchr(s, '=', len)) == NULL || p >= s + len || p == s)
- continue;
-
- if (memcmp(uri, s, p - s) == 0) {
- a_uri->ptr = s;
- a_uri->len = p - s;
- a_path->ptr = ++p;
- a_path->len = (s + len) - p;
- return (s);
- }
- }
-
- return (NULL);
-}
-
-void
-_shttpd_stop_stream(struct stream *stream)
-{
- if (stream->io_class != NULL && stream->io_class->close != NULL)
- stream->io_class->close(stream);
-
- stream->io_class= NULL;
- stream->flags |= FLAG_CLOSED;
- stream->flags &= ~(FLAG_R | FLAG_W | FLAG_ALWAYS_READY);
-
- DBG(("%d %s stopped. %lu of content data, %d now in a buffer",
- stream->conn->rem.chan.sock,
- stream->io_class ? stream->io_class->name : "(null)",
- (unsigned long) stream->io.total, (int) io_data_len(&stream->io)));
-}
-
-/*
- * Setup listening socket on given port, return socket
- */
-static int
-shttpd_open_listening_port(int port)
-{
- int sock, on = 1;
- struct usa sa;
-
-#ifdef _WIN32
- {WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
-#endif /* _WIN32 */
-
- sa.len = sizeof(sa.u.sin);
- sa.u.sin.sin_family = AF_INET;
- sa.u.sin.sin_port = htons((uint16_t) port);
- sa.u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
-
- if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
- goto fail;
- if (_shttpd_set_non_blocking_mode(sock) != 0)
- goto fail;
- if (setsockopt(sock, SOL_SOCKET,
- SO_REUSEADDR,(char *) &on, sizeof(on)) != 0)
- goto fail;
- if (bind(sock, &sa.u.sa, sa.len) < 0)
- goto fail;
- if (listen(sock, 128) != 0)
- goto fail;
-
-#ifndef _WIN32
- (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
-#endif /* !_WIN32 */
-
- return (sock);
-fail:
- if (sock != -1)
- (void) closesocket(sock);
- _shttpd_elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
- return (-1);
-}
-
-/*
- * Check whether full request is buffered Return headers length, or 0
- */
-int
-_shttpd_get_headers_len(const char *buf, size_t buflen)
-{
- const char *s, *e;
- int len = 0;
-
- for (s = buf, e = s + buflen - 1; len == 0 && s < e; s++)
- /* Control characters are not allowed but >=128 is. */
- if (!isprint(* (unsigned char *) s) && *s != '\r' &&
- *s != '\n' && * (unsigned char *) s < 128)
- len = -1;
- else if (s[0] == '\n' && s[1] == '\n')
- len = s - buf + 2;
- else if (s[0] == '\n' && &s[1] < e &&
- s[1] == '\r' && s[2] == '\n')
- len = s - buf + 3;
-
- return (len);
-}
-
-/*
- * Send error message back to a client.
- */
-void
-_shttpd_send_server_error(struct conn *c, int status, const char *reason)
-{
- struct llhead *lp;
- struct error_handler *e;
-
- LL_FOREACH(&c->ctx->error_handlers, lp) {
- e = LL_ENTRY(lp, struct error_handler, link);
-
- if (e->code == status) {
- if (c->loc.io_class != NULL &&
- c->loc.io_class->close != NULL)
- c->loc.io_class->close(&c->loc);
- io_clear(&c->loc.io);
- _shttpd_setup_embedded_stream(c,
- e->callback, e->callback_data);
- return;
- }
- }
-
- io_clear(&c->loc.io);
- c->loc.io.head = _shttpd_snprintf(c->loc.io.buf, c->loc.io.size,
- "HTTP/1.1 %d %s\r\n"
- "Content-Type: text/plain\r\n"
- "Content-Length: 12\r\n"
- "\r\n"
- "Error: %03d\r\n",
- status, reason, status);
- c->loc.content_len = 10;
- c->status = status;
- _shttpd_stop_stream(&c->loc);
-}
-
-/*
- * Convert month to the month number. Return -1 on error, or month number
- */
-static int
-montoi(const char *s)
-{
- static const char *ar[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
- };
- size_t i;
-
- for (i = 0; i < sizeof(ar) / sizeof(ar[0]); i++)
- if (!strcmp(s, ar[i]))
- return (i);
-
- return (-1);
-}
-
-/*
- * Parse date-time string, and return the corresponding time_t value
- */
-static time_t
-date_to_epoch(const char *s)
-{
- struct tm tm, *tmp;
- char mon[32];
- int sec, min, hour, mday, month, year;
-
- (void) memset(&tm, 0, sizeof(tm));
- sec = min = hour = mday = month = year = 0;
-
- if (((sscanf(s, "%d/%3s/%d %d:%d:%d",
- &mday, mon, &year, &hour, &min, &sec) == 6) ||
- (sscanf(s, "%d %3s %d %d:%d:%d",
- &mday, mon, &year, &hour, &min, &sec) == 6) ||
- (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",
- &mday, mon, &year, &hour, &min, &sec) == 6) ||
- (sscanf(s, "%d-%3s-%d %d:%d:%d",
- &mday, mon, &year, &hour, &min, &sec) == 6)) &&
- (month = montoi(mon)) != -1) {
- tm.tm_mday = mday;
- tm.tm_mon = month;
- tm.tm_year = year;
- tm.tm_hour = hour;
- tm.tm_min = min;
- tm.tm_sec = sec;
- }
-
- if (tm.tm_year > 1900)
- tm.tm_year -= 1900;
- else if (tm.tm_year < 70)
- tm.tm_year += 100;
-
- /* Set Daylight Saving Time field */
- tmp = localtime(&_shttpd_current_time);
- tm.tm_isdst = tmp->tm_isdst;
-
- return (mktime(&tm));
-}
-
-static void
-remove_double_dots(char *s)
-{
- char *p = s;
-
- while (*s != '\0') {
- *p++ = *s++;
- if (s[-1] == '/' || s[-1] == '\\')
- while (*s == '.' || *s == '/' || *s == '\\')
- s++;
- }
- *p = '\0';
-}
-
-void
-_shttpd_parse_headers(const char *s, int len, struct headers *parsed)
-{
- const struct http_header *h;
- union variant *v;
- const char *p, *e = s + len;
-
- DBG(("parsing headers (len %d): [%.*s]", len, len, s));
-
- /* Loop through all headers in the request */
- while (s < e) {
-
- /* Find where this header ends */
- for (p = s; p < e && *p != '\n'; ) p++;
-
- /* Is this header known to us ? */
- for (h = http_headers; h->len != 0; h++)
- if (e - s > h->len &&
- !_shttpd_strncasecmp(s, h->name, h->len))
- break;
-
- /* If the header is known to us, store its value */
- if (h->len != 0) {
-
- /* Shift to where value starts */
- s += h->len;
-
- /* Find place to store the value */
- v = (union variant *) ((char *) parsed + h->offset);
-
- /* Fetch header value into the connection structure */
- if (h->type == HDR_STRING) {
- v->v_vec.ptr = s;
- v->v_vec.len = p - s;
- if (p[-1] == '\r' && v->v_vec.len > 0)
- v->v_vec.len--;
- } else if (h->type == HDR_INT) {
- v->v_big_int = strtoul(s, NULL, 10);
- } else if (h->type == HDR_DATE) {
- v->v_time = date_to_epoch(s);
- }
- }
-
- s = p + 1; /* Shift to the next header */
- }
-}
-
-static const struct {
- const char *extension;
- int ext_len;
- const char *mime_type;
-} builtin_mime_types[] = {
- {"html", 4, "text/html" },
- {"htm", 3, "text/html" },
- {"txt", 3, "text/plain" },
- {"css", 3, "text/css" },
- {"ico", 3, "image/x-icon" },
- {"gif", 3, "image/gif" },
- {"jpg", 3, "image/jpeg" },
- {"jpeg", 4, "image/jpeg" },
- {"png", 3, "image/png" },
- {"svg", 3, "image/svg+xml" },
- {"torrent", 7, "application/x-bittorrent" },
- {"wav", 3, "audio/x-wav" },
- {"mp3", 3, "audio/x-mp3" },
- {"mid", 3, "audio/mid" },
- {"m3u", 3, "audio/x-mpegurl" },
- {"ram", 3, "audio/x-pn-realaudio" },
- {"ra", 2, "audio/x-pn-realaudio" },
- {"doc", 3, "application/msword", },
- {"exe", 3, "application/octet-stream" },
- {"zip", 3, "application/x-zip-compressed" },
- {"xls", 3, "application/excel" },
- {"tgz", 3, "application/x-tar-gz" },
- {"tar.gz", 6, "application/x-tar-gz" },
- {"tar", 3, "application/x-tar" },
- {"gz", 2, "application/x-gunzip" },
- {"arj", 3, "application/x-arj-compressed" },
- {"rar", 3, "application/x-arj-compressed" },
- {"rtf", 3, "application/rtf" },
- {"pdf", 3, "application/pdf" },
- {"swf", 3, "application/x-shockwave-flash" },
- {"mpg", 3, "video/mpeg" },
- {"mpeg", 4, "video/mpeg" },
- {"asf", 3, "video/x-ms-asf" },
- {"avi", 3, "video/x-msvideo" },
- {"bmp", 3, "image/bmp" },
- {NULL, 0, NULL }
-};
-
-void
-_shttpd_get_mime_type(struct shttpd_ctx *ctx,
- const char *uri, int len, struct vec *vec)
-{
- const char *eq, *p = ctx->options[OPT_MIME_TYPES];
- int i, n, ext_len;
-
- /* Firt, loop through the custom mime types if any */
- FOR_EACH_WORD_IN_LIST(p, n) {
- if ((eq = memchr(p, '=', n)) == NULL || eq >= p + n || eq == p)
- continue;
- ext_len = eq - p;
- if (len > ext_len && uri[len - ext_len - 1] == '.' &&
- !_shttpd_strncasecmp(p, &uri[len - ext_len], ext_len)) {
- vec->ptr = eq + 1;
- vec->len = p + n - vec->ptr;
- return;
- }
- }
-
- /* If no luck, try built-in mime types */
- for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
- ext_len = builtin_mime_types[i].ext_len;
- if (len > ext_len && uri[len - ext_len - 1] == '.' &&
- !_shttpd_strncasecmp(builtin_mime_types[i].extension,
- &uri[len - ext_len], ext_len)) {
- vec->ptr = builtin_mime_types[i].mime_type;
- vec->len = strlen(vec->ptr);
- return;
- }
- }
-
- /* Oops. This extension is unknown to us. Fallback to text/plain */
- vec->ptr = "text/plain";
- vec->len = strlen(vec->ptr);
-}
-
-/*
- * For given directory path, substitute it to valid index file.
- * Return 0 if index file has been found, -1 if not found
- */
-static int
-find_index_file(struct conn *c, char *path, size_t maxpath, struct stat *stp)
-{
- char buf[FILENAME_MAX];
- const char *s = c->ctx->options[OPT_INDEX_FILES];
- size_t len;
-
- FOR_EACH_WORD_IN_LIST(s, len) {
- /* path must end with '/' character */
- _shttpd_snprintf(buf, sizeof(buf), "%s%.*s", path, len, s);
- if (_shttpd_stat(buf, stp) == 0) {
- _shttpd_strlcpy(path, buf, maxpath);
- _shttpd_get_mime_type(c->ctx, s, len, &c->mime_type);
- return (0);
- }
- }
-
- return (-1);
-}
-
-/*
- * Try to open requested file, return 0 if OK, -1 if error.
- * If the file is given arguments using PATH_INFO mechanism,
- * initialize pathinfo pointer.
- */
-static int
-get_path_info(struct conn *c, char *path, struct stat *stp)
-{
- char *p, *e;
-
- if (_shttpd_stat(path, stp) == 0)
- return (0);
-
- p = path + strlen(path);
- e = path + strlen(c->ctx->options[OPT_ROOT]) + 2;
-
- /* Strip directory parts of the path one by one */
- for (; p > e; p--)
- if (*p == '/') {
- *p = '\0';
- if (!_shttpd_stat(path, stp) && !S_ISDIR(stp->st_mode)) {
- c->path_info = p + 1;
- return (0);
- } else {
- *p = '/';
- }
- }
-
- return (-1);
-}
-
-static void
-decide_what_to_do(struct conn *c)
-{
- char path[URI_MAX], buf[1024], *root;
- struct vec alias_uri, alias_path;
- struct stat st;
- int rc;
- struct registered_uri *ruri;
-
- DBG(("decide_what_to_do: [%s]", c->uri));
-
- if ((c->query = strchr(c->uri, '?')) != NULL)
- *c->query++ = '\0';
-
- _shttpd_url_decode(c->uri, strlen(c->uri), c->uri, strlen(c->uri) + 1);
- remove_double_dots(c->uri);
-
- root = c->ctx->options[OPT_ROOT];
- if (strlen(c->uri) + strlen(root) >= sizeof(path)) {
- _shttpd_send_server_error(c, 400, "URI is too long");
- return;
- }
-
- (void) _shttpd_snprintf(path, sizeof(path), "%s%s", root, c->uri);
-
- /* User may use the aliases - check URI for mount point */
- if (is_alias(c->ctx, c->uri, &alias_uri, &alias_path) != NULL) {
- (void) _shttpd_snprintf(path, sizeof(path), "%.*s%s",
- alias_path.len, alias_path.ptr, c->uri + alias_uri.len);
- DBG(("using alias %.*s -> %.*s", alias_uri.len, alias_uri.ptr,
- alias_path.len, alias_path.ptr));
- }
-
-#if !defined(NO_AUTH)
- if (_shttpd_check_authorization(c, path) != 1) {
- _shttpd_send_authorization_request(c);
- } else
-#endif /* NO_AUTH */
- if ((ruri = _shttpd_is_registered_uri(c->ctx, c->uri)) != NULL) {
- _shttpd_setup_embedded_stream(c,
- ruri->callback, ruri->callback_data);
- } else
- if (strstr(path, HTPASSWD)) {
- /* Do not allow to view passwords files */
- _shttpd_send_server_error(c, 403, "Forbidden");
- } else
-#if !defined(NO_AUTH)
- if ((c->method == METHOD_PUT || c->method == METHOD_DELETE) &&
- (c->ctx->options[OPT_AUTH_PUT] == NULL ||
- !_shttpd_is_authorized_for_put(c))) {
- _shttpd_send_authorization_request(c);
- } else
-#endif /* NO_AUTH */
- if (c->method == METHOD_PUT) {
- c->status = _shttpd_stat(path, &st) == 0 ? 200 : 201;
-
- if (c->ch.range.v_vec.len > 0) {
- _shttpd_send_server_error(c, 501,
- "PUT Range Not Implemented");
- } else if ((rc = _shttpd_put_dir(path)) == 0) {
- _shttpd_send_server_error(c, 200, "OK");
- } else if (rc == -1) {
- _shttpd_send_server_error(c, 500, "PUT Directory Error");
- } else if (c->rem.content_len == 0) {
- _shttpd_send_server_error(c, 411, "Length Required");
- } else if ((c->loc.chan.fd = _shttpd_open(path, O_WRONLY | O_BINARY |
- O_CREAT | O_NONBLOCK | O_TRUNC, 0644)) == -1) {
- _shttpd_send_server_error(c, 500, "PUT Error");
- } else {
- DBG(("PUT file [%s]", c->uri));
- c->loc.io_class = &_shttpd_io_file;
- c->loc.flags |= FLAG_W | FLAG_ALWAYS_READY ;
- }
- } else if (c->method == METHOD_DELETE) {
- DBG(("DELETE [%s]", c->uri));
- if (_shttpd_remove(path) == 0)
- _shttpd_send_server_error(c, 200, "OK");
- else
- _shttpd_send_server_error(c, 500, "DELETE Error");
- } else if (get_path_info(c, path, &st) != 0) {
- _shttpd_send_server_error(c, 404, "Not Found");
- } else if (S_ISDIR(st.st_mode) && path[strlen(path) - 1] != '/') {
- (void) _shttpd_snprintf(buf, sizeof(buf),
- "Moved Permanently\r\nLocation: %s/", c->uri);
- _shttpd_send_server_error(c, 301, buf);
- } else if (S_ISDIR(st.st_mode) &&
- find_index_file(c, path, sizeof(path) - 1, &st) == -1 &&
- !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
- _shttpd_send_server_error(c, 403, "Directory Listing Denied");
- } else if (S_ISDIR(st.st_mode) && IS_TRUE(c->ctx, OPT_DIR_LIST)) {
- if ((c->loc.chan.dir.path = _shttpd_strdup(path)) != NULL)
- _shttpd_get_dir(c);
- else
- _shttpd_send_server_error(c, 500, "GET Directory Error");
- } else if (S_ISDIR(st.st_mode) && !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
- _shttpd_send_server_error(c, 403, "Directory listing denied");
-#if !defined(NO_CGI)
- } else if (_shttpd_match_extension(path,
- c->ctx->options[OPT_CGI_EXTENSIONS])) {
- if (c->method != METHOD_POST && c->method != METHOD_GET) {
- _shttpd_send_server_error(c, 501, "Bad method ");
- } else if ((_shttpd_run_cgi(c, path)) == -1) {
- _shttpd_send_server_error(c, 500, "Cannot exec CGI");
- } else {
- _shttpd_do_cgi(c);
- }
-#endif /* NO_CGI */
-#if !defined(NO_SSI)
- } else if (_shttpd_match_extension(path,
- c->ctx->options[OPT_SSI_EXTENSIONS])) {
- if ((c->loc.chan.fd = _shttpd_open(path,
- O_RDONLY | O_BINARY, 0644)) == -1) {
- _shttpd_send_server_error(c, 500, "SSI open error");
- } else {
- _shttpd_do_ssi(c);
- }
-#endif /* NO_CGI */
- } else if (c->ch.ims.v_time && st.st_mtime <= c->ch.ims.v_time) {
- _shttpd_send_server_error(c, 304, "Not Modified");
- } else if ((c->loc.chan.fd = _shttpd_open(path,
- O_RDONLY | O_BINARY, 0644)) != -1) {
- _shttpd_get_file(c, &st);
- } else {
- _shttpd_send_server_error(c, 500, "Internal Error");
- }
-}
-
-static int
-set_request_method(struct conn *c)
-{
- const struct vec *v;
-
- /* Set the request method */
- for (v = _shttpd_known_http_methods; v->ptr != NULL; v++)
- if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
- c->method = v - _shttpd_known_http_methods;
- break;
- }
-
- return (v->ptr == NULL);
-}
-
-static void
-parse_http_request(struct conn *c)
-{
- char *s, *e, *p, *start;
- int uri_len, req_len, n;
-
- s = io_data(&c->rem.io);;
- req_len = c->rem.headers_len =
- _shttpd_get_headers_len(s, io_data_len(&c->rem.io));
-
- if (req_len == 0 && io_space_len(&c->rem.io) == 0) {
- io_clear(&c->rem.io);
- _shttpd_send_server_error(c, 400, "Request is too big");
- }
-
- if (req_len == 0) {
- return;
- } else if (req_len < 16) { /* Minimal: "GET / HTTP/1.0\n\n" */
- _shttpd_send_server_error(c, 400, "Bad request");
- } else if (set_request_method(c)) {
- _shttpd_send_server_error(c, 501, "Method Not Implemented");
- } else if ((c->request = _shttpd_strndup(s, req_len)) == NULL) {
- _shttpd_send_server_error(c, 500, "Cannot allocate request");
- }
-
- if (c->loc.flags & FLAG_CLOSED)
- return;
-
- io_inc_tail(&c->rem.io, req_len);
-
- DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
- c->rem.flags |= FLAG_HEADERS_PARSED;
-
- /* Set headers pointer. Headers follow the request line */
- c->headers = memchr(c->request, '\n', req_len);
- assert(c->headers != NULL);
- assert(c->headers < c->request + req_len);
- if (c->headers > c->request && c->headers[-1] == '\r')
- c->headers[-1] = '\0';
- *c->headers++ = '\0';
-
- /*
- * Now make a copy of the URI, because it will be URL-decoded,
- * and we need a copy of unmodified URI for the access log.
- * First, we skip the REQUEST_METHOD and shift to the URI.
- */
- for (p = c->request, e = p + req_len; *p != ' ' && p < e; p++);
- while (p < e && *p == ' ')
- p++;
-
- /* Now remember where URI starts, and shift to the end of URI */
- for (start = p; p < e && !isspace((unsigned char)*p); ) p++;
- uri_len = p - start;
-
- /* Skip space following the URI */
- while (p < e && *p == ' ')
- p++;
-
- /* Now comes the HTTP-Version in the form HTTP/<major>.<minor> */
- if (sscanf(p, "HTTP/%lu.%lu%n",
- &c->major_version, &c->minor_version, &n) != 2 || p[n] != '\0') {
- _shttpd_send_server_error(c, 400, "Bad HTTP version");
- } else if (c->major_version > 1 ||
- (c->major_version == 1 && c->minor_version > 1)) {
- _shttpd_send_server_error(c, 505, "HTTP version not supported");
- } else if (uri_len <= 0) {
- _shttpd_send_server_error(c, 400, "Bad URI");
- } else if ((c->uri = malloc(uri_len + 1)) == NULL) {
- _shttpd_send_server_error(c, 500, "Cannot allocate URI");
- } else {
- _shttpd_strlcpy(c->uri, (char *) start, uri_len + 1);
- _shttpd_parse_headers(c->headers,
- (c->request + req_len) - c->headers, &c->ch);
-
- /* Remove the length of request from total, count only data */
- assert(c->rem.io.total >= (big_int_t) req_len);
- c->rem.io.total -= req_len;
- c->rem.content_len = c->ch.cl.v_big_int;
- decide_what_to_do(c);
- }
-}
-
-static void
-add_socket(struct worker *worker, int sock, int is_ssl)
-{
- struct shttpd_ctx *ctx = worker->ctx;
- struct conn *c;
- struct usa sa;
- int l = IS_TRUE(ctx, OPT_INETD) ? E_FATAL : E_LOG;
-#if !defined(NO_SSL)
- SSL *ssl = NULL;
-#else
- is_ssl = is_ssl; /* supress warnings */
-#endif /* NO_SSL */
-
- sa.len = sizeof(sa.u.sin);
- (void) _shttpd_set_non_blocking_mode(sock);
-
- if (getpeername(sock, &sa.u.sa, &sa.len)) {
- _shttpd_elog(l, NULL, "add_socket: %s", strerror(errno));
-#if !defined(NO_SSL)
- } else if (is_ssl && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
- _shttpd_elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
- (void) closesocket(sock);
- } else if (is_ssl && SSL_set_fd(ssl, sock) == 0) {
- _shttpd_elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
- (void) closesocket(sock);
- SSL_free(ssl);
-#endif /* NO_SSL */
- } else if ((c = calloc(1, sizeof(*c) + 2 * URI_MAX)) == NULL) {
-#if !defined(NO_SSL)
- if (ssl)
- SSL_free(ssl);
-#endif /* NO_SSL */
- (void) closesocket(sock);
- _shttpd_elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
- } else {
- c->rem.conn = c->loc.conn = c;
- c->ctx = ctx;
- c->worker = worker;
- c->sa = sa;
- c->birth_time = _shttpd_current_time;
- c->expire_time = _shttpd_current_time + EXPIRE_TIME;
-
- (void) getsockname(sock, &sa.u.sa, &sa.len);
- c->loc_port = sa.u.sin.sin_port;
-
- _shttpd_set_close_on_exec(sock);
-
- c->loc.io_class = NULL;
-
- c->rem.io_class = &_shttpd_io_socket;
- c->rem.chan.sock = sock;
-
- /* Set IO buffers */
- c->loc.io.buf = (char *) (c + 1);
- c->rem.io.buf = c->loc.io.buf + URI_MAX;
- c->loc.io.size = c->rem.io.size = URI_MAX;
-
-#if !defined(NO_SSL)
- if (is_ssl) {
- c->rem.io_class = &_shttpd_io_ssl;
- c->rem.chan.ssl.sock = sock;
- c->rem.chan.ssl.ssl = ssl;
- _shttpd_ssl_handshake(&c->rem);
- }
-#endif /* NO_SSL */
-
- LL_TAIL(&worker->connections, &c->link);
- worker->num_conns++;
-
- DBG(("%s:%hu connected (socket %d)",
- inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
- ntohs(sa.u.sin.sin_port), sock));
- }
-}
-
-static struct worker *
-first_worker(struct shttpd_ctx *ctx)
-{
- return (LL_ENTRY(ctx->workers.next, struct worker, link));
-}
-
-static void
-pass_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
-{
- struct llhead *lp;
- struct worker *worker, *lazy;
- int buf[3];
-
- lazy = first_worker(ctx);
-
- /* Find least busy worker */
- LL_FOREACH(&ctx->workers, lp) {
- worker = LL_ENTRY(lp, struct worker, link);
- if (worker->num_conns < lazy->num_conns)
- lazy = worker;
- }
-
- buf[0] = CTL_PASS_SOCKET;
- buf[1] = sock;
- buf[2] = is_ssl;
-
- (void) send(lazy->ctl[1], (void *) buf, sizeof(buf), 0);
-}
-
-static int
-set_ports(struct shttpd_ctx *ctx, const char *p)
-{
- int sock, len, is_ssl, port;
- struct listener *l;
-
-
- free_list(&ctx->listeners, &listener_destructor);
-
- FOR_EACH_WORD_IN_LIST(p, len) {
-
- is_ssl = p[len - 1] == 's' ? 1 : 0;
- port = atoi(p);
-
- if ((sock = shttpd_open_listening_port(port)) == -1) {
- _shttpd_elog(E_LOG, NULL, "cannot open port %d", port);
- goto fail;
- } else if (is_ssl && ctx->ssl_ctx == NULL) {
- (void) closesocket(sock);
- _shttpd_elog(E_LOG, NULL, "cannot add SSL socket, "
- "please specify certificate file");
- goto fail;
- } else if ((l = calloc(1, sizeof(*l))) == NULL) {
- (void) closesocket(sock);
- _shttpd_elog(E_LOG, NULL, "cannot allocate listener");
- goto fail;
- } else {
- l->is_ssl = is_ssl;
- l->sock = sock;
- l->ctx = ctx;
- LL_TAIL(&ctx->listeners, &l->link);
- DBG(("shttpd_listen: added socket %d", sock));
- }
- }
-
- return (TRUE);
-fail:
- free_list(&ctx->listeners, &listener_destructor);
- return (FALSE);
-}
-
-static void
-read_stream(struct stream *stream)
-{
- int n, len;
-
- len = io_space_len(&stream->io);
- assert(len > 0);
-
- /* Do not read more that needed */
- if (stream->content_len > 0 &&
- stream->io.total + len > stream->content_len)
- len = stream->content_len - stream->io.total;
-
- /* Read from underlying channel */
- assert(stream->io_class != NULL);
- n = stream->io_class->read(stream, io_space(&stream->io), len);
-
- if (n > 0)
- io_inc_head(&stream->io, n);
- else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
- n = n; /* Ignore EINTR and EAGAIN */
- else if (!(stream->flags & FLAG_DONT_CLOSE))
- _shttpd_stop_stream(stream);
-
- DBG(("read_stream (%d %s): read %d/%d/%lu bytes (errno %d)",
- stream->conn->rem.chan.sock,
- stream->io_class ? stream->io_class->name : "(null)",
- n, len, (unsigned long) stream->io.total, ERRNO));
-
- /*
- * Close the local stream if everything was read
- * XXX We do not close the remote stream though! It may be
- * a POST data completed transfer, we do not want the socket
- * to be closed.
- */
- if (stream->content_len > 0 && stream == &stream->conn->loc) {
- assert(stream->io.total <= stream->content_len);
- if (stream->io.total == stream->content_len)
- _shttpd_stop_stream(stream);
- }
-
- stream->conn->expire_time = _shttpd_current_time + EXPIRE_TIME;
-}
-
-static void
-write_stream(struct stream *from, struct stream *to)
-{
- int n, len;
-
- len = io_data_len(&from->io);
- assert(len > 0);
-
- /* TODO: should be assert on CAN_WRITE flag */
- n = to->io_class->write(to, io_data(&from->io), len);
- to->conn->expire_time = _shttpd_current_time + EXPIRE_TIME;
- DBG(("write_stream (%d %s): written %d/%d bytes (errno %d)",
- to->conn->rem.chan.sock,
- to->io_class ? to->io_class->name : "(null)", n, len, ERRNO));
-
- if (n > 0)
- io_inc_tail(&from->io, n);
- else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
- n = n; /* Ignore EINTR and EAGAIN */
- else if (!(to->flags & FLAG_DONT_CLOSE))
- _shttpd_stop_stream(to);
-}
-
-
-static void
-connection_desctructor(struct llhead *lp)
-{
- struct conn *c = LL_ENTRY(lp, struct conn, link);
- static const struct vec vec = {"close", 5};
- int do_close;
-
- DBG(("Disconnecting %d (%.*s)", c->rem.chan.sock,
- c->ch.connection.v_vec.len, c->ch.connection.v_vec.ptr));
-
- if (c->request != NULL && c->ctx->access_log != NULL)
- _shttpd_log_access(c->ctx->access_log, c);
-
- /* In inetd mode, exit if request is finished. */
- if (IS_TRUE(c->ctx, OPT_INETD))
- exit(0);
-
- if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
- c->loc.io_class->close(&c->loc);
-
- /*
- * Check the "Connection: " header before we free c->request
- * If it its 'keep-alive', then do not close the connection
- */
- do_close = (c->ch.connection.v_vec.len >= vec.len &&
- !_shttpd_strncasecmp(vec.ptr,c->ch.connection.v_vec.ptr,vec.len)) ||
- (c->major_version < 1 ||
- (c->major_version >= 1 && c->minor_version < 1));
-
- if (c->request)
- free(c->request);
- if (c->uri)
- free(c->uri);
-
- /* Keep the connection open only if we have Content-Length set */
- if (!do_close && c->loc.content_len > 0) {
- c->loc.io_class = NULL;
- c->loc.flags = 0;
- c->loc.content_len = 0;
- c->rem.flags = FLAG_W | FLAG_R | FLAG_SSL_ACCEPTED;
- c->query = c->request = c->uri = c->path_info = NULL;
- c->mime_type.len = 0;
- (void) memset(&c->ch, 0, sizeof(c->ch));
- io_clear(&c->loc.io);
- c->birth_time = _shttpd_current_time;
- if (io_data_len(&c->rem.io) > 0)
- process_connection(c, 0, 0);
- } else {
- if (c->rem.io_class != NULL)
- c->rem.io_class->close(&c->rem);
-
- LL_DEL(&c->link);
- c->worker->num_conns--;
- assert(c->worker->num_conns >= 0);
-
- free(c);
- }
-}
-
-static void
-worker_destructor(struct llhead *lp)
-{
- struct worker *worker = LL_ENTRY(lp, struct worker, link);
-
- free_list(&worker->connections, connection_desctructor);
- free(worker);
-}
-
-static int
-is_allowed(const struct shttpd_ctx *ctx, const struct usa *usa)
-{
- const struct acl *acl;
- const struct llhead *lp;
- int allowed = '+';
- uint32_t ip;
-
- LL_FOREACH(&ctx->acl, lp) {
- acl = LL_ENTRY(lp, struct acl, link);
- (void) memcpy(&ip, &usa->u.sin.sin_addr, sizeof(ip));
- if (acl->ip == (ntohl(ip) & acl->mask))
- allowed = acl->flag;
- }
-
- return (allowed == '+');
-}
-
-static void
-add_to_set(int fd, fd_set *set, int *max_fd)
-{
- FD_SET(fd, set);
- if (fd > *max_fd)
- *max_fd = fd;
-}
-
-static void
-process_connection(struct conn *c, int remote_ready, int local_ready)
-{
- /* Read from remote end if it is ready */
- if (remote_ready && io_space_len(&c->rem.io))
- read_stream(&c->rem);
-
- /* If the request is not parsed yet, do so */
- if (!(c->rem.flags & FLAG_HEADERS_PARSED))
- parse_http_request(c);
-
- DBG(("loc: %d [%.*s]", (int) io_data_len(&c->loc.io),
- (int) io_data_len(&c->loc.io), io_data(&c->loc.io)));
- DBG(("rem: %d [%.*s]", (int) io_data_len(&c->rem.io),
- (int) io_data_len(&c->rem.io), io_data(&c->rem.io)));
-
- /* Read from the local end if it is ready */
- if (local_ready && io_space_len(&c->loc.io))
- read_stream(&c->loc);
-
- if (io_data_len(&c->rem.io) > 0 && (c->loc.flags & FLAG_W) &&
- c->loc.io_class != NULL && c->loc.io_class->write != NULL)
- write_stream(&c->rem, &c->loc);
-
- if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
- write_stream(&c->loc, &c->rem);
-
- /* Check whether we should close this connection */
- if ((_shttpd_current_time > c->expire_time) ||
- (c->rem.flags & FLAG_CLOSED) ||
- ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
- connection_desctructor(&c->link);
-}
-
-static int
-num_workers(const struct shttpd_ctx *ctx)
-{
- char *p = ctx->options[OPT_THREADS];
- return (p ? atoi(p) : 1);
-}
-
-static void
-handle_connected_socket(struct shttpd_ctx *ctx,
- struct usa *sap, int sock, int is_ssl)
-{
-#if !defined(_WIN32)
- if (sock >= (int) FD_SETSIZE) {
- _shttpd_elog(E_LOG, NULL, "ctx %p: discarding "
- "socket %d, too busy", ctx, sock);
- (void) closesocket(sock);
- } else
-#endif /* !_WIN32 */
- if (!is_allowed(ctx, sap)) {
- _shttpd_elog(E_LOG, NULL, "%s is not allowed to connect",
- inet_ntoa(sap->u.sin.sin_addr));
- (void) closesocket(sock);
- } else if (num_workers(ctx) > 1) {
- pass_socket(ctx, sock, is_ssl);
- } else {
- add_socket(first_worker(ctx), sock, is_ssl);
- }
-}
-
-static int
-do_select(int max_fd, fd_set *read_set, fd_set *write_set, int milliseconds)
-{
- struct timeval tv;
- int n;
-
- tv.tv_sec = milliseconds / 1000;
- tv.tv_usec = (milliseconds % 1000) * 1000;
-
- /* Check IO readiness */
- if ((n = select(max_fd + 1, read_set, write_set, NULL, &tv)) < 0) {
-#ifdef _WIN32
- /*
- * On windows, if read_set and write_set are empty,
- * select() returns "Invalid parameter" error
- * (at least on my Windows XP Pro). So in this case,
- * we sleep here.
- */
- Sleep(milliseconds);
-#endif /* _WIN32 */
- DBG(("select: %d", ERRNO));
- }
-
- return (n);
-}
-
-static int
-multiplex_worker_sockets(const struct worker *worker, int *max_fd,
- fd_set *read_set, fd_set *write_set)
-{
- struct llhead *lp;
- struct conn *c;
- int nowait = FALSE;
-
- /* Add control socket */
- add_to_set(worker->ctl[0], read_set, max_fd);
-
- /* Multiplex streams */
- LL_FOREACH(&worker->connections, lp) {
- c = LL_ENTRY(lp, struct conn, link);
-
- /* If there is a space in remote IO, check remote socket */
- if (io_space_len(&c->rem.io))
- add_to_set(c->rem.chan.fd, read_set, max_fd);
-
-#if !defined(NO_CGI)
- /*
- * If there is a space in local IO, and local endpoint is
- * CGI, check local socket for read availability
- */
- if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
- c->loc.io_class == &_shttpd_io_cgi)
- add_to_set(c->loc.chan.fd, read_set, max_fd);
-
- /*
- * If there is some data read from remote socket, and
- * local endpoint is CGI, check local for write availability
- */
- if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
- c->loc.io_class == &_shttpd_io_cgi)
- add_to_set(c->loc.chan.fd, write_set, max_fd);
-#endif /* NO_CGI */
-
- /*
- * If there is some data read from local endpoint, check the
- * remote socket for write availability
- */
- if (io_data_len(&c->loc.io) && !(c->loc.flags & FLAG_SUSPEND))
- add_to_set(c->rem.chan.fd, write_set, max_fd);
-
- /*
- * Set select wait interval to zero if FLAG_ALWAYS_READY set
- */
- if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
- (c->loc.flags & FLAG_ALWAYS_READY))
- nowait = TRUE;
-
- if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
- (c->loc.flags & FLAG_ALWAYS_READY))
- nowait = TRUE;
- }
-
- return (nowait);
-}
-
-int
-shttpd_join(struct shttpd_ctx *ctx,
- fd_set *read_set, fd_set *write_set, int *max_fd)
-{
- struct llhead *lp;
- struct listener *l;
- int nowait = FALSE;
-
- /* Add listening sockets to the read set */
- LL_FOREACH(&ctx->listeners, lp) {
- l = LL_ENTRY(lp, struct listener, link);
- add_to_set(l->sock, read_set, max_fd);
- DBG(("FD_SET(%d) (listening)", l->sock));
- }
-
- if (num_workers(ctx) == 1)
- nowait = multiplex_worker_sockets(first_worker(ctx), max_fd,
- read_set, write_set);
-
- return (nowait);
-}
-
-
-static void
-process_worker_sockets(struct worker *worker, fd_set *read_set)
-{
- struct llhead *lp, *tmp;
- int cmd, skt[2], sock = worker->ctl[0];
- struct conn *c;
-
- /* Check if new socket is passed to us over the control socket */
- if (FD_ISSET(worker->ctl[0], read_set))
- while (recv(sock, (void *) &cmd, sizeof(cmd), 0) == sizeof(cmd))
- switch (cmd) {
- case CTL_PASS_SOCKET:
- (void)recv(sock, (void *) &skt, sizeof(skt), 0);
- add_socket(worker, skt[0], skt[1]);
- break;
- case CTL_WAKEUP:
- (void)recv(sock, (void *) &c, sizeof(c), 0);
- c->loc.flags &= FLAG_SUSPEND;
- break;
- default:
- _shttpd_elog(E_FATAL, NULL, "ctx %p: ctl cmd %d",
- worker->ctx, cmd);
- break;
- }
-
- /* Process all connections */
- LL_FOREACH_SAFE(&worker->connections, lp, tmp) {
- c = LL_ENTRY(lp, struct conn, link);
- process_connection(c, FD_ISSET(c->rem.chan.sock, read_set),
- c->loc.io_class != NULL &&
- ((c->loc.flags & FLAG_ALWAYS_READY)
-#if !defined(NO_CGI)
- || (c->loc.io_class == &_shttpd_io_cgi &&
- FD_ISSET(c->loc.chan.fd, read_set))
-#endif /* NO_CGI */
- ));
- }
-}
-
-/*
- * One iteration of server loop. This is the core of the data exchange.
- */
-void
-shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
-{
- struct llhead *lp;
- struct listener *l;
- fd_set read_set, write_set;
- int sock, max_fd = -1;
- struct usa sa;
-
- _shttpd_current_time = time(0);
- FD_ZERO(&read_set);
- FD_ZERO(&write_set);
-
- if (shttpd_join(ctx, &read_set, &write_set, &max_fd))
- milliseconds = 0;
-
- if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
- return;;
-
- /* Check for incoming connections on listener sockets */
- LL_FOREACH(&ctx->listeners, lp) {
- l = LL_ENTRY(lp, struct listener, link);
- if (!FD_ISSET(l->sock, &read_set))
- continue;
- do {
- sa.len = sizeof(sa.u.sin);
- if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1)
- handle_connected_socket(ctx,&sa,sock,l->is_ssl);
- } while (sock != -1);
- }
-
- if (num_workers(ctx) == 1)
- process_worker_sockets(first_worker(ctx), &read_set);
-}
-
-/*
- * Deallocate shttpd object, free up the resources
- */
-void
-shttpd_fini(struct shttpd_ctx *ctx)
-{
- size_t i;
-
- free_list(&ctx->workers, worker_destructor);
- free_list(&ctx->registered_uris, registered_uri_destructor);
- free_list(&ctx->acl, acl_destructor);
- free_list(&ctx->listeners, listener_destructor);
-#if !defined(NO_SSI)
- free_list(&ctx->ssi_funcs, _shttpd_ssi_func_destructor);
-#endif /* !NO_SSI */
-
- for (i = 0; i < NELEMS(ctx->options); i++)
- if (ctx->options[i] != NULL)
- free(ctx->options[i]);
-
- if (ctx->access_log) (void) fclose(ctx->access_log);
- if (ctx->error_log) (void) fclose(ctx->error_log);
-
- /* TODO: free SSL context */
-
- free(ctx);
-}
-
-/*
- * UNIX socketpair() implementation. Why? Because Windows does not have it.
- * Return 0 on success, -1 on error.
- */
-int
-shttpd_socketpair(int sp[2])
-{
- struct sockaddr_in sa;
- int sock, ret = -1;
- socklen_t len = sizeof(sa);
-
- sp[0] = sp[1] = -1;
-
- (void) memset(&sa, 0, sizeof(sa));
- sa.sin_family = AF_INET;
- sa.sin_port = htons(0);
- sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- if ((sock = socket(AF_INET, SOCK_STREAM, 0)) != -1 &&
- !bind(sock, (struct sockaddr *) &sa, len) &&
- !listen(sock, 1) &&
- !getsockname(sock, (struct sockaddr *) &sa, &len) &&
- (sp[0] = socket(AF_INET, SOCK_STREAM, 6)) != -1 &&
- !connect(sp[0], (struct sockaddr *) &sa, len) &&
- (sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) != -1) {
-
- /* Success */
- ret = 0;
- } else {
-
- /* Failure, close descriptors */
- if (sp[0] != -1)
- (void) closesocket(sp[0]);
- if (sp[1] != -1)
- (void) closesocket(sp[1]);
- }
-
- (void) closesocket(sock);
- (void) _shttpd_set_non_blocking_mode(sp[0]);
- (void) _shttpd_set_non_blocking_mode(sp[1]);
-
-#ifndef _WIN32
- (void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
- (void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
-#endif /* _WIN32*/
-
- return (ret);
-}
-
-static int isbyte(int n) { return (n >= 0 && n <= 255); }
-
-static int
-set_inetd(struct shttpd_ctx *ctx, const char *flag)
-{
- ctx = NULL; /* Unused */
-
- if (_shttpd_is_true(flag)) {
- shttpd_set_option(ctx, "ports", NULL);
- (void) freopen("/dev/null", "a", stderr);
- add_socket(first_worker(ctx), 0, 0);
- }
-
- return (TRUE);
-}
-
-static int
-set_uid(struct shttpd_ctx *ctx, const char *uid)
-{
- struct passwd *pw;
-
- ctx = NULL; /* Unused */
-
-#if !defined(_WIN32)
- if ((pw = getpwnam(uid)) == NULL)
- _shttpd_elog(E_FATAL, 0, "%s: unknown user [%s]", __func__, uid);
- else if (setgid(pw->pw_gid) == -1)
- _shttpd_elog(E_FATAL, NULL, "%s: setgid(%s): %s",
- __func__, uid, strerror(errno));
- else if (setuid(pw->pw_uid) == -1)
- _shttpd_elog(E_FATAL, NULL, "%s: setuid(%s): %s",
- __func__, uid, strerror(errno));
-#endif /* !_WIN32 */
- return (TRUE);
-}
-
-static int
-set_acl(struct shttpd_ctx *ctx, const char *s)
-{
- struct acl *acl = NULL;
- char flag;
- int len, a, b, c, d, n, mask;
-
- /* Delete the old ACLs if any */
- free_list(&ctx->acl, acl_destructor);
-
- FOR_EACH_WORD_IN_LIST(s, len) {
-
- mask = 32;
-
- if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
- _shttpd_elog(E_FATAL, NULL, "[%s]: subnet must be "
- "[+|-]x.x.x.x[/x]", s);
- } else if (flag != '+' && flag != '-') {
- _shttpd_elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
- } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
- _shttpd_elog(E_FATAL, NULL, "bad ip address: [%s]", s);
- } else if ((acl = malloc(sizeof(*acl))) == NULL) {
- _shttpd_elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
- } else if (sscanf(s + n, "/%d", &mask) == 0) {
- /* Do nothing, no mask specified */
- } else if (mask < 0 || mask > 32) {
- _shttpd_elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
- }
-
- acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
- acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
- acl->flag = flag;
- LL_TAIL(&ctx->acl, &acl->link);
- }
-
- return (TRUE);
-}
-
-#ifndef NO_SSL
-/*
- * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
- */
-static int
-set_ssl(struct shttpd_ctx *ctx, const char *pem)
-{
- SSL_CTX *CTX;
- void *lib;
- struct ssl_func *fp;
- int retval = FALSE;
-
- /* Load SSL library dynamically */
- if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL) {
- _shttpd_elog(E_LOG, NULL, "set_ssl: cannot load %s", SSL_LIB);
- return (FALSE);
- }
-
- for (fp = ssl_sw; fp->name != NULL; fp++)
- if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL) {
- _shttpd_elog(E_LOG, NULL,"set_ssl: cannot find %s", fp->name);
- return (FALSE);
- }
-
- /* Initialize SSL crap */
- SSL_library_init();
-
- if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
- _shttpd_elog(E_LOG, NULL, "SSL_CTX_new error");
- else if (SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
- _shttpd_elog(E_LOG, NULL, "cannot open %s", pem);
- else if (SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
- _shttpd_elog(E_LOG, NULL, "cannot open %s", pem);
- else
- retval = TRUE;
-
- ctx->ssl_ctx = CTX;
-
- return (retval);
-}
-#endif /* NO_SSL */
-
-static int
-open_log_file(FILE **fpp, const char *path)
-{
- int retval = TRUE;
-
- if (*fpp != NULL)
- (void) fclose(*fpp);
-
- if (path == NULL) {
- *fpp = NULL;
- } else if ((*fpp = fopen(path, "a")) == NULL) {
- _shttpd_elog(E_LOG, NULL, "cannot open log file %s: %s",
- path, strerror(errno));
- retval = FALSE;
- }
-
- return (retval);
-}
-
-static int set_alog(struct shttpd_ctx *ctx, const char *path) {
- return (open_log_file(&ctx->access_log, path));
-}
-
-static int set_elog(struct shttpd_ctx *ctx, const char *path) {
- return (open_log_file(&ctx->error_log, path));
-}
-
-static void show_cfg_page(struct shttpd_arg *arg);
-
-static int
-set_cfg_uri(struct shttpd_ctx *ctx, const char *uri)
-{
- free_list(&ctx->registered_uris, ®istered_uri_destructor);
-
- if (uri != NULL)
- shttpd_register_uri(ctx, uri, &show_cfg_page, ctx);
-
- return (TRUE);
-}
-
-static struct worker *
-add_worker(struct shttpd_ctx *ctx)
-{
- struct worker *worker;
-
- if ((worker = calloc(1, sizeof(*worker))) == NULL)
- _shttpd_elog(E_FATAL, NULL, "Cannot allocate worker");
- LL_INIT(&worker->connections);
- worker->ctx = ctx;
- (void) shttpd_socketpair(worker->ctl);
- LL_TAIL(&ctx->workers, &worker->link);
-
- return (worker);
-}
-
-#if !defined(NO_THREADS)
-static void
-poll_worker(struct worker *worker, int milliseconds)
-{
- fd_set read_set, write_set;
- int max_fd = -1;
-
- FD_ZERO(&read_set);
- FD_ZERO(&write_set);
-
- if (multiplex_worker_sockets(worker, &max_fd, &read_set, &write_set))
- milliseconds = 0;
-
- if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
- return;;
-
- process_worker_sockets(worker, &read_set);
-}
-
-static void
-worker_function(void *param)
-{
- struct worker *worker = param;
-
- while (worker->exit_flag == 0)
- poll_worker(worker, 1000 * 10);
-
- free_list(&worker->connections, connection_desctructor);
- free(worker);
-}
-
-static int
-set_workers(struct shttpd_ctx *ctx, const char *value)
-{
- int new_num, old_num;
- struct llhead *lp, *tmp;
- struct worker *worker;
-
- new_num = atoi(value);
- old_num = 0;
- LL_FOREACH(&ctx->workers, lp)
- old_num++;
-
- if (new_num == 1) {
- if (old_num > 1)
- /* Stop old threads */
- LL_FOREACH_SAFE(&ctx->workers, lp, tmp) {
- worker = LL_ENTRY(lp, struct worker, link);
- LL_DEL(&worker->link);
- worker = LL_ENTRY(lp, struct worker, link);
- worker->exit_flag = 1;
- }
- (void) add_worker(ctx);
- } else {
- /* FIXME: we cannot here reduce the number of threads */
- while (new_num > 1 && new_num > old_num) {
- worker = add_worker(ctx);
- _beginthread(worker_function, 0, worker);
- old_num++;
- }
- }
-
- return (TRUE);
-}
-#endif /* NO_THREADS */
-
-static const struct opt {
- int index; /* Index in shttpd_ctx */
- const char *name; /* Option name in config file */
- const char *description; /* Description */
- const char *default_value; /* Default option value */
- int (*setter)(struct shttpd_ctx *, const char *);
-} known_options[] = {
- {OPT_ROOT, "root", "\tWeb root directory", ".", NULL},
- {OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL},
-#ifndef NO_SSL
- {OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl},
-#endif /* NO_SSL */
- {OPT_PORTS, "ports", "Listening ports", LISTENING_PORTS, set_ports},
- {OPT_DIR_LIST, "dir_list", "Directory listing", "yes", NULL},
- {OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri},
- {OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL},
-#ifndef NO_CGI
- {OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL},
- {OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL},
- {OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL},
-#endif /* NO_CGI */
- {OPT_SSI_EXTENSIONS, "ssi_ext", "SSI extensions", SSI_EXT, NULL},
-#ifndef NO_AUTH
- {OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL},
- {OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL},
- {OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL},
-#endif /* !NO_AUTH */
-#ifdef _WIN32
- {OPT_SERVICE, "service", "Manage WinNNT service (install"
- "|uninstall)", NULL, _shttpd_set_nt_service},
- {OPT_HIDE, "systray", "Hide console, show icon on systray",
- "no", _shttpd_set_systray},
-#else
- {OPT_INETD, "inetd", "Inetd mode", "no", set_inetd},
- {OPT_UID, "uid", "\tRun as user", NULL, set_uid},
-#endif /* _WIN32 */
- {OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog},
- {OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog},
- {OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL},
- {OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL},
- {OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl},
-#if !defined(NO_THREADS)
- {OPT_THREADS, "threads", "Number of worker threads", "1", set_workers},
-#endif /* !NO_THREADS */
- {-1, NULL, NULL, NULL, NULL}
-};
-
-static const struct opt *
-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);
-
- _shttpd_elog(E_FATAL, NULL, "no such option: [%s]", opt_name);
-
- /* UNREACHABLE */
- return (NULL);
-}
-
-int
-shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val)
-{
- const struct opt *o = find_opt(opt);
- int retval = TRUE;
-
- /* Call option setter first, so it can use both new and old values */
- if (o->setter != NULL)
- retval = o->setter(ctx, val);
-
- /* Free old value if any */
- if (ctx->options[o->index] != NULL)
- free(ctx->options[o->index]);
-
- /* Set new option value */
- ctx->options[o->index] = val ? _shttpd_strdup(val) : NULL;
-
- return (retval);
-}
-
-static void
-show_cfg_page(struct shttpd_arg *arg)
-{
- struct shttpd_ctx *ctx = arg->user_data;
- char opt_name[20], value[BUFSIZ];
- const struct opt *o;
-
- opt_name[0] = value[0] = '\0';
-
- if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) {
- if (arg->flags & SHTTPD_MORE_POST_DATA)
- return;
- (void) shttpd_get_var("o", arg->in.buf, arg->in.len,
- opt_name, sizeof(opt_name));
- (void) shttpd_get_var("v", arg->in.buf, arg->in.len,
- value, sizeof(value));
- shttpd_set_option(ctx, opt_name, value[0] ? value : NULL);
- }
-
- shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
- "<html><body><h1>SHTTPD v. %s</h1>", shttpd_version());
-
- shttpd_printf(arg, "%s", "name != NULL; o++) {
- shttpd_printf(arg,
- "<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>",
- o->name, o->description, o->name,
- ctx->options[o->index] ? ctx->options[o->index] : "");
- }
-
- shttpd_printf(arg, "%s", "</table></body></html>");
- arg->flags |= SHTTPD_END_OF_OUTPUT;
-}
-
-/*
- * Show usage string and exit.
- */
-void
-_shttpd_usage(const char *prog)
-{
- const struct opt *o;
-
- (void) fprintf(stderr,
- "SHTTPD version %s (c) Sergey Lyubka\n"
- "usage: %s [options] [config_file]\n", VERSION, prog);
-
-#if !defined(NO_AUTH)
- fprintf(stderr, " -A <realm> <user> <passwd>\n");
-#endif /* NO_AUTH */
-
- for (o = known_options; o->name != NULL; o++) {
- (void) fprintf(stderr, " -%s\t%s", o->name, o->description);
- if (o->default_value != NULL)
- fprintf(stderr, " (default: %s)", o->default_value);
- fputc('\n', stderr);
- }
-
- exit(EXIT_FAILURE);
-}
-
-static void
-set_opt(struct shttpd_ctx *ctx, const char *opt, const char *value)
-{
- const struct opt *o;
-
- o = find_opt(opt);
- if (ctx->options[o->index] != NULL)
- free(ctx->options[o->index]);
- ctx->options[o->index] = _shttpd_strdup(value);
-}
-
-static void
-process_command_line_arguments(struct shttpd_ctx *ctx, char *argv[])
-{
- const char *config_file = CONFIG_FILE;
- char line[BUFSIZ], opt[BUFSIZ],
- val[BUFSIZ], path[FILENAME_MAX], *p;
- FILE *fp;
- size_t i, line_no = 0;
-
- /* First find out, which config file to open */
- for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
- if (argv[i + 1] == NULL)
- _shttpd_usage(argv[0]);
-
- if (argv[i] != NULL && argv[i + 1] != NULL) {
- /* More than one non-option arguments are given w*/
- _shttpd_usage(argv[0]);
- } 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 shttpd lives */
- if ((p = strrchr(argv[0], DIRSEP)) != 0) {
- _shttpd_snprintf(path, sizeof(path), "%.*s%s",
- 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)
- _shttpd_elog(E_FATAL, NULL, "cannot open config file %s: %s",
- config_file, strerror(errno));
-
- if (fp != NULL) {
-
- _shttpd_elog(E_LOG, NULL, "Loading config file %s", 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 %[^\n#]", opt, val) != 2)
- _shttpd_elog(E_FATAL, NULL, "line %d in %s is invalid",
- line_no, config_file);
-
- set_opt(ctx, opt, val);
- }
-
- (void) fclose(fp);
- }
-
- /* Now pass through the command line options */
- for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
- set_opt(ctx, &argv[i][1], argv[i + 1]);
-}
-
-struct shttpd_ctx *
-shttpd_init(int argc, char *argv[])
-{
- struct shttpd_ctx *ctx;
- struct tm *tm;
- const struct opt *o;
-
- if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
- _shttpd_elog(E_FATAL, NULL, "cannot allocate shttpd context");
-
- LL_INIT(&ctx->registered_uris);
- LL_INIT(&ctx->error_handlers);
- LL_INIT(&ctx->acl);
- LL_INIT(&ctx->ssi_funcs);
- LL_INIT(&ctx->listeners);
- LL_INIT(&ctx->workers);
-
- /* Initialize options. First pass: set default option values */
- for (o = known_options; o->name != NULL; o++)
- ctx->options[o->index] = o->default_value ?
- _shttpd_strdup(o->default_value) : NULL;
-
- /* Second and third passes: config file and argv */
- if (argc > 0 && argv != NULL)
- process_command_line_arguments(ctx, argv);
-
- /* Call setter functions */
- for (o = known_options; o->name != NULL; o++)
- if (o->setter && ctx->options[o->index] != NULL)
- if (o->setter(ctx, ctx->options[o->index]) == FALSE) {
- shttpd_fini(ctx);
- return (NULL);
- }
-
- _shttpd_current_time = time(NULL);
- tm = localtime(&_shttpd_current_time);
- _shttpd_tz_offset = 0;
-
- if (num_workers(ctx) == 1)
- (void) add_worker(ctx);
-#if 0
- tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
-#endif
-
-#ifdef _WIN32
- {WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
-#endif /* _WIN32 */
-
- return (ctx);
-}
diff --git a/vendor/shttpd/shttpd.h b/vendor/shttpd/shttpd.h
deleted file mode 100644
index d475b06..0000000
--- a/vendor/shttpd/shttpd.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (c) 2004-2008 Sergey Lyubka
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- *
- * $Id: shttpd.h,v 1.18 2008/08/23 08:34:50 drozd Exp $
- */
-
-#ifndef SHTTPD_HEADER_INCLUDED
-#define SHTTPD_HEADER_INCLUDED
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-struct ubuf {
- char *buf; /* Buffer pointer */
- int len; /* Size of a buffer */
- int num_bytes; /* Bytes processed by callback */
-};
-
-/*
- * This structure is passed to the user callback function
- */
-struct shttpd_arg {
- void *priv; /* Private! Do not touch! */
- void *state; /* User state */
- void *user_data; /* Data from register_uri() */
- struct ubuf in; /* Input is here, POST data */
- struct ubuf out; /* Output goes here */
-
- unsigned int flags;
-#define SHTTPD_END_OF_OUTPUT 1 /* No more data do send */
-#define SHTTPD_CONNECTION_ERROR 2 /* Server closed the connection */
-#define SHTTPD_MORE_POST_DATA 4 /* arg->in has incomplete data */
-#define SHTTPD_POST_BUFFER_FULL 8 /* arg->in has max data */
-#define SHTTPD_SSI_EVAL_TRUE 16 /* SSI eval callback must set it*/
-#define SHTTPD_SUSPEND 32 /* User wants to suspend output */
-};
-
-/*
- * User callback function. Called when certain registered URLs have been
- * requested. These are the requirements to the callback function:
- *
- * 1. It must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
- * and record how many bytes are copied, into 'out.num_bytes'
- * 2. It must not call any blocking functions
- * 3. It must set SHTTPD_END_OF_OUTPUT flag when there is no more data to send
- * 4. For POST requests, it must process the incoming data (in.buf) of length
- * 'in.len', and set 'in.num_bytes', which is how many bytes of POST
- * data was processed and can be discarded by SHTTPD.
- * 5. If callback allocates arg->state, to keep state, it must deallocate it
- * at the end of coonection SHTTPD_CONNECTION_ERROR or SHTTPD_END_OF_OUTPUT
- * 6. If callback function wants to suspend until some event, it must store
- * arg->priv pointer elsewhere, set SHTTPD_SUSPEND flag and return. When
- * the event happens, user code should call shttpd_wakeup(priv).
- * It is safe to call shttpd_wakeup() from any thread. User code must
- * not call shttpd_wakeup once the connection is closed.
- */
-typedef void (*shttpd_callback_t)(struct shttpd_arg *);
-
-/*
- * shttpd_init Initialize shttpd context
- * shttpd_fini Dealocate the context, close all connections
- * shttpd_set_option Set new value for option
- * shttpd_register_uri Setup the callback function for specified URL
- * shttpd_poll Do connections processing
- * shttpd_version return string with SHTTPD version
- * shttpd_get_var Fetch POST/GET variable value by name. Return value len
- * shttpd_get_header return value of the specified HTTP header
- * shttpd_get_env return values for the following pseudo-variables:
- "REQUEST_METHOD", "REQUEST_URI",
- * "REMOTE_USER" and "REMOTE_ADDR"
- * shttpd_printf helper function to output data
- * shttpd_handle_error register custom HTTP error handler
- * shttpd_wakeup clear SHTTPD_SUSPEND state for the connection
- */
-
-struct shttpd_ctx;
-
-struct shttpd_ctx *shttpd_init(int argc, char *argv[]);
-int shttpd_set_option(struct shttpd_ctx *, const char *opt, const char *val);
-void shttpd_fini(struct shttpd_ctx *);
-void shttpd_register_uri(struct shttpd_ctx *ctx, const char *uri,
- shttpd_callback_t callback, void *const user_data);
-void shttpd_poll(struct shttpd_ctx *, int milliseconds);
-const char *shttpd_version(void);
-int shttpd_get_var(const char *var, const char *buf, int buf_len,
- char *value, int value_len);
-const char *shttpd_get_header(struct shttpd_arg *, const char *header_name);
-const char *shttpd_get_env(struct shttpd_arg *, const char *name);
-void shttpd_get_http_version(struct shttpd_arg *,
- unsigned long *major, unsigned long *minor);
-size_t shttpd_printf(struct shttpd_arg *, const char *fmt, ...);
-void shttpd_handle_error(struct shttpd_ctx *ctx, int status,
- shttpd_callback_t func, void *const data);
-void shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
- shttpd_callback_t func, void *const user_data);
-void shttpd_wakeup(const void *priv);
-int shttpd_join(struct shttpd_ctx *, fd_set *, fd_set *, int *max_fd);
-int shttpd_socketpair(int sp[2]);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* SHTTPD_HEADER_INCLUDED */
diff --git a/vendor/shttpd/ssl.h b/vendor/shttpd/ssl.h
deleted file mode 100644
index 5aeae4a..0000000
--- a/vendor/shttpd/ssl.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-/*
- * Snatched from OpenSSL includes. I put the prototypes here to be independent
- * from the OpenSSL source installation. Having this, shttpd + SSL can be
- * built on any system with binary SSL libraries installed.
- */
-
-typedef struct ssl_st SSL;
-typedef struct ssl_method_st SSL_METHOD;
-typedef struct ssl_ctx_st SSL_CTX;
-
-#define SSL_ERROR_WANT_READ 2
-#define SSL_ERROR_WANT_WRITE 3
-#define SSL_FILETYPE_PEM 1
-
-/*
- * Dynamically loaded SSL functionality
- */
-struct ssl_func {
- const char *name; /* SSL function name */
- union variant ptr; /* Function pointer */
-};
-
-extern struct ssl_func ssl_sw[];
-
-#define FUNC(x) ssl_sw[x].ptr.v_func
-
-#define SSL_free(x) (* (void (*)(SSL *)) FUNC(0))(x)
-#define SSL_accept(x) (* (int (*)(SSL *)) FUNC(1))(x)
-#define SSL_connect(x) (* (int (*)(SSL *)) FUNC(2))(x)
-#define SSL_read(x,y,z) (* (int (*)(SSL *, void *, int)) FUNC(3))((x),(y),(z))
-#define SSL_write(x,y,z) \
- (* (int (*)(SSL *, const void *,int)) FUNC(4))((x), (y), (z))
-#define SSL_get_error(x,y)(* (int (*)(SSL *, int)) FUNC(5))((x), (y))
-#define SSL_set_fd(x,y) (* (int (*)(SSL *, int)) FUNC(6))((x), (y))
-#define SSL_new(x) (* (SSL * (*)(SSL_CTX *)) FUNC(7))(x)
-#define SSL_CTX_new(x) (* (SSL_CTX * (*)(SSL_METHOD *)) FUNC(8))(x)
-#define SSLv23_server_method() (* (SSL_METHOD * (*)(void)) FUNC(9))()
-#define SSL_library_init() (* (int (*)(void)) FUNC(10))()
-#define SSL_CTX_use_PrivateKey_file(x,y,z) (* (int (*)(SSL_CTX *, \
- const char *, int)) FUNC(11))((x), (y), (z))
-#define SSL_CTX_use_certificate_file(x,y,z) (* (int (*)(SSL_CTX *, \
- const char *, int)) FUNC(12))((x), (y), (z))
diff --git a/vendor/shttpd/standalone.c b/vendor/shttpd/standalone.c
deleted file mode 100644
index 89ba661..0000000
--- a/vendor/shttpd/standalone.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#include "defs.h"
-
-static int exit_flag; /* Program termination flag */
-
-static void
-signal_handler(int sig_num)
-{
- switch (sig_num) {
-#ifndef _WIN32
- case SIGCHLD:
- while (waitpid(-1, &sig_num, WNOHANG) > 0) ;
- break;
-#endif /* !_WIN32 */
- default:
- exit_flag = sig_num;
- break;
- }
-}
-
-int
-main(int argc, char *argv[])
-{
- struct shttpd_ctx *ctx;
-
-#if !defined(NO_AUTH)
- if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') {
- if (argc != 6)
- _shttpd_usage(argv[0]);
- exit(_shttpd_edit_passwords(argv[2],argv[3],argv[4],argv[5]));
- }
-#endif /* NO_AUTH */
-
- if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
- _shttpd_usage(argv[0]);
-
-#if defined(_WIN32)
- try_to_run_as_nt_service();
-#endif /* _WIN32 */
-
-#ifndef _WIN32
- (void) signal(SIGCHLD, signal_handler);
- (void) signal(SIGPIPE, SIG_IGN);
-#endif /* _WIN32 */
-
- (void) signal(SIGTERM, signal_handler);
- (void) signal(SIGINT, signal_handler);
-
- if ((ctx = shttpd_init(argc, argv)) == NULL)
- _shttpd_elog(E_FATAL, NULL, "%s",
- "Cannot initialize SHTTPD context");
-
- _shttpd_elog(E_LOG, NULL, "shttpd %s started on port(s) %s, serving %s",
- VERSION, ctx->options[OPT_PORTS], ctx->options[OPT_ROOT]);
-
- while (exit_flag == 0)
- shttpd_poll(ctx, 10 * 1000);
-
- _shttpd_elog(E_LOG, NULL, "Exit on signal %d", exit_flag);
- shttpd_fini(ctx);
-
- return (EXIT_SUCCESS);
-}
diff --git a/vendor/shttpd/std_includes.h b/vendor/shttpd/std_includes.h
deleted file mode 100644
index 4bf1ea7..0000000
--- a/vendor/shttpd/std_includes.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#ifndef STD_HEADERS_INCLUDED
-#define STD_HEADERS_INCLUDED
-
-#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
-#include
-#include
-#include
-#include
-#include
-#include
-#endif /* _WIN32_WCE */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#if defined(_WIN32) /* Windows specific */
-#include "compat_win32.h"
-#elif defined(__rtems__) /* RTEMS specific */
-#include "compat_rtems.h"
-#else /* UNIX specific */
-#include "compat_unix.h"
-#endif /* _WIN32 */
-
-#endif /* STD_HEADERS_INCLUDED */
diff --git a/vendor/shttpd/string.c b/vendor/shttpd/string.c
deleted file mode 100644
index a383cf8..0000000
--- a/vendor/shttpd/string.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2004-2005 Sergey Lyubka
- * All rights reserved
- *
- * "THE BEER-WARE LICENSE" (Revision 42):
- * Sergey Lyubka wrote this file. As long as you retain this notice you
- * can do whatever you want with this stuff. If we meet some day, and you think
- * this stuff is worth it, you can buy me a beer in return.
- */
-
-#include "defs.h"
-
-void
-_shttpd_strlcpy(register char *dst, register const char *src, size_t n)
-{
- for (; *src != '\0' && n > 1; n--)
- *dst++ = *src++;
- *dst = '\0';
-}
-
-int
-_shttpd_strncasecmp(const char *str1, const char *str2, size_t len)
-{
- register const unsigned char *s1 = (unsigned char *) str1,
- *s2 = (unsigned char *) str2, *e;
- int ret;
-
- for (e = s1 + len - 1; s1 < e && *s1 != '\0' && *s2 != '\0' &&
- tolower(*s1) == tolower(*s2); s1++, s2++) ;
- ret = tolower(*s1) - tolower(*s2);
-
- return (ret);
-}
-
-char *
-_shttpd_strndup(const char *ptr, size_t len)
-{
- char *p;
-
- if ((p = malloc(len + 1)) != NULL)
- _shttpd_strlcpy(p, ptr, len + 1);
-
- return (p);
-
-}
-
-char *
-_shttpd_strdup(const char *str)
-{
- return (_shttpd_strndup(str, strlen(str)));
-}
-
-/*
- * Sane snprintf(). Acts like snprintf(), but never return -1 or the
- * value bigger than supplied buffer.
- * Thanks Adam Zeldis to pointing snprintf()-caused vulnerability
- * in his audit report.
- */
-int
-_shttpd_snprintf(char *buf, size_t buflen, const char *fmt, ...)
-{
- va_list ap;
- int n;
-
- if (buflen == 0)
- return (0);
-
- va_start(ap, fmt);
- n = vsnprintf(buf, buflen, fmt, ap);
- va_end(ap);
-
- if (n < 0 || (size_t) n >= buflen)
- n = buflen - 1;
- buf[n] = '\0';
-
- return (n);
-}
-
-/*
- * Verify that given file has certain extension
- */
-int
-_shttpd_match_extension(const char *path, const char *ext_list)
-{
- size_t len, path_len;
-
- path_len = strlen(path);
-
- FOR_EACH_WORD_IN_LIST(ext_list, len)
- if (len < path_len && path[path_len - len - 1] == '.' &&
- !_shttpd_strncasecmp(path + path_len - len, ext_list, len))
- return (TRUE);
-
- return (FALSE);
-}
--
To unsubscribe, e-mail: zypp-commit+unsubscribe@opensuse.org
For additional commands, e-mail: zypp-commit+help@opensuse.org
|