You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2018/07/03 20:45:53 UTC
[1/6] celix git commit: CELIX-451: Adds celix_bundle_dir for copying
files. Also remove mongoose example and adds a new civetweb example.
Repository: celix
Updated Branches:
refs/heads/develop 3f24edf0b -> c4de9077d
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/mongoose/root/index.html
----------------------------------------------------------------------
diff --git a/examples/celix-examples/mongoose/root/index.html b/examples/celix-examples/mongoose/root/index.html
deleted file mode 100644
index 255758f..0000000
--- a/examples/celix-examples/mongoose/root/index.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- *Licensed to the Apache Software Foundation (ASF) under one
- *or more contributor license agreements. See the NOTICE file
- *distributed with this work for additional information
- *regarding copyright ownership. The ASF licenses this file
- *to you under the Apache License, Version 2.0 (the
- *"License"); you may not use this file except in compliance
- *with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- *Unless required by applicable law or agreed to in writing,
- *software distributed under the License is distributed on an
- *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- *specific language governing permissions and limitations
- *under the License.
--->
-<div align="center"><FONT
-color="#ffffff" size="+1"><MARQUEE bgcolor="#000080"
-direction="right" loop="20" width="75%"><STRONG>
-Serving content from within a bundle works!!1!11!!
-</STRONG></MARQUEE></FONT></DIV>
\ No newline at end of file
[2/6] celix git commit: CELIX-451: Adds celix_bundle_dir for copying
files. Also remove mongoose example and adds a new civetweb example.
Posted by pn...@apache.org.
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/mongoose/private/src/mongoose.c
----------------------------------------------------------------------
diff --git a/examples/celix-examples/mongoose/private/src/mongoose.c b/examples/celix-examples/mongoose/private/src/mongoose.c
deleted file mode 100644
index 1a1f87c..0000000
--- a/examples/celix-examples/mongoose/private/src/mongoose.c
+++ /dev/null
@@ -1,4076 +0,0 @@
-// Copyright (c) 2004-2010 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.
-
-#if defined(_WIN32)
-#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
-#else
-#define _XOPEN_SOURCE 600 // For flockfile() on Linux
-#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
-#endif
-
-#ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <signal.h>
-#include <fcntl.h>
-#endif // !_WIN32_WCE
-
-#include <time.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <assert.h>
-#include <string.h>
-#include <ctype.h>
-#include <limits.h>
-#include <stdio.h>
-#include <sys/wait.h>
-
-#if defined(_WIN32) // Windows specific #includes and #defines
-#define _WIN32_WINNT 0x0400 // To make it link in VS2005
-#include <windows.h>
-
-#ifndef PATH_MAX
-#define PATH_MAX MAX_PATH
-#endif
-
-#ifndef _WIN32_WCE
-#include <process.h>
-#include <direct.h>
-#include <io.h>
-#else // _WIN32_WCE
-#include <winsock2.h>
-#define NO_CGI // WinCE has no pipes
-
-typedef long off_t;
-#define BUFSIZ 4096
-
-#define errno GetLastError()
-#define strerror(x) _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
-#endif // _WIN32_WCE
-
-#define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \
- ((uint64_t)((uint32_t)(hi))) << 32))
-#define RATE_DIFF 10000000 // 100 nsecs
-#define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de)
-#define SYS2UNIX_TIME(lo, hi) \
- (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)
-
-// Visual Studio 6 does not know __func__ or __FUNCTION__
-// The rest of MS compilers use __FUNCTION__, not C99 __func__
-// Also use _strtoui64 on modern M$ compilers
-#if defined(_MSC_VER) && _MSC_VER < 1300
-#define STRX(x) #x
-#define STR(x) STRX(x)
-#define __func__ "line " STR(__LINE__)
-#define strtoull(x, y, z) strtoul(x, y, z)
-#define strtoll(x, y, z) strtol(x, y, z)
-#else
-#define __func__ __FUNCTION__
-#define strtoull(x, y, z) _strtoui64(x, y, z)
-#define strtoll(x, y, z) _strtoi64(x, y, z)
-#endif // _MSC_VER
-
-#define ERRNO GetLastError()
-#define NO_SOCKLEN_T
-#define SSL_LIB "ssleay32.dll"
-#define CRYPTO_LIB "libeay32.dll"
-#define DIRSEP '\\'
-#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')
-#define O_NONBLOCK 0
-#if !defined(EWOULDBLOCK)
-#define EWOULDBLOCK WSAEWOULDBLOCK
-#endif // !EWOULDBLOCK
-#define _POSIX_
-#define INT64_FMT "I64d"
-
-#define WINCDECL __cdecl
-#define SHUT_WR 1
-#define snprintf _snprintf
-#define vsnprintf _vsnprintf
-#define sleep(x) Sleep((x) * 1000)
-
-#define pipe(x) _pipe(x, BUFSIZ, _O_BINARY)
-#define popen(x, y) _popen(x, y)
-#define pclose(x) _pclose(x)
-#define close(x) _close(x)
-#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
-#define RTLD_LAZY 0
-#define fseeko(x, y, z) fseek((x), (y), (z))
-#define fdopen(x, y) _fdopen((x), (y))
-#define write(x, y, z) _write((x), (y), (unsigned) z)
-#define read(x, y, z) _read((x), (y), (unsigned) z)
-#define flockfile(x) (void) 0
-#define funlockfile(x) (void) 0
-
-#if !defined(fileno)
-#define fileno(x) _fileno(x)
-#endif // !fileno MINGW #defines fileno
-
-typedef HANDLE pthread_mutex_t;
-typedef struct {HANDLE signal, broadcast;} pthread_cond_t;
-typedef DWORD pthread_t;
-#define pid_t HANDLE // MINGW typedefs pid_t to int. Using #define here.
-
-struct timespec {
- long tv_nsec;
- long tv_sec;
-};
-
-static int pthread_mutex_lock(pthread_mutex_t *);
-static int pthread_mutex_unlock(pthread_mutex_t *);
-static FILE *mg_fopen(const char *path, const char *mode);
-
-#if defined(HAVE_STDINT)
-#include <stdint.h>
-#else
-typedef unsigned int uint32_t;
-typedef unsigned short uint16_t;
-typedef unsigned __int64 uint64_t;
-typedef __int64 int64_t;
-#define INT64_MAX 9223372036854775807
-#endif // HAVE_STDINT
-
-// POSIX dirent interface
-struct dirent {
- char d_name[PATH_MAX];
-};
-
-typedef struct DIR {
- HANDLE handle;
- WIN32_FIND_DATAW info;
- struct dirent result;
-} DIR;
-
-#else // UNIX specific
-
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <inttypes.h>
-#include <netdb.h>
-
-#include <pwd.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <dlfcn.h>
-#include <pthread.h>
-#if defined(__MACH__)
-#define SSL_LIB "libssl.dylib"
-#define CRYPTO_LIB "libcrypto.dylib"
-#else
-#define SSL_LIB "libssl.so"
-#define CRYPTO_LIB "libcrypto.so"
-#endif
-#define DIRSEP '/'
-#define IS_DIRSEP_CHAR(c) ((c) == '/')
-#define O_BINARY 0
-#define closesocket(a) close(a)
-#define mg_fopen(x, y) fopen(x, y)
-#define mg_mkdir(x, y) mkdir(x, y)
-#define mg_remove(x) remove(x)
-#define mg_rename(x, y) rename(x, y)
-#define ERRNO errno
-#define INVALID_SOCKET (-1)
-#define INT64_FMT PRId64
-typedef int SOCKET;
-#define WINCDECL
-
-#endif // End of Windows and UNIX specific includes
-
-#include "mongoose.h"
-
-#define MONGOOSE_VERSION "2.11"
-#define PASSWORDS_FILE_NAME ".htpasswd"
-#define CGI_ENVIRONMENT_SIZE 4096
-#define MAX_CGI_ENVIR_VARS 64
-#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
-
-#if defined(DEBUG)
-#define DEBUG_TRACE(x) do { \
- flockfile(stdout); \
- printf("*** %lu.%p.%s.%d: ", \
- (unsigned long) time(NULL), (void *) pthread_self(), \
- __func__, __LINE__); \
- printf x; \
- putchar('\n'); \
- fflush(stdout); \
- funlockfile(stdout); \
-} while (0)
-#else
-#define DEBUG_TRACE(x)
-#endif // DEBUG
-
-// Darwin prior to 7.0 and Win32 do not have socklen_t
-#ifdef NO_SOCKLEN_T
-typedef int socklen_t;
-#endif // NO_SOCKLEN_T
-
-typedef void * (*mg_thread_func_pt)(void *);
-
-static const char *http_500_error = "Internal Server Error";
-
-// 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
-#define CRYPTO_LOCK 1
-
-#if defined(NO_SSL_DL)
-extern void SSL_free(SSL *);
-extern int SSL_accept(SSL *);
-extern int SSL_connect(SSL *);
-extern int SSL_read(SSL *, void *, int);
-extern int SSL_write(SSL *, const void *, int);
-extern int SSL_get_error(const SSL *, int);
-extern int SSL_set_fd(SSL *, int);
-extern SSL *SSL_new(SSL_CTX *);
-extern SSL_CTX *SSL_CTX_new(SSL_METHOD *);
-extern SSL_METHOD *SSLv23_server_method(void);
-extern int SSL_library_init(void);
-extern void SSL_load_error_strings(void);
-extern int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int);
-extern int SSL_CTX_use_certificate_file(SSL_CTX *, const char *, int);
-extern int SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *);
-extern void SSL_CTX_set_default_passwd_cb(SSL_CTX *, mg_callback_pt);
-extern void SSL_CTX_free(SSL_CTX *);
-extern unsigned long ERR_get_error(void);
-extern char *ERR_error_string(unsigned long, char *);
-extern int CRYPTO_num_locks(void);
-extern void CRYPTO_set_locking_callback(void (*)(int, int, const char *, int));
-extern void CRYPTO_set_id_callback(unsigned long (*)(void));
-#else
-// Dynamically loaded SSL functionality
-struct ssl_func {
- const char *name; // SSL function name
- void (*ptr)(void); // Function pointer
-};
-
-#define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
-#define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
-#define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr)
-#define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr)
-#define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr)
-#define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5])
-#define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)
-#define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
-#define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
-#define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
-#define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr)
-#define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \
- const char *, int)) ssl_sw[11].ptr)
-#define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
- const char *, int)) ssl_sw[12].ptr)
-#define SSL_CTX_set_default_passwd_cb \
- (* (void (*)(SSL_CTX *, mg_callback_pt)) ssl_sw[13].ptr)
-#define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
-#define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
-#define SSL_CTX_use_certificate_chain_file \
- (* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr)
-
-#define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr)
-#define CRYPTO_set_locking_callback \
- (* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr)
-#define CRYPTO_set_id_callback \
- (* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)
-#define ERR_get_error (* (unsigned long (*)(void)) ssl_sw[3].ptr)
-#define ERR_error_string (* (char * (*)(unsigned long, char *)) ssl_sw[4].ptr)
-
-// set_ssl_option() function updates this array.
-// It loads SSL library dynamically and changes NULLs to the actual addresses
-// of respective functions. The macros above (like SSL_connect()) are really
-// just calling these functions indirectly via the pointer.
-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},
- {"SSL_CTX_free", NULL},
- {"SSL_load_error_strings", NULL},
- {"SSL_CTX_use_certificate_chain_file", NULL},
- {NULL, NULL}
-};
-
-// Similar array as ssl_sw. These functions could be located in different lib.
-static struct ssl_func crypto_sw[] = {
- {"CRYPTO_num_locks", NULL},
- {"CRYPTO_set_locking_callback", NULL},
- {"CRYPTO_set_id_callback", NULL},
- {"ERR_get_error", NULL},
- {"ERR_error_string", NULL},
- {NULL, NULL}
-};
-#endif // NO_SSL_DL
-
-static const char *month_names[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-// Unified socket address. For IPv6 support, add IPv6 address structure
-// in the union u.
-struct usa {
- socklen_t len;
- union {
- struct sockaddr sa;
- struct sockaddr_in sin;
- } u;
-};
-
-// Describes a string (chunk of memory).
-struct vec {
- const char *ptr;
- size_t len;
-};
-
-// Structure used by mg_stat() function. Uses 64 bit file length.
-struct mgstat {
- int is_directory; // Directory marker
- int64_t size; // File size
- time_t mtime; // Modification time
-};
-
-// Describes listening socket, or socket which was accept()-ed by the master
-// thread and queued for future handling by the worker thread.
-struct socket {
- struct socket *next; // Linkage
- SOCKET sock; // Listening socket
- struct usa lsa; // Local socket address
- struct usa rsa; // Remote socket address
- int is_ssl; // Is socket SSL-ed
- int is_proxy;
-};
-
-enum {
- CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER,
- PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS, ACCESS_LOG_FILE,
- SSL_CHAIN_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE,
- GLOBAL_PASSWORDS_FILE, INDEX_FILES,
- ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST, MAX_REQUEST_SIZE,
- EXTRA_MIME_TYPES, LISTENING_PORTS,
- DOCUMENT_ROOT, SSL_CERTIFICATE, NUM_THREADS, RUN_AS_USER,
- NUM_OPTIONS
-};
-
-static const char *config_options[] = {
- "C", "cgi_extensions", ".cgi,.pl,.php",
- "E", "cgi_environment", NULL,
- "G", "put_delete_passwords_file", NULL,
- "I", "cgi_interpreter", NULL,
- "P", "protect_uri", NULL,
- "R", "authentication_domain", "mydomain.com",
- "S", "ssi_extensions", ".shtml,.shtm",
- "a", "access_log_file", NULL,
- "c", "ssl_chain_file", NULL,
- "d", "enable_directory_listing", "yes",
- "e", "error_log_file", NULL,
- "g", "global_passwords_file", NULL,
- "i", "index_files", "index.html,index.htm,index.cgi",
- "k", "enable_keep_alive", "no",
- "l", "access_control_list", NULL,
- "M", "max_request_size", "16384",
- "m", "extra_mime_types", NULL,
- "p", "listening_ports", "8080",
- "r", "document_root", ".",
- "s", "ssl_certificate", NULL,
- "t", "num_threads", "10",
- "u", "run_as_user", NULL,
- NULL
-};
-#define ENTRIES_PER_CONFIG_OPTION 3
-
-struct mg_context {
- int stop_flag; // Should we stop event loop
- SSL_CTX *ssl_ctx; // SSL context
- char *config[NUM_OPTIONS]; // Mongoose configuration parameters
- mg_callback_pt user_callback; // User-defined callback function
-
- struct socket *listening_sockets;
-
- int num_threads; // Number of threads
- pthread_mutex_t mutex; // Protects (max|num)_threads
- pthread_cond_t cond; // Condvar for tracking workers terminations
-
- struct socket queue[20]; // Accepted sockets
- int sq_head; // Head of the socket queue
- int sq_tail; // Tail of the socket queue
- pthread_cond_t sq_full; // Singaled when socket is produced
- pthread_cond_t sq_empty; // Signaled when socket is consumed
-};
-
-struct mg_connection {
- struct mg_connection *peer; // Remote target in proxy mode
- struct mg_request_info request_info;
- struct mg_context *ctx;
- SSL *ssl; // SSL descriptor
- struct socket client; // Connected client
- time_t birth_time; // Time connection was accepted
- int64_t num_bytes_sent; // Total bytes sent to client
- int64_t content_len; // Content-Length header value
- int64_t consumed_content; // How many bytes of content is already read
- char *buf; // Buffer for received data
- int buf_size; // Buffer size
- int request_len; // Size of the request + headers in a buffer
- int data_len; // Total size of data in a buffer
-};
-
-const char **mg_get_valid_option_names(void) {
- return config_options;
-}
-
-static void *call_user(struct mg_connection *conn, enum mg_event event) {
- return conn->ctx->user_callback == NULL ? NULL :
- conn->ctx->user_callback(event, conn, &conn->request_info);
-}
-
-static int get_option_index(const char *name) {
- int i;
-
- for (i = 0; config_options[i] != NULL; i += ENTRIES_PER_CONFIG_OPTION) {
- if (strcmp(config_options[i], name) == 0 ||
- strcmp(config_options[i + 1], name) == 0) {
- return i / ENTRIES_PER_CONFIG_OPTION;
- }
- }
- return -1;
-}
-
-const char *mg_get_option(const struct mg_context *ctx, const char *name) {
- int i;
- if ((i = get_option_index(name)) == -1) {
- return NULL;
- } else if (ctx->config[i] == NULL) {
- return "";
- } else {
- return ctx->config[i];
- }
-}
-
-// Print error message to the opened error log stream.
-static void cry(struct mg_connection *conn, const char *fmt, ...) {
- char buf[BUFSIZ];
- va_list ap;
- FILE *fp;
- time_t timestamp;
-
- va_start(ap, fmt);
- (void) vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
-
- // Do not lock when getting the callback value, here and below.
- // I suppose this is fine, since function cannot disappear in the
- // same way string option can.
- conn->request_info.log_message = buf;
- if (call_user(conn, MG_EVENT_LOG) == NULL) {
- fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
- mg_fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
-
- if (fp != NULL) {
- flockfile(fp);
- timestamp = time(NULL);
-
- (void) fprintf(fp,
- "[%010lu] [error] [client %s] ",
- (unsigned long) timestamp,
- inet_ntoa(conn->client.rsa.u.sin.sin_addr));
-
- if (conn->request_info.request_method != NULL) {
- (void) fprintf(fp, "%s %s: ",
- conn->request_info.request_method,
- conn->request_info.uri);
- }
-
- (void) fprintf(fp, "%s", buf);
- fputc('\n', fp);
- funlockfile(fp);
- if (fp != stderr) {
- fclose(fp);
- }
- }
- }
- conn->request_info.log_message = NULL;
-}
-
-// Return OpenSSL error message
-static const char *ssl_error(void) {
- unsigned long err;
- err = ERR_get_error();
- return err == 0 ? "" : ERR_error_string(err, NULL);
-}
-
-// Return fake connection structure. Used for logging, if connection
-// is not applicable at the moment of logging.
-static struct mg_connection *fc(struct mg_context *ctx) {
- static struct mg_connection fake_connection;
- fake_connection.ctx = ctx;
- return &fake_connection;
-}
-
-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 lowercase(const char *s) {
- return tolower(* (unsigned char *) s);
-}
-
-static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
- int diff = 0;
-
- if (len > 0)
- do {
- diff = lowercase(s1++) - lowercase(s2++);
- } while (diff == 0 && s1[-1] != '\0' && --len > 0);
-
- return diff;
-}
-
-static int mg_strcasecmp(const char *s1, const char *s2) {
- int diff;
-
- do {
- diff = lowercase(s1++) - lowercase(s2++);
- } while (diff == 0 && s1[-1] != '\0');
-
- return diff;
-}
-
-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(struct mg_connection *conn, 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(conn, "vsnprintf error");
- n = 0;
- } else if (n >= (int) buflen) {
- cry(conn, "truncating vsnprintf buffer: [%.*s]",
- n > 200 ? 200 : n, buf);
- n = (int) buflen - 1;
- }
- buf[n] = '\0';
-
- return n;
-}
-
-static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
- const char *fmt, ...) {
- va_list ap;
- int n;
-
- va_start(ap, fmt);
- n = mg_vsnprintf(conn, buf, buflen, fmt, ap);
- va_end(ap);
-
- return n;
-}
-
-// 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);
-}
-
-// A helper function for traversing comma separated list of values.
-// It returns a list pointer shifted to the next value, of NULL if the end
-// of the list found.
-// Value is stored in val vector. If value has form "x=y", then eq_val
-// vector is initialized to point to the "y" part, and val vector length
-// is adjusted to point only to "x".
-static const char *next_option(const char *list, struct vec *val,
- struct vec *eq_val) {
- if (list == NULL || *list == '\0') {
- /* End of the list */
- list = NULL;
- } else {
- val->ptr = list;
- if ((list = strchr(val->ptr, ',')) != NULL) {
- /* Comma found. Store length and shift the list ptr */
- val->len = list - val->ptr;
- list++;
- } else {
- /* This value is the last one */
- list = val->ptr + strlen(val->ptr);
- val->len = list - val->ptr;
- }
-
- if (eq_val != NULL) {
- /*
- * Value has form "x=y", adjust pointers and lengths
- * so that val points to "x", and eq_val points to "y".
- */
- eq_val->len = 0;
- eq_val->ptr = memchr(val->ptr, '=', val->len);
- if (eq_val->ptr != NULL) {
- eq_val->ptr++; /* Skip over '=' character */
- eq_val->len = val->ptr + val->len - eq_val->ptr;
- val->len = (eq_val->ptr - val->ptr) - 1;
- }
- }
- }
-
- return list;
-}
-
-#if !defined(NO_CGI)
-static int match_extension(const char *path, const char *ext_list) {
- struct vec ext_vec;
- size_t path_len;
-
- path_len = strlen(path);
-
- while ((ext_list = next_option(ext_list, &ext_vec, NULL)) != NULL)
- if (ext_vec.len < path_len &&
- mg_strncasecmp(path + path_len - ext_vec.len,
- ext_vec.ptr, ext_vec.len) == 0)
- return 1;
-
- return 0;
-}
-#endif // !NO_CGI
-
-// HTTP 1.1 assumes keep alive if "Connection:" header is not set
-// This function must tolerate situations when connection info is not
-// set up, for example if request parsing failed.
-static int should_keep_alive(const struct mg_connection *conn) {
- const char *http_version = conn->request_info.http_version;
- const char *header = mg_get_header(conn, "Connection");
- return (header == NULL && http_version && !strcmp(http_version, "1.1")) ||
- (header != NULL && !strcmp(header, "keep-alive"));
-}
-
-static const char *suggest_connection_header(const struct mg_connection *conn) {
- return should_keep_alive(conn) ? "keep-alive" : "close";
-}
-
-static void send_http_error(struct mg_connection *conn, int status,
- const char *reason, const char *fmt, ...) {
- char buf[BUFSIZ];
- va_list ap;
- int len;
-
- conn->request_info.status_code = status;
-
- if (call_user(conn, MG_HTTP_ERROR) == NULL) {
- buf[0] = '\0';
- len = 0;
-
- /* Errors 1xx, 204 and 304 MUST NOT send a body */
- if (status > 199 && status != 204 && status != 304) {
- len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
- cry(conn, "%s", buf);
- buf[len++] = '\n';
-
- va_start(ap, fmt);
- len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap);
- va_end(ap);
- }
- DEBUG_TRACE(("[%s]", buf));
-
- mg_printf(conn, "HTTP/1.1 %d %s\r\n"
- "Content-Type: text/plain\r\n"
- "Content-Length: %d\r\n"
- "Connection: %s\r\n\r\n", status, reason, len,
- suggest_connection_header(conn));
- conn->num_bytes_sent += mg_printf(conn, "%s", buf);
- }
-}
-
-#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) {
- return CloseHandle(*mutex) == 0 ? -1 : 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 int pthread_cond_init(pthread_cond_t *cv, const void *unused) {
- unused = NULL;
- cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL);
- cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL);
- return cv->signal != NULL && cv->broadcast != NULL ? 0 : -1;
-}
-
-static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) {
- HANDLE handles[] = {cv->signal, cv->broadcast};
- ReleaseMutex(*mutex);
- WaitForMultipleObjects(2, handles, FALSE, INFINITE);
- return ReleaseMutex(*mutex) == 0 ? -1 : 0;
-}
-
-static int pthread_cond_signal(pthread_cond_t *cv) {
- return SetEvent(cv->signal) == 0 ? -1 : 0;
-}
-
-static int pthread_cond_broadcast(pthread_cond_t *cv) {
- // Implementation with PulseEvent() has race condition, see
- // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
- return PulseEvent(cv->broadcast) == 0 ? -1 : 0;
-}
-
-static int pthread_cond_destroy(pthread_cond_t *cv) {
- return CloseHandle(cv->signal) && CloseHandle(cv->broadcast) ? 0 : -1;
-}
-
-static pthread_t pthread_self(void) {
- return GetCurrentThreadId();
-}
-
-// For Windows, change all slashes to backslashes in path names.
-static void change_slashes_to_backslashes(char *path) {
- int i;
-
- for (i = 0; path[i] != '\0'; i++) {
- if (path[i] == '/')
- path[i] = '\\';
- // i > 0 check is to preserve UNC paths, like \\server\file.txt
- if (path[i] == '\\' && i > 0)
- while (path[i + 1] == '\\' || path[i + 1] == '/')
- (void) memmove(path + i + 1,
- path + i + 2, strlen(path + i + 1));
- }
-}
-
-// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
-// wbuf and wbuf_len is a target buffer and its length.
-static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) {
- char buf[PATH_MAX], *p;
-
- mg_strlcpy(buf, path, sizeof(buf));
- change_slashes_to_backslashes(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 || // No space at the end
- (*p == 0x2e && p > buf) || // No '.' but allow '.' as full path
- *p == 0x2b || // No '+'
- (*p & ~0x7f)) { // And generally no non-ascii chars
- (void) fprintf(stderr, "Rejecting suspicious path: [%s]", buf);
- buf[0] = '\0';
- }
-
- (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
-}
-
-#if defined(_WIN32_WCE)
-static time_t time(time_t *ptime) {
- time_t t;
- SYSTEMTIME st;
- FILETIME ft;
-
- GetSystemTime(&st);
- SystemTimeToFileTime(&st, &ft);
- t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
-
- if (ptime != NULL) {
- *ptime = t;
- }
-
- return t;
-}
-
-static time_t mktime(struct tm *ptm) {
- SYSTEMTIME st;
- FILETIME ft, lft;
-
- st.wYear = ptm->tm_year + 1900;
- st.wMonth = ptm->tm_mon + 1;
- st.wDay = ptm->tm_mday;
- st.wHour = ptm->tm_hour;
- st.wMinute = ptm->tm_min;
- st.wSecond = ptm->tm_sec;
- st.wMilliseconds = 0;
-
- SystemTimeToFileTime(&st, &ft);
- LocalFileTimeToFileTime(&ft, &lft);
- return (time_t) ((MAKEUQUAD(lft.dwLowDateTime, lft.dwHighDateTime) -
- EPOCH_DIFF) / RATE_DIFF);
-}
-
-static struct tm *localtime(const time_t *ptime, struct tm *ptm) {
- int64_t t = ((int64_t) *ptime) * RATE_DIFF + EPOCH_DIFF;
- FILETIME ft, lft;
- SYSTEMTIME st;
- TIME_ZONE_INFORMATION tzinfo;
-
- if (ptm == NULL) {
- return NULL;
- }
-
- * (int64_t *) &ft = t;
- FileTimeToLocalFileTime(&ft, &lft);
- FileTimeToSystemTime(&lft, &st);
- ptm->tm_year = st.wYear - 1900;
- ptm->tm_mon = st.wMonth - 1;
- ptm->tm_wday = st.wDayOfWeek;
- ptm->tm_mday = st.wDay;
- ptm->tm_hour = st.wHour;
- ptm->tm_min = st.wMinute;
- ptm->tm_sec = st.wSecond;
- ptm->tm_yday = 0; // hope nobody uses this
- ptm->tm_isdst =
- GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0;
-
- return ptm;
-}
-
-static size_t strftime(char *dst, size_t dst_size, const char *fmt,
- const struct tm *tm) {
- (void) snprintf(dst, dst_size, "implement strftime() for WinCE");
- return 0;
-}
-#endif
-
-static int mg_rename(const char* oldname, const char* newname) {
- wchar_t woldbuf[PATH_MAX];
- wchar_t wnewbuf[PATH_MAX];
-
- to_unicode(oldname, woldbuf, ARRAY_SIZE(woldbuf));
- to_unicode(newname, wnewbuf, ARRAY_SIZE(wnewbuf));
-
- return MoveFileW(woldbuf, wnewbuf) ? 0 : -1;
-}
-
-
-static FILE *mg_fopen(const char *path, const char *mode) {
- wchar_t wbuf[PATH_MAX], wmode[20];
-
- to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
- MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));
-
- return _wfopen(wbuf, wmode);
-}
-
-static int mg_stat(const char *path, struct mgstat *stp) {
- int ok = -1; // Error
- wchar_t wbuf[PATH_MAX];
- WIN32_FILE_ATTRIBUTE_DATA info;
-
- to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
-
- if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
- stp->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
- stp->mtime = SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
- info.ftLastWriteTime.dwHighDateTime);
- stp->is_directory =
- info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
- ok = 0; // Success
- }
-
- return ok;
-}
-
-static int mg_remove(const char *path) {
- wchar_t wbuf[PATH_MAX];
- to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
- return DeleteFileW(wbuf) ? 0 : -1;
-}
-
-static int mg_mkdir(const char *path, int mode) {
- char buf[PATH_MAX];
- wchar_t wbuf[PATH_MAX];
-
- mode = 0; // Unused
- mg_strlcpy(buf, path, sizeof(buf));
- change_slashes_to_backslashes(buf);
-
- (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
-
- return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
-}
-
-// Implementation of POSIX opendir/closedir/readdir for Windows.
-static DIR * opendir(const char *name) {
- DIR *dir = NULL;
- wchar_t wpath[PATH_MAX];
- DWORD attrs;
-
- if (name == NULL) {
- SetLastError(ERROR_BAD_ARGUMENTS);
- } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- } else {
- to_unicode(name, wpath, ARRAY_SIZE(wpath));
- attrs = GetFileAttributesW(wpath);
- if (attrs != 0xFFFFFFFF &&
- ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
- (void) wcscat(wpath, L"\\*");
- dir->handle = FindFirstFileW(wpath, &dir->info);
- dir->result.d_name[0] = '\0';
- } else {
- free(dir);
- dir = NULL;
- }
- }
-
- return dir;
-}
-
-static int closedir(DIR *dir) {
- int result = 0;
-
- if (dir != NULL) {
- if (dir->handle != INVALID_HANDLE_VALUE)
- result = FindClose(dir->handle) ? 0 : -1;
-
- free(dir);
- } else {
- result = -1;
- SetLastError(ERROR_BAD_ARGUMENTS);
- }
-
- return result;
-}
-
-struct dirent * readdir(DIR *dir) {
- struct dirent *result = 0;
-
- if (dir) {
- if (dir->handle != INVALID_HANDLE_VALUE) {
- result = &dir->result;
- (void) WideCharToMultiByte(CP_UTF8, 0,
- dir->info.cFileName, -1, result->d_name,
- sizeof(result->d_name), NULL, NULL);
-
- if (!FindNextFileW(dir->handle, &dir->info)) {
- (void) FindClose(dir->handle);
- dir->handle = INVALID_HANDLE_VALUE;
- }
-
- } else {
- SetLastError(ERROR_FILE_NOT_FOUND);
- }
- } else {
- SetLastError(ERROR_BAD_ARGUMENTS);
- }
-
- return result;
-}
-
-#define set_close_on_exec(fd) // No FD_CLOEXEC on Windows
-
-static int start_thread(struct mg_context *ctx, mg_thread_func_pt func,
- void *param) {
- HANDLE hThread;
- ctx = NULL; // Unused
-
- hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, param, 0,
- NULL);
- if (hThread != NULL) {
- (void) CloseHandle(hThread);
- }
-
- return hThread == NULL ? -1 : 0;
-}
-
-static HANDLE dlopen(const char *dll_name, int flags) {
- wchar_t wbuf[PATH_MAX];
- flags = 0; // Unused
- to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf));
- return LoadLibraryW(wbuf);
-}
-
-#if !defined(NO_CGI)
-#define SIGKILL 0
-static int kill(pid_t pid, int sig_num) {
- (void) TerminateProcess(pid, sig_num);
- (void) CloseHandle(pid);
- return 0;
-}
-
-static pid_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[PATH_MAX], buf[PATH_MAX];
- FILE *fp;
- STARTUPINFOA si;
- PROCESS_INFORMATION pi;
-
- envp = NULL; // Unused
-
- (void) memset(&si, 0, sizeof(si));
- (void) memset(&pi, 0, sizeof(pi));
-
- // TODO(lsm): redirect CGI errors to the error log file
- si.cb = sizeof(si);
- si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- si.wShowWindow = SW_HIDE;
-
- me = GetCurrentProcess();
- (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me,
- &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
- (void) 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->config[CGI_INTERPRETER];
- if (interp == NULL) {
- buf[2] = '\0';
- if ((fp = fopen(cmdline, "r")) != NULL) {
- (void) fgets(buf, sizeof(buf), fp);
- if (buf[0] != '#' || buf[1] != '!') {
- // First line does not start with "#!". Do not set interpreter.
- buf[2] = '\0';
- } else {
- // Trim whitespaces in interpreter name
- for (p = &buf[strlen(buf) - 1]; p > buf && isspace(*p); p--) {
- *p = '\0';
- }
- }
- (void) fclose(fp);
- }
- interp = buf + 2;
- }
-
- (void) mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s%s%c%s",
- interp, interp[0] == '\0' ? "" : " ", dir, DIRSEP, prog);
-
- DEBUG_TRACE(("Running [%s]", cmdline));
- if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
- CREATE_NEW_PROCESS_GROUP, envblk, dir, &si, &pi) == 0) {
- cry(conn, "%s: CreateProcess(%s): %d",
- __func__, cmdline, ERRNO);
- pi.hProcess = (pid_t) -1;
- } else {
- (void) close(fd_stdin);
- (void) close(fd_stdout);
- }
-
- (void) CloseHandle(si.hStdOutput);
- (void) CloseHandle(si.hStdInput);
- (void) CloseHandle(pi.hThread);
-
- return (pid_t) pi.hProcess;
-}
-#endif /* !NO_CGI */
-
-static int set_non_blocking_mode(SOCKET sock) {
- unsigned long on = 1;
- return ioctlsocket(sock, FIONBIO, &on);
-}
-
-#else
-static int mg_stat(const char *path, struct mgstat *stp) {
- struct stat st;
- int ok;
-
- if (stat(path, &st) == 0) {
- ok = 0;
- stp->size = st.st_size;
- stp->mtime = st.st_mtime;
- stp->is_directory = S_ISDIR(st.st_mode);
- } else {
- ok = -1;
- }
-
- return ok;
-}
-
-static void set_close_on_exec(int fd) {
- (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
-}
-
-static int start_thread(struct mg_context *ctx, mg_thread_func_pt func,
- 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);
- // TODO(lsm): figure out why mongoose dies on Linux if next line is enabled
- // (void) pthread_attr_setstacksize(&attr, sizeof(struct mg_connection) * 5);
-
- if ((retval = pthread_create(&thread_id, &attr, func, param)) != 0) {
- cry(fc(ctx), "%s: %s", __func__, strerror(retval));
- }
-
- return retval;
-}
-
-#ifndef NO_CGI
-static pid_t spawn_process(struct mg_connection *conn, const char *prog,
- __attribute__((unused)) char *envblk, char *envp[], int fd_stdin,
- int fd_stdout, const char *dir) {
- pid_t pid;
- const char *interp;
-
- // envblk = NULL; // Unused
-
- if ((pid = fork()) == -1) {
- // Parent
- send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
- } else if (pid == 0) {
- // Child
- if (chdir(dir) != 0) {
- cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
- } else if (dup2(fd_stdin, 0) == -1) {
- cry(conn, "%s: dup2(%d, 0): %s", __func__, fd_stdin, strerror(ERRNO));
- } else if (dup2(fd_stdout, 1) == -1) {
- cry(conn, "%s: dup2(%d, 1): %s", __func__, fd_stdout, strerror(ERRNO));
- } else {
- (void) dup2(fd_stdout, 2);
- (void) close(fd_stdin);
- (void) close(fd_stdout);
-
- // Execute CGI program. No need to lock: new process
- interp = conn->ctx->config[CGI_INTERPRETER];
- if (interp == NULL) {
- (void) execle(prog, prog, NULL, envp);
- cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
- } else {
- (void) execle(interp, interp, prog, NULL, envp);
- cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
- strerror(ERRNO));
- }
- }
- exit(EXIT_FAILURE);
- } else {
- // Parent. Close stdio descriptors
- (void) close(fd_stdin);
- (void) close(fd_stdout);
- }
-
- return pid;
-}
-#endif // !NO_CGI
-
-static int set_non_blocking_mode(SOCKET sock) {
- int flags;
-
- flags = fcntl(sock, F_GETFL, 0);
- (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
-
- return 0;
-}
-#endif // _WIN32
-
-// Write data to the IO channel - opened file descriptor, socket or SSL
-// descriptor. Return number of bytes written.
-static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
- int64_t len) {
- int64_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 (fp != NULL) {
- n = fwrite(buf + sent, 1, k, fp);
- if (ferror(fp))
- n = -1;
- } else {
- n = send(sock, buf + sent, k, 0);
- }
-
- if (n < 0)
- break;
-
- sent += n;
- }
-
- return sent;
-}
-
-// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
-// Return number of bytes read.
-static int pull(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int len) {
- int nread;
-
- if (ssl != NULL) {
- nread = SSL_read(ssl, buf, len);
- } else if (fp != NULL) {
- nread = fread(buf, 1, (size_t) len, fp);
- if (ferror(fp))
- nread = -1;
- } else {
- nread = recv(sock, buf, (size_t) len, 0);
- }
-
- return nread;
-}
-
-int mg_read(struct mg_connection *conn, void *buf, size_t len) {
- int n, buffered_len, nread;
- const char *buffered;
-
- assert(conn->content_len >= conn->consumed_content);
- DEBUG_TRACE(("%p %zu %ld %ld", buf, len,
- (long)conn->content_len, (long)conn->consumed_content));
- nread = 0;
- if (strcmp(conn->request_info.request_method, "POST") == 0 &&
- conn->consumed_content < conn->content_len) {
-
- // Adjust number of bytes to read.
- int64_t to_read = conn->content_len - conn->consumed_content;
- if (to_read < (int64_t) len) {
- len = (int) to_read;
- }
-
- // How many bytes of data we have buffered in the request buffer?
- buffered = conn->buf + conn->request_len;
- buffered_len = conn->data_len - conn->request_len;
- assert(buffered_len >= 0);
-
- // Return buffered data back if we haven't done that yet.
- if (conn->consumed_content < (int64_t) buffered_len) {
- buffered_len -= (int) conn->consumed_content;
- if (len < (size_t) buffered_len) {
- buffered_len = len;
- }
- memcpy(buf, buffered, buffered_len);
- len -= buffered_len;
- buf = (char *) buf + buffered_len;
- conn->consumed_content += buffered_len;
- nread = buffered_len;
- }
-
- // We have returned all buffered data. Read new data from the remote socket.
- while (len > 0) {
- n = pull(NULL, conn->client.sock, conn->ssl, (char *) buf, (int) len);
- if (n <= 0) {
- break;
- }
- buf = (char *) buf + n;
- conn->consumed_content += n;
- nread += n;
- len -= n;
- }
- }
- return nread;
-}
-
-int mg_write(struct mg_connection *conn, const void *buf, size_t len) {
- return (int) push(NULL, conn->client.sock, conn->ssl,
- (const char *) buf, (int64_t) len);
-}
-
-int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
- char buf[BUFSIZ];
- int len;
- va_list ap;
-
- va_start(ap, fmt);
- len = mg_vsnprintf(conn, buf, sizeof(buf), fmt, ap);
- va_end(ap);
-
- return mg_write(conn, buf, len);
-}
-
-// 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, int 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] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
- 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;
-}
-
-// Scan given buffer and fetch the value of the given 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.
-int mg_get_var(const char *buf, size_t buf_len, const char *name,
- char *dst, size_t dst_len) {
- const char *p, *e, *s;
- size_t name_len, len;
-
- name_len = strlen(name);
- e = buf + buf_len;
- len = -1;
- dst[0] = '\0';
-
- // buf is "var1=val1&var2=val2...". Find variable first
- for (p = buf; p != NULL && p + name_len < e; p++) {
- if ((p == buf || p[-1] == '&') && p[name_len] == '=' &&
- !mg_strncasecmp(name, p, name_len)) {
-
- // Point p to variable value
- p += name_len + 1;
-
- // Point s to the end of the value
- s = (const char *) memchr(p, '&', e - p);
- if (s == NULL) {
- s = e;
- }
- assert(s >= p);
-
- // Decode variable into destination buffer
- if ((size_t) (s - p) < dst_len) {
- len = url_decode(p, s - p, dst, dst_len, 1);
- }
- break;
- }
- }
-
- return len;
-}
-
-int mg_get_cookie(const struct mg_connection *conn, const char *cookie_name,
- char *dst, size_t dst_size) {
- const char *s, *p, *end;
- int name_len, len = -1;
-
- dst[0] = '\0';
- if ((s = mg_get_header(conn, "Cookie")) == NULL) {
- return 0;
- }
-
- name_len = strlen(cookie_name);
- end = s + strlen(s);
-
- for (; (s = strstr(s, cookie_name)) != NULL; s += name_len)
- if (s[name_len] == '=') {
- s += name_len + 1;
- if ((p = strchr(s, ' ')) == NULL)
- p = end;
- if (p[-1] == ';')
- p--;
- if (*s == '"' && p[-1] == '"' && p > s + 1) {
- s++;
- p--;
- }
- if ((size_t) (p - s) < dst_size) {
- len = (p - s) + 1;
- mg_strlcpy(dst, s, len);
- }
- break;
- }
-
- return len;
-}
-
-// Mongoose allows to specify multiple directories to serve,
-// like /var/www,/~bob=/home/bob. That means that root directory depends on URI.
-// This function returns root dir for given URI.
-static int get_document_root(const struct mg_connection *conn,
- struct vec *document_root) {
- const char *root, *uri;
- int len_of_matched_uri;
- struct vec uri_vec, path_vec;
-
- uri = conn->request_info.uri;
- len_of_matched_uri = 0;
- root = next_option(conn->ctx->config[DOCUMENT_ROOT], document_root, NULL);
-
- while ((root = next_option(root, &uri_vec, &path_vec)) != NULL) {
- if (memcmp(uri, uri_vec.ptr, uri_vec.len) == 0) {
- *document_root = path_vec;
- len_of_matched_uri = uri_vec.len;
- break;
- }
- }
-
- return len_of_matched_uri;
-}
-
-static void convert_uri_to_file_name(struct mg_connection *conn,
- const char *uri, char *buf,
- size_t buf_len) {
- struct vec vec;
- int match_len;
-
- match_len = get_document_root(conn, &vec);
- mg_snprintf(conn, buf, buf_len, "%.*s%s", vec.len, vec.ptr, uri + match_len);
-
-#ifdef _WIN32
- change_slashes_to_backslashes(buf);
-#endif /* _WIN32 */
-
- DEBUG_TRACE(("[%s] -> [%s], [%.*s]", uri, buf, (int) vec.len, vec.ptr));
-}
-
-static int sslize(struct mg_connection *conn, int (*func)(SSL *)) {
- return (conn->ssl = SSL_new(conn->ctx->ssl_ctx)) != NULL &&
- SSL_set_fd(conn->ssl, conn->client.sock) == 1 &&
- func(conn->ssl) == 1;
-}
-
-struct mg_connection *mg_connect(struct mg_connection *conn,
- const char *host, int port, int use_ssl) {
- struct mg_connection *newconn = NULL;
- struct sockaddr_in sin;
- struct hostent *he;
- int sock;
-
- if (conn->ctx->ssl_ctx == NULL && use_ssl) {
- cry(conn, "%s: SSL is not initialized", __func__);
- } else if ((he = gethostbyname(host)) == NULL) {
- cry(conn, "%s: gethostbyname(%s): %s", __func__, host, strerror(ERRNO));
- } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
- cry(conn, "%s: socket: %s", __func__, strerror(ERRNO));
- } else {
- sin.sin_family = AF_INET;
- sin.sin_port = htons((uint16_t) port);
- sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
- if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
- cry(conn, "%s: connect(%s:%d): %s", __func__, host, port,
- strerror(ERRNO));
- closesocket(sock);
- } else if ((newconn = calloc(1, sizeof(*newconn))) == NULL) {
- cry(conn, "%s: calloc: %s", __func__, strerror(ERRNO));
- closesocket(sock);
- } else {
- newconn->client.sock = sock;
- newconn->client.rsa.u.sin = sin;
- if (use_ssl) {
- sslize(newconn, SSL_connect);
- }
- }
- }
-
- return newconn;
-}
-
-// Check whether full request is buffered. Return:
-// -1 if request is malformed
-// 0 if request is not yet fully buffered
-// >0 actual request length, including last \r\n\r\n
-static int get_request_len(const char *buf, int buflen) {
- const char *s, *e;
- int len = 0;
-
- DEBUG_TRACE(("buf: %p, len: %d", buf, buflen));
- 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 month_number_to_month_name(const char *s) {
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE(month_names); 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 parse_date_string(const char *s) {
- time_t current_time;
- 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 = month_number_to_month_name(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);
-}
-
-// Protect against directory disclosure attack by removing '..',
-// excessive '/' and '\' characters
-static void remove_double_dots_and_double_slashes(char *s) {
- char *p = s;
-
- while (*s != '\0') {
- *p++ = *s++;
- if (s[-1] == '/' || s[-1] == '\\') {
- // Skip all following slashes and backslashes
- while (*s == '/' || *s == '\\') {
- s++;
- }
-
- // Skip all double-dots
- while (*s == '.' && s[1] == '.') {
- s += 2;
- }
- }
- }
- *p = '\0';
-}
-
-static const struct {
- const char *extension;
- size_t ext_len;
- const char *mime_type;
- size_t mime_type_len;
-} builtin_mime_types[] = {
- {".html", 5, "text/html", 9},
- {".htm", 4, "text/html", 9},
- {".shtm", 5, "text/html", 9},
- {".shtml", 6, "text/html", 9},
- {".css", 4, "text/css", 8},
- {".js", 3, "application/x-javascript", 24},
- {".ico", 4, "image/x-icon", 12},
- {".gif", 4, "image/gif", 9},
- {".jpg", 4, "image/jpeg", 10},
- {".jpeg", 5, "image/jpeg", 10},
- {".png", 4, "image/png", 9},
- {".svg", 4, "image/svg+xml", 13},
- {".torrent", 8, "application/x-bittorrent", 24},
- {".wav", 4, "audio/x-wav", 11},
- {".mp3", 4, "audio/x-mp3", 11},
- {".mid", 4, "audio/mid", 9},
- {".m3u", 4, "audio/x-mpegurl", 15},
- {".ram", 4, "audio/x-pn-realaudio", 20},
- {".xml", 4, "text/xml", 8},
- {".xslt", 5, "application/xml", 15},
- {".ra", 3, "audio/x-pn-realaudio", 20},
- {".doc", 4, "application/msword", 19},
- {".exe", 4, "application/octet-stream", 24},
- {".zip", 4, "application/x-zip-compressed", 28},
- {".xls", 4, "application/excel", 17},
- {".tgz", 4, "application/x-tar-gz", 20},
- {".tar", 4, "application/x-tar", 17},
- {".gz", 3, "application/x-gunzip", 20},
- {".arj", 4, "application/x-arj-compressed", 28},
- {".rar", 4, "application/x-arj-compressed", 28},
- {".rtf", 4, "application/rtf", 15},
- {".pdf", 4, "application/pdf", 15},
- {".swf", 4, "application/x-shockwave-flash",29},
- {".mpg", 4, "video/mpeg", 10},
- {".mpeg", 5, "video/mpeg", 10},
- {".asf", 4, "video/x-ms-asf", 14},
- {".avi", 4, "video/x-msvideo", 15},
- {".bmp", 4, "image/bmp", 9},
- {NULL, 0, NULL, 0}
-};
-
-// Look at the "path" extension and figure what mime type it has.
-// Store mime type in the vector.
-static void get_mime_type(struct mg_context *ctx, const char *path,
- struct vec *vec) {
- struct vec ext_vec, mime_vec;
- const char *list, *ext;
- size_t i, path_len;
-
- path_len = strlen(path);
-
- // Scan user-defined mime types first, in case user wants to
- // override default mime types.
- list = ctx->config[EXTRA_MIME_TYPES];
- while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
- // ext now points to the path suffix
- ext = path + path_len - ext_vec.len;
- if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
- *vec = mime_vec;
- return;
- }
- }
-
- // Now scan built-in mime types
- for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
- ext = path + (path_len - builtin_mime_types[i].ext_len);
- if (path_len > builtin_mime_types[i].ext_len &&
- mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) {
- vec->ptr = builtin_mime_types[i].mime_type;
- vec->len = builtin_mime_types[i].mime_type_len;
- return;
- }
- }
-
- // Nothing found. Fall back to "text/plain"
- vec->ptr = "text/plain";
- vec->len = 10;
-}
-
-#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) // Do nothing
-#else
-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
-
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-#define MD5STEP(f, w, x, y, z, data, s) \
- ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
-
-// 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;
-}
-
-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;
-}
-
-static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
- uint32_t t;
-
- t = ctx->bits[0];
- if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
- ctx->bits[1]++;
- ctx->bits[1] += len >> 29;
-
- t = (t >> 3) & 0x3f;
-
- 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;
- }
-
- while (len >= 64) {
- memcpy(ctx->in, buf, 64);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (uint32_t *) ctx->in);
- buf += 64;
- len -= 64;
- }
-
- memcpy(ctx->in, buf, len);
-}
-
-static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
- unsigned count;
- unsigned char *p;
-
- count = (ctx->bits[0] >> 3) & 0x3F;
-
- p = ctx->in + count;
- *p++ = 0x80;
- count = 64 - 1 - count;
- if (count < 8) {
- memset(p, 0, count);
- byteReverse(ctx->in, 16);
- MD5Transform(ctx->buf, (uint32_t *) ctx->in);
- memset(ctx->in, 0, 56);
- } else {
- memset(p, 0, count - 8);
- }
- byteReverse(ctx->in, 14);
-
- ((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));
-}
-#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. Buffer must be 33 bytes.
-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 int check_password(const char *method, const char *ha1, const char *uri,
- const char *nonce, const char *nc, const char *cnonce,
- const char *qop, const char *response) {
- char ha2[32 + 1], expected_response[32 + 1];
-
- // NOTE(lsm): due to a bug in MSIE, we do not compare the URI
- // TODO(lsm): check for authentication timeout
- if (// strcmp(dig->uri, c->ouri) != 0 ||
- strlen(response) != 32
- // || now - strtoul(dig->nonce, NULL, 10) > 3600
- ) {
- return 0;
- }
-
- mg_md5(ha2, method, ":", uri, NULL);
- mg_md5(expected_response, ha1, ":", nonce, ":", nc,
- ":", cnonce, ":", qop, ":", ha2, NULL);
-
- return mg_strcasecmp(response, expected_response) == 0;
-}
-
-// 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_connection *conn, const char *path) {
- struct mg_context *ctx = conn->ctx;
- char name[PATH_MAX];
- const char *p, *e;
- struct mgstat st;
- FILE *fp;
-
- if (ctx->config[GLOBAL_PASSWORDS_FILE] != NULL) {
- // Use global passwords file
- fp = mg_fopen(ctx->config[GLOBAL_PASSWORDS_FILE], "r");
- if (fp == NULL)
- cry(fc(ctx), "fopen(%s): %s",
- ctx->config[GLOBAL_PASSWORDS_FILE], strerror(ERRNO));
- } else if (!mg_stat(path, &st) && st.is_directory) {
- (void) mg_snprintf(conn, name, sizeof(name), "%s%c%s",
- path, DIRSEP, PASSWORDS_FILE_NAME);
- fp = mg_fopen(name, "r");
- } else {
- // Try to find .htpasswd in requested directory.
- for (p = path, e = p + strlen(p) - 1; e > p; e--)
- if (IS_DIRSEP_CHAR(*e))
- break;
- (void) mg_snprintf(conn, name, sizeof(name), "%.*s%c%s",
- (int) (e - p), p, DIRSEP, PASSWORDS_FILE_NAME);
- fp = mg_fopen(name, "r");
- }
-
- return fp;
-}
-
-// Parsed Authorization header
-struct ah {
- char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
-};
-
-static int parse_auth_header(struct mg_connection *conn, char *buf,
- size_t buf_size, struct ah *ah) {
- char *name, *value, *s;
- const char *auth_header;
-
- if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
- mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
- return 0;
- }
-
- // 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, " ");
-
- // Handle commas: Digest username="a", realm="b", ...
- if (value[strlen(value) - 1] == ',') {
- value[strlen(value) - 1] = '\0';
- }
-
- // Trim double quotes around values
- 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 1;
-}
-
-// Authorize against the opened passwords file. Return 1 if authorized.
-static int authorize(struct mg_connection *conn, FILE *fp) {
- struct ah ah;
- char line[256], f_user[256], ha1[256], f_domain[256], buf[BUFSIZ];
-
- if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) {
- return 0;
- }
-
- // Loop over passwords file
- while (fgets(line, sizeof(line), fp) != NULL) {
- if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
- continue;
- }
-
- if (!strcmp(ah.user, f_user) &&
- !strcmp(conn->ctx->config[AUTHENTICATION_DOMAIN], f_domain))
- return check_password(
- conn->request_info.request_method,
- ha1, ah.uri, ah.nonce, ah.nc, ah.cnonce, ah.qop,
- ah.response);
- }
-
- return 0;
-}
-
-// Return 1 if request is authorised, 0 otherwise.
-static int check_authorization(struct mg_connection *conn, const char *path) {
- FILE *fp;
- char fname[PATH_MAX];
- struct vec uri_vec, filename_vec;
- const char *list;
- int authorized;
-
- fp = NULL;
- authorized = 1;
-
- list = conn->ctx->config[PROTECT_URI];
- while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
- if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
- (void) mg_snprintf(conn, fname, sizeof(fname), "%.*s",
- filename_vec.len, filename_vec.ptr);
- if ((fp = mg_fopen(fname, "r")) == NULL) {
- cry(conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno));
- }
- break;
- }
- }
-
- if (fp == NULL) {
- fp = open_auth_file(conn, path);
- }
-
- if (fp != NULL) {
- authorized = authorize(conn, fp);
- (void) fclose(fp);
- }
-
- return authorized;
-}
-
-static void send_authorization_request(struct mg_connection *conn) {
- conn->request_info.status_code = 401;
- (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->config[AUTHENTICATION_DOMAIN],
- (unsigned long) time(NULL));
-}
-
-static int is_authorized_for_put(struct mg_connection *conn) {
- FILE *fp;
- int ret = 0;
-
- fp = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL ? NULL :
- mg_fopen(conn->ctx->config[PUT_DELETE_PASSWORDS_FILE], "r");
-
- if (fp != NULL) {
- ret = authorize(conn, fp);
- (void) fclose(fp);
- }
-
- return ret;
-}
-
-int mg_modify_passwords_file(struct mg_context *ctx, const char *fname,
- const char *user, const char *pass) {
- int found;
- char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
- const char *domain;
- FILE *fp, *fp2;
-
- found = 0;
- fp = fp2 = NULL;
- domain = ctx->config[AUTHENTICATION_DOMAIN];
-
- // Regard empty password as no password - remove user record.
- if (pass[0] == '\0') {
- pass = NULL;
- }
-
- (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
-
- // Create the file if does not exist
- if ((fp = mg_fopen(fname, "a+")) != NULL) {
- (void) fclose(fp);
- }
-
- // Open the given file and temporary file
- if ((fp = mg_fopen(fname, "r")) == NULL) {
- cry(fc(ctx), "Cannot open %s: %s", fname, strerror(errno));
- return 0;
- } else if ((fp2 = mg_fopen(tmp, "w+")) == NULL) {
- cry(fc(ctx), "Cannot open %s: %s", tmp, strerror(errno));
- fclose(fp);
- return 0;
- }
-
- // Copy the stuff to temporary file
- while (fgets(line, sizeof(line), fp) != NULL) {
- if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
- continue;
- }
-
- if (!strcmp(u, user) && !strcmp(d, domain)) {
- found++;
- if (pass != NULL) {
- mg_md5(ha1, user, ":", domain, ":", pass, NULL);
- fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
- }
- } else {
- (void) fprintf(fp2, "%s", line);
- }
- }
-
- // If new user, just add it
- if (!found && pass != NULL) {
- mg_md5(ha1, user, ":", domain, ":", pass, NULL);
- (void) fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
- }
-
- // Close files
- (void) fclose(fp);
- (void) fclose(fp2);
-
- // Put the temp file in place of real file
- (void) mg_remove(fname);
- (void) mg_rename(tmp, fname);
-
- return 1;
-}
-
-struct de {
- struct mg_connection *conn;
- char *file_name;
- struct mgstat st;
-};
-
-static void url_encode(const char *src, char *dst, size_t dst_len) {
- static const char *dont_escape = "._-$,;~()";
- static const char *hex = "0123456789abcdef";
- const char *end = dst + dst_len - 1;
-
- for (; *src != '\0' && dst < end; src++, dst++) {
- if (isalnum(*(unsigned char *) src) ||
- strchr(dont_escape, * (unsigned char *) src) != NULL) {
- *dst = *src;
- } else if (dst + 2 < end) {
- dst[0] = '%';
- dst[1] = hex[(* (unsigned char *) src) >> 4];
- dst[2] = hex[(* (unsigned char *) src) & 0xf];
- dst += 2;
- }
- }
-
- *dst = '\0';
-}
-
-static void print_dir_entry(struct de *de) {
- char size[64], mod[64], href[PATH_MAX];
-
- if (de->st.is_directory) {
- (void) mg_snprintf(de->conn, size, sizeof(size), "%s", "[DIRECTORY]");
- } else {
- // We use (signed) cast below because MSVC 6 compiler cannot
- // convert unsigned __int64 to double. Sigh.
- if (de->st.size < 1024) {
- (void) mg_snprintf(de->conn, size, sizeof(size),
- "%lu", (unsigned long) de->st.size);
- } else if (de->st.size < 1024 * 1024) {
- (void) mg_snprintf(de->conn, size, sizeof(size),
- "%.1fk", (double) de->st.size / 1024.0);
- } else if (de->st.size < 1024 * 1024 * 1024) {
- (void) mg_snprintf(de->conn, size, sizeof(size),
- "%.1fM", (double) de->st.size / 1048576);
- } else {
- (void) mg_snprintf(de->conn, size, sizeof(size),
- "%.1fG", (double) de->st.size / 1073741824);
- }
- }
- (void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.mtime));
- url_encode(de->file_name, href, sizeof(href));
- de->conn->num_bytes_sent += mg_printf(de->conn,
- "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
- "<td> %s</td><td> %s</td></tr>\n",
- de->conn->request_info.uri, href, de->st.is_directory ? "/" : "",
- de->file_name, de->st.is_directory ? "/" : "", mod, size);
-}
-
-// This function is called from send_directory() and used for
-// sorting directory entries by size, or name, or modification time.
-// On windows, __cdecl specification is needed in case if project is built
-// with __stdcall convention. qsort always requires __cdels callback.
-static int WINCDECL compare_dir_entries(const void *p1, const void *p2) {
- const struct de *a = (struct de *) p1, *b = (struct de *) p2;
- const char *query_string = a->conn->request_info.query_string;
- int cmp_result = 0;
-
- if (query_string == NULL) {
- query_string = "na";
- }
-
- if (a->st.is_directory && !b->st.is_directory) {
- return -1; // Always put directories on top
- } else if (!a->st.is_directory && b->st.is_directory) {
- return 1; // Always put directories on top
- } else if (*query_string == 'n') {
- cmp_result = strcmp(a->file_name, b->file_name);
- } else if (*query_string == 's') {
- cmp_result = a->st.size == b->st.size ? 0 :
- a->st.size > b->st.size ? 1 : -1;
- } else if (*query_string == 'd') {
- cmp_result = a->st.mtime == b->st.mtime ? 0 :
- a->st.mtime > b->st.mtime ? 1 : -1;
- }
-
- return query_string[1] == 'd' ? -cmp_result : cmp_result;
-}
-
-static void handle_directory_request(struct mg_connection *conn,
- const char *dir) {
- struct dirent *dp;
- DIR *dirp;
- struct de *entries = NULL;
- char path[PATH_MAX];
- int i, sort_direction, num_entries = 0, arr_size = 128;
-
- if ((dirp = opendir(dir)) == NULL) {
- send_http_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_http_error(conn, 500, "Cannot open directory",
- "%s", "Error: cannot allocate memory");
- closedir(dirp);
- return;
- }
-
- mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, DIRSEP, dp->d_name);
-
- // If we don't memset stat structure to zero, mtime will have
- // garbage and strftime() will segfault later on in
- // print_dir_entry(). memset is required only if mg_stat()
- // fails. For more details, see
- // http://code.google.com/p/mongoose/issues/detail?id=79
- if (mg_stat(path, &entries[num_entries].st) != 0) {
- memset(&entries[num_entries].st, 0, sizeof(entries[num_entries].st));
- }
-
- entries[num_entries].conn = conn;
- entries[num_entries].file_name = mg_strdup(dp->d_name);
- num_entries++;
- }
- (void) closedir(dirp);
-
- 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><table cellpadding=\"0\">"
- "<tr><th><a href=\"?n%c\">Name</a></th>"
- "<th><a href=\"?d%c\">Modified</a></th>"
- "<th><a href=\"?s%c\">Size</a></th></tr>"
- "<tr><td colspan=\"3\"><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><a href=\"%s%s\">%s</a></td>"
- "<td> %s</td><td> %s</td></tr>\n",
- conn->request_info.uri, "..", "Parent directory", "-", "-");
-
- // Sort and print directory entries
- if(entries!=NULL){
- qsort(entries, num_entries, sizeof(entries[0]), compare_dir_entries);
- 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_file_data(struct mg_connection *conn, FILE *fp, int64_t len) {
- char buf[BUFSIZ];
- int to_read, num_read, num_written;
-
- while (len > 0) {
- // Calculate how much to read from the file in the buffer
- to_read = sizeof(buf);
- if ((int64_t) to_read > len)
- to_read = (int) len;
-
- // Read from file, exit the loop on error
- if ((num_read = fread(buf, 1, to_read, fp)) == 0)
- break;
-
- // Send read bytes to the client, exit the loop on error
- if ((num_written = mg_write(conn, buf, num_read)) != num_read)
- break;
-
- // Both read and were successful, adjust counters
- conn->num_bytes_sent += num_written;
- len -= num_written;
- }
-}
-
-static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
- return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
-}
-
-static void handle_file_request(struct mg_connection *conn, const char *path,
- struct mgstat *stp) {
- char date[64], lm[64], etag[64], range[64];
- const char *fmt = "%a, %d %b %Y %H:%M:%S %Z", *msg = "OK", *hdr;
- time_t curtime = time(NULL);
- int64_t cl, r1, r2;
- struct vec mime_vec;
- FILE *fp;
- int n;
-
- get_mime_type(conn->ctx, path, &mime_vec);
- cl = stp->size;
- conn->request_info.status_code = 200;
- range[0] = '\0';
-
- if ((fp = mg_fopen(path, "rb")) == NULL) {
- send_http_error(conn, 500, http_500_error,
- "fopen(%s): %s", path, strerror(ERRNO));
- return;
- }
- set_close_on_exec(fileno(fp));
-
- // If Range: header specified, act accordingly
- r1 = r2 = 0;
- hdr = mg_get_header(conn, "Range");
- if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0) {
- conn->request_info.status_code = 206;
- (void) fseeko(fp, (off_t) r1, SEEK_SET);
- cl = n == 2 ? r2 - r1 + 1: cl - r1;
- (void) mg_snprintf(conn, range, sizeof(range),
- "Content-Range: bytes "
- "%" INT64_FMT "-%"
- INT64_FMT "/%" INT64_FMT "\r\n",
- r1, r1 + cl - 1, stp->size);
- 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->mtime));
- (void) mg_snprintf(conn, etag, sizeof(etag), "%lx.%lx",
- (unsigned long) stp->mtime, (unsigned long) stp->size);
-
- (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: %" INT64_FMT "\r\n"
- "Connection: %s\r\n"
- "Accept-Ranges: bytes\r\n"
- "%s\r\n",
- conn->request_info.status_code, msg, date, lm, etag,
- mime_vec.len, mime_vec.ptr, cl, suggest_connection_header(conn), range);
-
- if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
- send_file_data(conn, fp, cl);
- }
- (void) fclose(fp);
-}
-
-// Parse HTTP headers from the given buffer, advance buffer to the point
-// where parsing stopped.
-static void parse_http_headers(char **buf, struct mg_request_info *ri) {
- int i;
-
- for (i = 0; i < (int) ARRAY_SIZE(ri->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 int is_valid_http_method(const char *method) {
- return !strcmp(method, "GET") || !strcmp(method, "POST") ||
- !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
- !strcmp(method, "PUT") || !strcmp(method, "DELETE");
-}
-
-// Parse HTTP request, fill in mg_request_info structure.
-static int parse_http_request(char *buf, struct mg_request_info *ri) {
- int status = 0;
-
- // RFC says that all initial whitespaces should be ingored
- while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
- buf++;
- }
-
- ri->request_method = skip(&buf, " ");
- ri->uri = skip(&buf, " ");
- ri->http_version = skip(&buf, "\r\n");
-
- if (is_valid_http_method(ri->request_method) &&
- strncmp(ri->http_version, "HTTP/", 5) == 0) {
- ri->http_version += 5; /* Skip "HTTP/" */
- parse_http_headers(&buf, ri);
- status = 1;
- }
-
- return status;
-}
-
-// Keep reading the input (either opened file descriptor fd, or socket sock,
-// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
-// buffer (which marks the end of HTTP request). Buffer buf may already
-// have some data. The length of the data is stored in nread.
-// Upon every read operation, increase nread by the number of bytes read.
-static int read_request(FILE *fp, 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(fp, sock, ssl, buf + *nread, bufsiz - *nread);
- if (n <= 0) {
- break;
- } else {
- *nread += n;
- request_len = get_request_len(buf, *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.
-// If the file is found, it's stats is returned in stp.
-static int substitute_index_file(struct mg_connection *conn, char *path,
- size_t path_len, struct mgstat *stp) {
- const char *list = conn->ctx->config[INDEX_FILES];
- struct mgstat st;
- struct vec filename_vec;
- size_t n = strlen(path);
- int found = 0;
-
- // The 'path' given to us points to the directory. Remove all trailing
- // directory separator characters from the end of the path, and
- // then append single directory separator character.
- while (n > 0 && IS_DIRSEP_CHAR(path[n - 1])) {
- n--;
- }
- path[n] = DIRSEP;
-
- // Traverse index files list. For each entry, append it to the given
- // path and see if the file exists. If it exists, break the loop
- while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
-
- // Ignore too long entries that may overflow path buffer
- if (filename_vec.len > path_len - n)
- continue;
-
- // Prepare full path to the index file
- (void) mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
-
- // Does it exist?
- if (mg_stat(path, &st) == 0) {
- // Yes it does, break the loop
- *stp = st;
- found = 1;
- break;
- }
- }
-
- // If no index file exists, restore directory path
- if (!found) {
- path[n] = '\0';
- }
-
- return found;
-}
-
-// Return True if we should reply 304 Not Modified.
-static int is_not_modified(const struct mg_connection *conn,
- const struct mgstat *stp) {
- const char *ims = mg_get_header(conn, "If-Modified-Since");
- return ims != NULL && stp->mtime <= parse_date_string(ims);
-}
-
-static int forward_body_data(struct mg_connection *conn, FILE *fp,
- SOCKET sock, SSL *ssl) {
- const char *expect, *buffered;
- char buf[BUFSIZ];
- int to_read, nread, buffered_len, success = 0;
-
- expect = mg_get_header(conn, "Expect");
- assert(fp != NULL);
-
- if (conn->content_len == -1) {
- send_http_error(conn, 411, "Length Required", "");
- } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
- send_http_error(conn, 417, "Expectation Failed", "");
- } else {
- if (expect != NULL) {
- (void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
- }
-
- buffered = conn->buf + conn->request_len;
- buffered_len = conn->data_len - conn->request_len;
- assert(buffered_len >= 0);
- assert(conn->consumed_content == 0);
-
- if (buffered_len > 0) {
- if ((int64_t) buffered_len > conn->content_len) {
- buffered_len = (int) conn->content_len;
- }
- push(fp, sock, ssl, buffered, (int64_t) buffered_len);
- conn->consumed_content += buffered_len;
- }
-
- while (conn->consumed_content < conn->content_len) {
- to_read = sizeof(buf);
- if ((int64_t) to_read > conn->content_len - conn->consumed_content) {
- to_read = (int) (conn->content_len - conn->consumed_content);
- }
- nread = pull(NULL, conn->client.sock, conn->ssl, buf, to_read);
- if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) {
- break;
- }
- conn->consumed_content += nread;
- }
-
- if (conn->consumed_content == conn->content_len) {
- success = 1;
- }
-
- // Each error code path in this function must send an error
- if (!success) {
- send_http_error(conn, 577, http_500_error, "");
- }
- }
-
- return success;
-}
-
-#if !defined(NO_CGI)
-// This structure helps to create an environment for the spawned CGI program.
-// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
-// last element must be NULL.
-// However, on Windows there is a requirement that all these VARIABLE=VALUE\0
-// strings must reside in a contiguous buffer. The end of the buffer is
-// marked by two '\0' characters.
-// We satisfy both worlds: we create an envp array (which is vars), all
-// entries are actually pointers inside buf.
-struct cgi_env_block {
- struct mg_connection *conn;
- char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer
- int len; // Space taken
- char *vars[MAX_CGI_ENVIR_VARS]; // char **envp
- int nvars; // Number of variables
-};
-
-// Append VARIABLE=VALUE\0 string to the buffer, and add a respective
-// pointer into the vars array.
-static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
- int n, space;
- char *added;
- va_list ap;
-
- // Calculate how much space is left in the buffer
- space = sizeof(block->buf) - block->len - 2;
- assert(space >= 0);
-
- // Make a pointer to the free space int the buffer
- added = block->buf + block->len;
-
- // Copy VARIABLE=VALUE\0 string into the free space
- va_start(ap, fmt);
- n = mg_vsnprintf(block->conn, added, (size_t) space, fmt, ap);
- va_end(ap);
-
- // Make sure we do not overflow buffer and the envp array
- if (n > 0 && n < space &&
- block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
- // Append a pointer to the added string into the envp array
- block->vars[block->nvars++] = block->buf + block->len;
- // Bump up used length counter. Include \0 terminator
- block->len += n + 1;
- }
-
- return added;
-}
-
-static void prepare_cgi_environment(struct mg_connection *conn,
- const char *prog,
- struct cgi_env_block *blk) {
- const char *s, *slash;
- struct vec var_vec, root;
- char *p;
- int i;
-
- blk->len = blk->nvars = 0;
- blk->conn = conn;
-
- get_document_root(conn, &root);
-
- addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]);
- addenv(blk, "SERVER_ROOT=%.*s", root.len, root.ptr);
- addenv(blk, "DOCUMENT_ROOT=%.*s", root.len, root.ptr);
-
- // 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"); // For PHP
- addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.u.sin.sin_port));
- addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method);
- addenv(blk, "REMOTE_ADDR=%s",
- inet_ntoa(conn->client.rsa.u.sin.sin_addr));
- addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port);
- addenv(blk, "REQUEST_URI=%s", conn->request_info.uri);
-
- // SCRIPT_NAME
- assert(conn->request_info.uri[0] == '/');
- slash = strrchr(conn->request_info.uri, '/');
- if ((s = strrchr(prog, '/')) == NULL)
- s = prog;
- addenv(blk, "SCRIPT_NAME=%.*s%s", slash - conn->request_info.uri,
- conn->request_info.uri, s);
-
- addenv(blk, "SCRIPT_FILENAME=%s", prog);
- addenv(blk, "PATH_TRANSLATED=%s", prog);
- addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
-
- 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 = (char) toupper(* (unsigned char *) p);
- }
- }
-
- // Add user-specified variables
- s = conn->ctx->config[CGI_ENVIRONMENT];
- while ((s = next_option(s, &var_vec, NULL)) != NULL) {
- addenv(blk, "%.*s", var_vec.len, var_vec.ptr);
- }
-
- 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 handle_cgi_request(struct mg_connection *conn, const char *prog) {
- int headers_len, data_len, i, fd_stdin[2], fd_stdout[2];
- const char *status;
- char buf[BUFSIZ], *pbuf, dir[PATH_MAX], *p;
- struct mg_request_info ri;
- struct cgi_env_block blk;
- FILE *in, *out;
- pid_t pid;
-
- prepare_cgi_environment(conn, prog, &blk);
-
- // CGI must be executed
<TRUNCATED>
[5/6] celix git commit: CELIX-451: Adds celix_bundle_dir for copying
files. Also remove mongoose example and adds a new civetweb example.
Posted by pn...@apache.org.
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/civetweb/civetweb.c
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/civetweb/civetweb.c b/examples/celix-examples/civetweb/civetweb/civetweb.c
new file mode 100644
index 0000000..259663a
--- /dev/null
+++ b/examples/celix-examples/civetweb/civetweb/civetweb.c
@@ -0,0 +1,19792 @@
+/* Copyright (c) 2013-2018 the Civetweb developers
+ * Copyright (c) 2004-2013 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.
+ */
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+/* Disable unused macros warnings - not all defines are required
+ * for all systems and all compilers. */
+#pragma GCC diagnostic ignored "-Wunused-macros"
+/* A padding warning is just plain useless */
+#pragma GCC diagnostic ignored "-Wpadded"
+#endif
+
+#if defined(__clang__) /* GCC does not (yet) support this pragma */
+/* We must set some flags for the headers we include. These flags
+ * are reserved ids according to C99, so we need to disable a
+ * warning for that. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wreserved-id-macro"
+#endif
+
+#if defined(_WIN32)
+#if !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
+#endif
+#if !defined(_WIN32_WINNT) /* defined for tdm-gcc so we can use getnameinfo */
+#define _WIN32_WINNT 0x0501
+#endif
+#else
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE /* for setgroups(), pthread_setname_np() */
+#endif
+#if defined(__linux__) && !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
+#endif
+#if !defined(_LARGEFILE_SOURCE)
+#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */
+#endif
+#if !defined(_FILE_OFFSET_BITS)
+#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */
+#endif
+#if !defined(__STDC_FORMAT_MACROS)
+#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */
+#endif
+#if !defined(__STDC_LIMIT_MACROS)
+#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */
+#endif
+#if !defined(_DARWIN_UNLIMITED_SELECT)
+#define _DARWIN_UNLIMITED_SELECT
+#endif
+#if defined(__sun)
+#define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */
+#define __inline inline /* not recognized on older compiler versions */
+#endif
+#endif
+
+#if defined(__clang__)
+/* Enable reserved-id-macro warning again. */
+#pragma GCC diagnostic pop
+#endif
+
+
+#if defined(USE_LUA)
+#define USE_TIMERS
+#endif
+
+#if defined(_MSC_VER)
+/* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */
+#pragma warning(disable : 4306)
+/* conditional expression is constant: introduced by FD_SET(..) */
+#pragma warning(disable : 4127)
+/* non-constant aggregate initializer: issued due to missing C99 support */
+#pragma warning(disable : 4204)
+/* padding added after data member */
+#pragma warning(disable : 4820)
+/* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */
+#pragma warning(disable : 4668)
+/* no function prototype given: converting '()' to '(void)' */
+#pragma warning(disable : 4255)
+/* function has been selected for automatic inline expansion */
+#pragma warning(disable : 4711)
+#endif
+
+
+/* This code uses static_assert to check some conditions.
+ * Unfortunately some compilers still do not support it, so we have a
+ * replacement function here. */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ > 201100L
+#define mg_static_assert _Static_assert
+#elif defined(__cplusplus) && __cplusplus >= 201103L
+#define mg_static_assert static_assert
+#else
+char static_assert_replacement[1];
+#define mg_static_assert(cond, txt) \
+ extern char static_assert_replacement[(cond) ? 1 : -1]
+#endif
+
+mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8,
+"int data type size check");
+mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8,
+"pointer data type size check");
+mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
+
+
+/* Alternative queue is well tested and should be the new default */
+#if defined(NO_ALTERNATIVE_QUEUE)
+#if defined(ALTERNATIVE_QUEUE)
+#error "Define ALTERNATIVE_QUEUE or NO_ALTERNATIVE_QUEUE or none, but not both"
+#endif
+#else
+#define ALTERNATIVE_QUEUE
+#endif
+
+
+/* DTL -- including winsock2.h works better if lean and mean */
+#if !defined(WIN32_LEAN_AND_MEAN)
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#if defined(__SYMBIAN32__)
+/* According to https://en.wikipedia.org/wiki/Symbian#History,
+ * Symbian is no longer maintained since 2014-01-01.
+ * Recent versions of CivetWeb are no longer tested for Symbian.
+ * It makes no sense, to support an abandoned operating system.
+ */
+#error "Symbian is no longer maintained. CivetWeb no longer supports Symbian."
+#define NO_SSL /* SSL is not supported */
+#define NO_CGI /* CGI is not supported */
+#define PATH_MAX FILENAME_MAX
+#endif /* __SYMBIAN32__ */
+
+
+#if !defined(CIVETWEB_HEADER_INCLUDED)
+/* Include the header file here, so the CivetWeb interface is defined for the
+ * entire implementation, including the following forward definitions. */
+#include "civetweb.h"
+#endif
+
+#if !defined(DEBUG_TRACE)
+#if defined(DEBUG)
+static void DEBUG_TRACE_FUNC(const char *func,
+unsigned line,
+PRINTF_FORMAT_STRING(const char *fmt),
+...) PRINTF_ARGS(3, 4);
+
+#define DEBUG_TRACE(fmt, ...) \
+ DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
+
+#define NEED_DEBUG_TRACE_FUNC
+
+#else
+#define DEBUG_TRACE(fmt, ...) \
+ do { \
+ } while (0)
+#endif /* DEBUG */
+#endif /* DEBUG_TRACE */
+
+
+#if !defined(DEBUG_ASSERT)
+#if defined(DEBUG)
+#define DEBUG_ASSERT(cond) \
+ do { \
+ if (!(cond)) { \
+ DEBUG_TRACE("ASSERTION FAILED: %s", #cond); \
+ exit(2); /* Exit with error */ \
+ } \
+ } while (0)
+#else
+#define DEBUG_ASSERT(cond)
+#endif /* DEBUG */
+#endif
+
+
+#if !defined(IGNORE_UNUSED_RESULT)
+#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
+#endif
+
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+
+/* GCC unused function attribute seems fundamentally broken.
+ * Several attempts to tell the compiler "THIS FUNCTION MAY BE USED
+ * OR UNUSED" for individual functions failed.
+ * Either the compiler creates an "unused-function" warning if a
+ * function is not marked with __attribute__((unused)).
+ * On the other hand, if the function is marked with this attribute,
+ * but is used, the compiler raises a completely idiotic
+ * "used-but-marked-unused" warning - and
+ * #pragma GCC diagnostic ignored "-Wused-but-marked-unused"
+ * raises error: unknown option after "#pragma GCC diagnostic".
+ * Disable this warning completely, until the GCC guys sober up
+ * again.
+ */
+
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#define FUNCTION_MAY_BE_UNUSED /* __attribute__((unused)) */
+
+#else
+#define FUNCTION_MAY_BE_UNUSED
+#endif
+
+
+/* Some ANSI #includes are not available on Windows CE */
+#if !defined(_WIN32_WCE)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif /* !_WIN32_WCE */
+
+
+#if defined(__clang__)
+/* When using -Weverything, clang does not accept it's own headers
+ * in a release build configuration. Disable what is too much in
+ * -Weverything. */
+#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
+#endif
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+/* Who on earth came to the conclusion, using __DATE__ should rise
+ * an "expansion of date or time macro is not reproducible"
+ * warning. That's exactly what was intended by using this macro.
+ * Just disable this nonsense warning. */
+
+/* And disabling them does not work either:
+ * #pragma clang diagnostic ignored "-Wno-error=date-time"
+ * #pragma clang diagnostic ignored "-Wdate-time"
+ * So we just have to disable ALL warnings for some lines
+ * of code.
+ * This seems to be a known GCC bug, not resolved since 2012:
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
+ */
+#endif
+
+
+#if defined(__MACH__) /* Apple OSX section */
+
+#if defined(__clang__)
+#if (__clang_major__ == 3) && ((__clang_minor__ == 7) || (__clang_minor__ == 8))
+/* Avoid warnings for Xcode 7. It seems it does no longer exist in Xcode 8 */
+#pragma clang diagnostic ignored "-Wno-reserved-id-macro"
+#pragma clang diagnostic ignored "-Wno-keyword-macro"
+#endif
+#endif
+
+#define CLOCK_MONOTONIC (1)
+#define CLOCK_REALTIME (2)
+
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <mach/clock.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+
+/* clock_gettime is not implemented on OSX prior to 10.12 */
+static int
+_civet_clock_gettime(int clk_id, struct timespec *t)
+{
+memset(t, 0, sizeof(*t));
+if (clk_id == CLOCK_REALTIME) {
+struct timeval now;
+int rv = gettimeofday(&now, NULL);
+if (rv) {
+return rv;
+}
+t->tv_sec = now.tv_sec;
+t->tv_nsec = now.tv_usec * 1000;
+return 0;
+
+} else if (clk_id == CLOCK_MONOTONIC) {
+static uint64_t clock_start_time = 0;
+static mach_timebase_info_data_t timebase_ifo = {0, 0};
+
+uint64_t now = mach_absolute_time();
+
+if (clock_start_time == 0) {
+kern_return_t mach_status = mach_timebase_info(&timebase_ifo);
+DEBUG_ASSERT(mach_status == KERN_SUCCESS);
+
+/* appease "unused variable" warning for release builds */
+(void)mach_status;
+
+clock_start_time = now;
+}
+
+now = (uint64_t)((double)(now - clock_start_time)
+* (double)timebase_ifo.numer
+/ (double)timebase_ifo.denom);
+
+t->tv_sec = now / 1000000000;
+t->tv_nsec = now % 1000000000;
+return 0;
+}
+return -1; /* EINVAL - Clock ID is unknown */
+}
+
+/* if clock_gettime is declared, then __CLOCK_AVAILABILITY will be defined */
+#if defined(__CLOCK_AVAILABILITY)
+/* If we compiled with Mac OSX 10.12 or later, then clock_gettime will be
+ * declared but it may be NULL at runtime. So we need to check before using
+ * it. */
+static int
+_civet_safe_clock_gettime(int clk_id, struct timespec *t)
+{
+if (clock_gettime) {
+return clock_gettime(clk_id, t);
+}
+return _civet_clock_gettime(clk_id, t);
+}
+#define clock_gettime _civet_safe_clock_gettime
+#else
+#define clock_gettime _civet_clock_gettime
+#endif
+
+#endif
+
+
+#include <time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+
+/********************************************************************/
+/* CivetWeb configuration defines */
+/********************************************************************/
+
+/* Maximum number of threads that can be configured.
+ * The number of threads actually created depends on the "num_threads"
+ * configuration parameter, but this is the upper limit. */
+#if !defined(MAX_WORKER_THREADS)
+#define MAX_WORKER_THREADS (1024 * 64) /* in threads (count) */
+#endif
+
+/* Timeout interval for select/poll calls.
+ * The timeouts depend on "*_timeout_ms" configuration values, but long
+ * timeouts are split into timouts as small as SOCKET_TIMEOUT_QUANTUM.
+ * This reduces the time required to stop the server. */
+#if !defined(SOCKET_TIMEOUT_QUANTUM)
+#define SOCKET_TIMEOUT_QUANTUM (2000) /* in ms */
+#endif
+
+/* Do not try to compress files smaller than this limit. */
+#if !defined(MG_FILE_COMPRESSION_SIZE_LIMIT)
+#define MG_FILE_COMPRESSION_SIZE_LIMIT (1024) /* in bytes */
+#endif
+
+#if !defined(PASSWORDS_FILE_NAME)
+#define PASSWORDS_FILE_NAME ".htpasswd"
+#endif
+
+/* Initial buffer size for all CGI environment variables. In case there is
+ * not enough space, another block is allocated. */
+#if !defined(CGI_ENVIRONMENT_SIZE)
+#define CGI_ENVIRONMENT_SIZE (4096) /* in bytes */
+#endif
+
+/* Maximum number of environment variables. */
+#if !defined(MAX_CGI_ENVIR_VARS)
+#define MAX_CGI_ENVIR_VARS (256) /* in variables (count) */
+#endif
+
+/* General purpose buffer size. */
+#if !defined(MG_BUF_LEN) /* in bytes */
+#define MG_BUF_LEN (1024 * 8)
+#endif
+
+/* Size of the accepted socket queue (in case the old queue implementation
+ * is used). */
+#if !defined(MGSQLEN)
+#define MGSQLEN (20) /* count */
+#endif
+
+
+/********************************************************************/
+
+/* Helper makros */
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+
+/* Standard defines */
+#if !defined(INT64_MAX)
+#define INT64_MAX (9223372036854775807)
+#endif
+
+#define SHUTDOWN_RD (0)
+#define SHUTDOWN_WR (1)
+#define SHUTDOWN_BOTH (2)
+
+mg_static_assert(MAX_WORKER_THREADS >= 1,
+"worker threads must be a positive number");
+
+mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
+"size_t data type size check");
+
+#if defined(_WIN32) /* WINDOWS include block */
+#include <winsock2.h> /* DTL add for SO_EXCLUSIVE */
+#include <ws2tcpip.h>
+#include <windows.h>
+
+typedef const char *SOCK_OPT_TYPE;
+
+#if !defined(PATH_MAX)
+#define W_PATH_MAX (MAX_PATH)
+/* at most three UTF-8 chars per wchar_t */
+#define PATH_MAX (W_PATH_MAX * 3)
+#else
+#define W_PATH_MAX ((PATH_MAX + 2) / 3)
+#endif
+
+mg_static_assert(PATH_MAX >= 1, "path length must be a positive number");
+
+#if !defined(_IN_PORT_T)
+#if !defined(in_port_t)
+#define in_port_t u_short
+#endif
+#endif
+
+#if !defined(_WIN32_WCE)
+#include <process.h>
+#include <direct.h>
+#include <io.h>
+#else /* _WIN32_WCE */
+#define NO_CGI /* WinCE has no pipes */
+#define NO_POPEN /* WinCE has no popen */
+
+typedef long off_t;
+
+#define errno ((int)(GetLastError()))
+#define strerror(x) (_ultoa(x, (char *)_alloca(sizeof(x) * 3), 10))
+#endif /* _WIN32_WCE */
+
+#define MAKEUQUAD(lo, hi) \
+ ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32))
+#define RATE_DIFF (10000000) /* 100 nsecs */
+#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de))
+#define SYS2UNIX_TIME(lo, hi) \
+ ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF))
+
+/* Visual Studio 6 does not know __func__ or __FUNCTION__
+ * The rest of MS compilers use __FUNCTION__, not C99 __func__
+ * Also use _strtoui64 on modern M$ compilers */
+#if defined(_MSC_VER)
+#if (_MSC_VER < 1300)
+#define STRX(x) #x
+#define STR(x) STRX(x)
+#define __func__ __FILE__ ":" STR(__LINE__)
+#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x))
+#define strtoll(x, y, z) (_atoi64(x))
+#else
+#define __func__ __FUNCTION__
+#define strtoull(x, y, z) (_strtoui64(x, y, z))
+#define strtoll(x, y, z) (_strtoi64(x, y, z))
+#endif
+#endif /* _MSC_VER */
+
+#define ERRNO ((int)(GetLastError()))
+#define NO_SOCKLEN_T
+
+#if defined(_WIN64) || defined(__MINGW64__)
+#if !defined(SSL_LIB)
+#define SSL_LIB "ssleay64.dll"
+#endif
+#if !defined(CRYPTO_LIB)
+#define CRYPTO_LIB "libeay64.dll"
+#endif
+#else
+#if !defined(SSL_LIB)
+#define SSL_LIB "ssleay32.dll"
+#endif
+#if !defined(CRYPTO_LIB)
+#define CRYPTO_LIB "libeay32.dll"
+#endif
+#endif
+
+#define O_NONBLOCK (0)
+#if !defined(W_OK)
+#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */
+#endif
+#if !defined(EWOULDBLOCK)
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#endif /* !EWOULDBLOCK */
+#define _POSIX_
+#define INT64_FMT "I64d"
+#define UINT64_FMT "I64u"
+
+#define WINCDECL __cdecl
+#define vsnprintf_impl _vsnprintf
+#define access _access
+#define mg_sleep(x) (Sleep(x))
+
+#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
+#if !defined(popen)
+#define popen(x, y) (_popen(x, y))
+#endif
+#if !defined(pclose)
+#define pclose(x) (_pclose(x))
+#endif
+#define close(x) (_close(x))
+#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y)))
+#define RTLD_LAZY (0)
+#define fseeko(x, y, z) ((_lseeki64(_fileno(x), (y), (z)) == -1) ? -1 : 0)
+#define fdopen(x, y) (_fdopen((x), (y)))
+#define write(x, y, z) (_write((x), (y), (unsigned)z))
+#define read(x, y, z) (_read((x), (y), (unsigned)z))
+#define flockfile(x) (EnterCriticalSection(&global_log_file_lock))
+#define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock))
+#define sleep(x) (Sleep((x)*1000))
+#define rmdir(x) (_rmdir(x))
+#if defined(_WIN64) || !defined(__MINGW32__)
+/* Only MinGW 32 bit is missing this function */
+#define timegm(x) (_mkgmtime(x))
+#else
+time_t timegm(struct tm *tm);
+#define NEED_TIMEGM
+#endif
+
+
+#if !defined(fileno)
+#define fileno(x) (_fileno(x))
+#endif /* !fileno MINGW #defines fileno */
+
+typedef HANDLE pthread_mutex_t;
+typedef DWORD pthread_key_t;
+typedef HANDLE pthread_t;
+typedef struct {
+CRITICAL_SECTION threadIdSec;
+struct mg_workerTLS *waiting_thread; /* The chain of threads */
+} pthread_cond_t;
+
+#if !defined(__clockid_t_defined)
+typedef DWORD clockid_t;
+#endif
+#if !defined(CLOCK_MONOTONIC)
+#define CLOCK_MONOTONIC (1)
+#endif
+#if !defined(CLOCK_REALTIME)
+#define CLOCK_REALTIME (2)
+#endif
+#if !defined(CLOCK_THREAD)
+#define CLOCK_THREAD (3)
+#endif
+#if !defined(CLOCK_PROCESS)
+#define CLOCK_PROCESS (4)
+#endif
+
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+#define _TIMESPEC_DEFINED
+#endif
+#if !defined(_TIMESPEC_DEFINED)
+struct timespec {
+time_t tv_sec; /* seconds */
+long tv_nsec; /* nanoseconds */
+};
+#endif
+
+#if !defined(WIN_PTHREADS_TIME_H)
+#define MUST_IMPLEMENT_CLOCK_GETTIME
+#endif
+
+#if defined(MUST_IMPLEMENT_CLOCK_GETTIME)
+#define clock_gettime mg_clock_gettime
+static int
+clock_gettime(clockid_t clk_id, struct timespec *tp)
+{
+FILETIME ft;
+ULARGE_INTEGER li, li2;
+BOOL ok = FALSE;
+double d;
+static double perfcnt_per_sec = 0.0;
+static BOOL initialized = FALSE;
+
+if (!initialized) {
+QueryPerformanceFrequency((LARGE_INTEGER *)&li);
+perfcnt_per_sec = 1.0 / li.QuadPart;
+initialized = TRUE;
+}
+
+if (tp) {
+memset(tp, 0, sizeof(*tp));
+
+if (clk_id == CLOCK_REALTIME) {
+
+/* BEGIN: CLOCK_REALTIME = wall clock (date and time) */
+GetSystemTimeAsFileTime(&ft);
+li.LowPart = ft.dwLowDateTime;
+li.HighPart = ft.dwHighDateTime;
+li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */
+tp->tv_sec = (time_t)(li.QuadPart / 10000000);
+tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
+ok = TRUE;
+/* END: CLOCK_REALTIME */
+
+} else if (clk_id == CLOCK_MONOTONIC) {
+
+/* BEGIN: CLOCK_MONOTONIC = stopwatch (time differences) */
+QueryPerformanceCounter((LARGE_INTEGER *)&li);
+d = li.QuadPart * perfcnt_per_sec;
+tp->tv_sec = (time_t)d;
+d -= (double)tp->tv_sec;
+tp->tv_nsec = (long)(d * 1.0E9);
+ok = TRUE;
+/* END: CLOCK_MONOTONIC */
+
+} else if (clk_id == CLOCK_THREAD) {
+
+/* BEGIN: CLOCK_THREAD = CPU usage of thread */
+FILETIME t_create, t_exit, t_kernel, t_user;
+if (GetThreadTimes(GetCurrentThread(),
+&t_create,
+&t_exit,
+&t_kernel,
+&t_user)) {
+li.LowPart = t_user.dwLowDateTime;
+li.HighPart = t_user.dwHighDateTime;
+li2.LowPart = t_kernel.dwLowDateTime;
+li2.HighPart = t_kernel.dwHighDateTime;
+li.QuadPart += li2.QuadPart;
+tp->tv_sec = (time_t)(li.QuadPart / 10000000);
+tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
+ok = TRUE;
+}
+/* END: CLOCK_THREAD */
+
+} else if (clk_id == CLOCK_PROCESS) {
+
+/* BEGIN: CLOCK_PROCESS = CPU usage of process */
+FILETIME t_create, t_exit, t_kernel, t_user;
+if (GetProcessTimes(GetCurrentProcess(),
+&t_create,
+&t_exit,
+&t_kernel,
+&t_user)) {
+li.LowPart = t_user.dwLowDateTime;
+li.HighPart = t_user.dwHighDateTime;
+li2.LowPart = t_kernel.dwLowDateTime;
+li2.HighPart = t_kernel.dwHighDateTime;
+li.QuadPart += li2.QuadPart;
+tp->tv_sec = (time_t)(li.QuadPart / 10000000);
+tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
+ok = TRUE;
+}
+/* END: CLOCK_PROCESS */
+
+} else {
+
+/* BEGIN: unknown clock */
+/* ok = FALSE; already set by init */
+/* END: unknown clock */
+}
+}
+
+return ok ? 0 : -1;
+}
+#endif
+
+
+#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */
+
+static int pthread_mutex_lock(pthread_mutex_t *);
+static int pthread_mutex_unlock(pthread_mutex_t *);
+static void path_to_unicode(const struct mg_connection *conn,
+const char *path,
+wchar_t *wbuf,
+size_t wbuf_len);
+
+/* All file operations need to be rewritten to solve #246. */
+
+struct mg_file;
+
+static const char *
+mg_fgets(char *buf, size_t size, struct mg_file *filep, char **p);
+
+
+/* POSIX dirent interface */
+struct dirent {
+char d_name[PATH_MAX];
+};
+
+typedef struct DIR {
+HANDLE handle;
+WIN32_FIND_DATAW info;
+struct dirent result;
+} DIR;
+
+#if defined(_WIN32)
+#if !defined(HAVE_POLL)
+struct pollfd {
+SOCKET fd;
+short events;
+short revents;
+};
+#endif
+#endif
+
+/* Mark required libraries */
+#if defined(_MSC_VER)
+#pragma comment(lib, "Ws2_32.lib")
+#endif
+
+#else /* defined(_WIN32) - WINDOWS vs UNIX include block */
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+typedef const void *SOCK_OPT_TYPE;
+
+#if defined(ANDROID)
+typedef unsigned short int in_port_t;
+#endif
+
+#include <pwd.h>
+#include <unistd.h>
+#include <grp.h>
+#include <dirent.h>
+#define vsnprintf_impl vsnprintf
+
+#if !defined(NO_SSL_DL) && !defined(NO_SSL)
+#include <dlfcn.h>
+#endif
+#include <pthread.h>
+#if defined(__MACH__)
+#define SSL_LIB "libssl.dylib"
+#define CRYPTO_LIB "libcrypto.dylib"
+#else
+#if !defined(SSL_LIB)
+#define SSL_LIB "libssl.so"
+#endif
+#if !defined(CRYPTO_LIB)
+#define CRYPTO_LIB "libcrypto.so"
+#endif
+#endif
+#if !defined(O_BINARY)
+#define O_BINARY (0)
+#endif /* O_BINARY */
+#define closesocket(a) (close(a))
+#define mg_mkdir(conn, path, mode) (mkdir(path, mode))
+#define mg_remove(conn, x) (remove(x))
+#define mg_sleep(x) (usleep((x)*1000))
+#define mg_opendir(conn, x) (opendir(x))
+#define mg_closedir(x) (closedir(x))
+#define mg_readdir(x) (readdir(x))
+#define ERRNO (errno)
+#define INVALID_SOCKET (-1)
+#define INT64_FMT PRId64
+#define UINT64_FMT PRIu64
+typedef int SOCKET;
+#define WINCDECL
+
+#if defined(__hpux)
+/* HPUX 11 does not have monotonic, fall back to realtime */
+#if !defined(CLOCK_MONOTONIC)
+#define CLOCK_MONOTONIC CLOCK_REALTIME
+#endif
+
+/* HPUX defines socklen_t incorrectly as size_t which is 64bit on
+ * Itanium. Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED
+ * the prototypes use int* rather than socklen_t* which matches the
+ * actual library expectation. When called with the wrong size arg
+ * accept() returns a zero client inet addr and check_acl() always
+ * fails. Since socklen_t is widely used below, just force replace
+ * their typedef with int. - DTL
+ */
+#define socklen_t int
+#endif /* hpux */
+
+#endif /* defined(_WIN32) - WINDOWS vs UNIX include block */
+
+/* Maximum queue length for pending connections. This value is passed as
+ * parameter to the "listen" socket call. */
+#if !defined(SOMAXCONN)
+/* This symbol may be defined in winsock2.h so this must after that include */
+#define SOMAXCONN (100) /* in pending connections (count) */
+#endif
+
+/* In case our C library is missing "timegm", provide an implementation */
+#if defined(NEED_TIMEGM)
+static inline int
+is_leap(int y)
+{
+return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
+}
+
+static inline int
+count_leap(int y)
+{
+return (y - 1969) / 4 - (y - 1901) / 100 + (y - 1601) / 400;
+}
+
+time_t
+timegm(struct tm *tm)
+{
+static const unsigned short ydays[] = {
+0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
+int year = tm->tm_year + 1900;
+int mon = tm->tm_mon;
+int mday = tm->tm_mday - 1;
+int hour = tm->tm_hour;
+int min = tm->tm_min;
+int sec = tm->tm_sec;
+
+if (year < 1970 || mon < 0 || mon > 11 || mday < 0
+|| (mday >= ydays[mon + 1] - ydays[mon]
++ (mon == 1 && is_leap(year) ? 1 : 0)) || hour < 0
+|| hour > 23
+|| min < 0
+|| min > 59
+|| sec < 0
+|| sec > 60)
+return -1;
+
+time_t res = year - 1970;
+res *= 365;
+res += mday;
+res += ydays[mon] + (mon > 1 && is_leap(year) ? 1 : 0);
+res += count_leap(year);
+
+res *= 24;
+res += hour;
+res *= 60;
+res += min;
+res *= 60;
+res += sec;
+return res;
+}
+#endif /* NEED_TIMEGM */
+
+
+/* va_copy should always be a macro, C99 and C++11 - DTL */
+#if !defined(va_copy)
+#define va_copy(x, y) ((x) = (y))
+#endif
+
+
+#if defined(_WIN32)
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static CRITICAL_SECTION global_log_file_lock;
+
+FUNCTION_MAY_BE_UNUSED
+static DWORD
+pthread_self(void)
+{
+return GetCurrentThreadId();
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static int
+pthread_key_create(
+pthread_key_t *key,
+void (*_ignored)(void *) /* destructor not supported for Windows */
+)
+{
+(void)_ignored;
+
+if ((key != 0)) {
+*key = TlsAlloc();
+return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
+}
+return -2;
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static int
+pthread_key_delete(pthread_key_t key)
+{
+return TlsFree(key) ? 0 : 1;
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static int
+pthread_setspecific(pthread_key_t key, void *value)
+{
+return TlsSetValue(key, value) ? 0 : 1;
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static void *
+pthread_getspecific(pthread_key_t key)
+{
+return TlsGetValue(key);
+}
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL;
+#else
+static pthread_mutexattr_t pthread_mutex_attr;
+#endif /* _WIN32 */
+
+
+#if defined(_WIN32_WCE)
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+FUNCTION_MAY_BE_UNUSED
+static time_t
+time(time_t *ptime)
+{
+time_t t;
+SYSTEMTIME st;
+FILETIME ft;
+
+GetSystemTime(&st);
+SystemTimeToFileTime(&st, &ft);
+t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
+
+if (ptime != NULL) {
+*ptime = t;
+}
+
+return t;
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static struct tm *
+localtime_s(const time_t *ptime, struct tm *ptm)
+{
+int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF;
+FILETIME ft, lft;
+SYSTEMTIME st;
+TIME_ZONE_INFORMATION tzinfo;
+
+if (ptm == NULL) {
+return NULL;
+}
+
+*(int64_t *)&ft = t;
+FileTimeToLocalFileTime(&ft, &lft);
+FileTimeToSystemTime(&lft, &st);
+ptm->tm_year = st.wYear - 1900;
+ptm->tm_mon = st.wMonth - 1;
+ptm->tm_wday = st.wDayOfWeek;
+ptm->tm_mday = st.wDay;
+ptm->tm_hour = st.wHour;
+ptm->tm_min = st.wMinute;
+ptm->tm_sec = st.wSecond;
+ptm->tm_yday = 0; /* hope nobody uses this */
+ptm->tm_isdst =
+(GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT) ? 1 : 0;
+
+return ptm;
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static struct tm *
+gmtime_s(const time_t *ptime, struct tm *ptm)
+{
+/* FIXME(lsm): fix this. */
+return localtime_s(ptime, ptm);
+}
+
+
+static int mg_atomic_inc(volatile int *addr);
+static struct tm tm_array[MAX_WORKER_THREADS];
+static int tm_index = 0;
+
+
+FUNCTION_MAY_BE_UNUSED
+static struct tm *
+localtime(const time_t *ptime)
+{
+int i = mg_atomic_inc(&tm_index) % (sizeof(tm_array) / sizeof(tm_array[0]));
+return localtime_s(ptime, tm_array + i);
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static struct tm *
+gmtime(const time_t *ptime)
+{
+int i = mg_atomic_inc(&tm_index) % ARRAY_SIZE(tm_array);
+return gmtime_s(ptime, tm_array + i);
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static size_t
+strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm)
+{
+/* TODO: (void)mg_snprintf(NULL, dst, dst_size, "implement strftime()
+ * for WinCE"); */
+return 0;
+}
+
+#define _beginthreadex(psec, stack, func, prm, flags, ptid) \
+ (uintptr_t) CreateThread(psec, stack, func, prm, flags, ptid)
+
+#define remove(f) mg_remove(NULL, f)
+
+
+FUNCTION_MAY_BE_UNUSED
+static int
+rename(const char *a, const char *b)
+{
+wchar_t wa[W_PATH_MAX];
+wchar_t wb[W_PATH_MAX];
+path_to_unicode(NULL, a, wa, ARRAY_SIZE(wa));
+path_to_unicode(NULL, b, wb, ARRAY_SIZE(wb));
+
+return MoveFileW(wa, wb) ? 0 : -1;
+}
+
+
+struct stat {
+int64_t st_size;
+time_t st_mtime;
+};
+
+
+FUNCTION_MAY_BE_UNUSED
+static int
+stat(const char *name, struct stat *st)
+{
+wchar_t wbuf[W_PATH_MAX];
+WIN32_FILE_ATTRIBUTE_DATA attr;
+time_t creation_time, write_time;
+
+path_to_unicode(NULL, name, wbuf, ARRAY_SIZE(wbuf));
+memset(&attr, 0, sizeof(attr));
+
+GetFileAttributesExW(wbuf, GetFileExInfoStandard, &attr);
+st->st_size =
+(((int64_t)attr.nFileSizeHigh) << 32) + (int64_t)attr.nFileSizeLow;
+
+write_time = SYS2UNIX_TIME(attr.ftLastWriteTime.dwLowDateTime,
+attr.ftLastWriteTime.dwHighDateTime);
+creation_time = SYS2UNIX_TIME(attr.ftCreationTime.dwLowDateTime,
+attr.ftCreationTime.dwHighDateTime);
+
+if (creation_time > write_time) {
+st->st_mtime = creation_time;
+} else {
+st->st_mtime = write_time;
+}
+return 0;
+}
+
+#define access(x, a) 1 /* not required anyway */
+
+/* WinCE-TODO: define stat, remove, rename, _rmdir, _lseeki64 */
+/* Values from errno.h in Windows SDK (Visual Studio). */
+#define EEXIST 17
+#define EACCES 13
+#define ENOENT 2
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+#endif /* defined(_WIN32_WCE) */
+
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#define GCC_VERSION \
+ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#if GCC_VERSION >= 40500
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif /* GCC_VERSION >= 40500 */
+#endif /* defined(__GNUC__) || defined(__MINGW32__) */
+#if defined(__clang__)
+/* Show no warning in case system functions are not used. */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+static pthread_mutex_t global_lock_mutex;
+
+
+#if defined(_WIN32)
+/* Forward declaration for Windows */
+FUNCTION_MAY_BE_UNUSED
+static int pthread_mutex_lock(pthread_mutex_t *mutex);
+
+FUNCTION_MAY_BE_UNUSED
+static int pthread_mutex_unlock(pthread_mutex_t *mutex);
+#endif
+
+
+FUNCTION_MAY_BE_UNUSED
+static void
+mg_global_lock(void)
+{
+(void)pthread_mutex_lock(&global_lock_mutex);
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static void
+mg_global_unlock(void)
+{
+(void)pthread_mutex_unlock(&global_lock_mutex);
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static int
+mg_atomic_inc(volatile int *addr)
+{
+int ret;
+#if defined(_WIN32) && !defined(NO_ATOMICS)
+/* Depending on the SDK, this function uses either
+ * (volatile unsigned int *) or (volatile LONG *),
+ * so whatever you use, the other SDK is likely to raise a warning. */
+ret = InterlockedIncrement((volatile long *)addr);
+#elif defined(__GNUC__) \
+ && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
+ && !defined(NO_ATOMICS)
+ret = __sync_add_and_fetch(addr, 1);
+#else
+mg_global_lock();
+ret = (++(*addr));
+mg_global_unlock();
+#endif
+return ret;
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static int
+mg_atomic_dec(volatile int *addr)
+{
+int ret;
+#if defined(_WIN32) && !defined(NO_ATOMICS)
+/* Depending on the SDK, this function uses either
+ * (volatile unsigned int *) or (volatile LONG *),
+ * so whatever you use, the other SDK is likely to raise a warning. */
+ret = InterlockedDecrement((volatile long *)addr);
+#elif defined(__GNUC__) \
+ && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
+ && !defined(NO_ATOMICS)
+ret = __sync_sub_and_fetch(addr, 1);
+#else
+mg_global_lock();
+ret = (--(*addr));
+mg_global_unlock();
+#endif
+return ret;
+}
+
+
+#if defined(USE_SERVER_STATS)
+static int64_t
+mg_atomic_add(volatile int64_t *addr, int64_t value)
+{
+int64_t ret;
+#if defined(_WIN64) && !defined(NO_ATOMICS)
+ret = InterlockedAdd64(addr, value);
+#elif defined(__GNUC__) \
+ && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) \
+ && !defined(NO_ATOMICS)
+ret = __sync_add_and_fetch(addr, value);
+#else
+mg_global_lock();
+*addr += value;
+ret = (*addr);
+mg_global_unlock();
+#endif
+return ret;
+}
+#endif
+
+
+#if defined(__GNUC__)
+/* Show no warning in case system functions are not used. */
+#if GCC_VERSION >= 40500
+#pragma GCC diagnostic pop
+#endif /* GCC_VERSION >= 40500 */
+#endif /* defined(__GNUC__) */
+#if defined(__clang__)
+/* Show no warning in case system functions are not used. */
+#pragma clang diagnostic pop
+#endif
+
+
+#if defined(USE_SERVER_STATS)
+
+struct mg_memory_stat {
+volatile int64_t totalMemUsed;
+volatile int64_t maxMemUsed;
+volatile int blockCount;
+};
+
+
+static struct mg_memory_stat *get_memory_stat(struct mg_context *ctx);
+
+
+static void *
+mg_malloc_ex(size_t size,
+struct mg_context *ctx,
+const char *file,
+unsigned line)
+{
+void *data = malloc(size + 2 * sizeof(uintptr_t));
+void *memory = 0;
+struct mg_memory_stat *mstat = get_memory_stat(ctx);
+
+#if defined(MEMORY_DEBUGGING)
+char mallocStr[256];
+#else
+(void)file;
+(void)line;
+#endif
+
+if (data) {
+int64_t mmem = mg_atomic_add(&mstat->totalMemUsed, (int64_t)size);
+if (mmem > mstat->maxMemUsed) {
+/* could use atomic compare exchange, but this
+ * seems overkill for statistics data */
+mstat->maxMemUsed = mmem;
+}
+
+mg_atomic_inc(&mstat->blockCount);
+((uintptr_t *)data)[0] = size;
+((uintptr_t *)data)[1] = (uintptr_t)mstat;
+memory = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
+}
+
+#if defined(MEMORY_DEBUGGING)
+sprintf(mallocStr,
+"MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n",
+memory,
+(unsigned long)size,
+(unsigned long)mstat->totalMemUsed,
+(unsigned long)mstat->blockCount,
+file,
+line);
+#if defined(_WIN32)
+OutputDebugStringA(mallocStr);
+#else
+DEBUG_TRACE("%s", mallocStr);
+#endif
+#endif
+
+return memory;
+}
+
+
+static void *
+mg_calloc_ex(size_t count,
+size_t size,
+struct mg_context *ctx,
+const char *file,
+unsigned line)
+{
+void *data = mg_malloc_ex(size * count, ctx, file, line);
+
+if (data) {
+memset(data, 0, size * count);
+}
+return data;
+}
+
+
+static void
+mg_free_ex(void *memory, const char *file, unsigned line)
+{
+void *data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
+
+
+#if defined(MEMORY_DEBUGGING)
+char mallocStr[256];
+#else
+(void)file;
+(void)line;
+#endif
+
+if (memory) {
+uintptr_t size = ((uintptr_t *)data)[0];
+struct mg_memory_stat *mstat =
+(struct mg_memory_stat *)(((uintptr_t *)data)[1]);
+mg_atomic_add(&mstat->totalMemUsed, -(int64_t)size);
+mg_atomic_dec(&mstat->blockCount);
+#if defined(MEMORY_DEBUGGING)
+sprintf(mallocStr,
+"MEM: %p %5lu free %7lu %4lu --- %s:%u\n",
+memory,
+(unsigned long)size,
+(unsigned long)mstat->totalMemUsed,
+(unsigned long)mstat->blockCount,
+file,
+line);
+#if defined(_WIN32)
+OutputDebugStringA(mallocStr);
+#else
+DEBUG_TRACE("%s", mallocStr);
+#endif
+#endif
+free(data);
+}
+}
+
+
+static void *
+mg_realloc_ex(void *memory,
+size_t newsize,
+struct mg_context *ctx,
+const char *file,
+unsigned line)
+{
+void *data;
+void *_realloc;
+uintptr_t oldsize;
+
+#if defined(MEMORY_DEBUGGING)
+char mallocStr[256];
+#else
+(void)file;
+(void)line;
+#endif
+
+if (newsize) {
+if (memory) {
+/* Reallocate existing block */
+struct mg_memory_stat *mstat;
+data = (void *)(((char *)memory) - 2 * sizeof(uintptr_t));
+oldsize = ((uintptr_t *)data)[0];
+mstat = (struct mg_memory_stat *)((uintptr_t *)data)[1];
+_realloc = realloc(data, newsize + 2 * sizeof(uintptr_t));
+if (_realloc) {
+data = _realloc;
+mg_atomic_add(&mstat->totalMemUsed, -(int64_t)oldsize);
+#if defined(MEMORY_DEBUGGING)
+sprintf(mallocStr,
+"MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n",
+memory,
+(unsigned long)oldsize,
+(unsigned long)mstat->totalMemUsed,
+(unsigned long)mstat->blockCount,
+file,
+line);
+#if defined(_WIN32)
+OutputDebugStringA(mallocStr);
+#else
+DEBUG_TRACE("%s", mallocStr);
+#endif
+#endif
+mg_atomic_add(&mstat->totalMemUsed, (int64_t)newsize);
+#if defined(MEMORY_DEBUGGING)
+sprintf(mallocStr,
+"MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
+memory,
+(unsigned long)newsize,
+(unsigned long)mstat->totalMemUsed,
+(unsigned long)mstat->blockCount,
+file,
+line);
+#if defined(_WIN32)
+OutputDebugStringA(mallocStr);
+#else
+DEBUG_TRACE("%s", mallocStr);
+#endif
+#endif
+*(uintptr_t *)data = newsize;
+data = (void *)(((char *)data) + 2 * sizeof(uintptr_t));
+} else {
+#if defined(MEMORY_DEBUGGING)
+#if defined(_WIN32)
+OutputDebugStringA("MEM: realloc failed\n");
+#else
+DEBUG_TRACE("%s", "MEM: realloc failed\n");
+#endif
+#endif
+return _realloc;
+}
+} else {
+/* Allocate new block */
+data = mg_malloc_ex(newsize, ctx, file, line);
+}
+} else {
+/* Free existing block */
+data = 0;
+mg_free_ex(memory, file, line);
+}
+
+return data;
+}
+
+#define mg_malloc(a) mg_malloc_ex(a, NULL, __FILE__, __LINE__)
+#define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __FILE__, __LINE__)
+#define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __FILE__, __LINE__)
+#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
+
+#define mg_malloc_ctx(a, c) mg_malloc_ex(a, c, __FILE__, __LINE__)
+#define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __FILE__, __LINE__)
+#define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __FILE__, __LINE__)
+
+#else /* USE_SERVER_STATS */
+
+static __inline void *
+mg_malloc(size_t a)
+{
+return malloc(a);
+}
+
+static __inline void *
+mg_calloc(size_t a, size_t b)
+{
+return calloc(a, b);
+}
+
+static __inline void *
+mg_realloc(void *a, size_t b)
+{
+return realloc(a, b);
+}
+
+static __inline void
+mg_free(void *a)
+{
+free(a);
+}
+
+#define mg_malloc_ctx(a, c) mg_malloc(a)
+#define mg_calloc_ctx(a, b, c) mg_calloc(a, b)
+#define mg_realloc_ctx(a, b, c) mg_realloc(a, b)
+#define mg_free_ctx(a, c) mg_free(a)
+
+#endif /* USE_SERVER_STATS */
+
+
+static void mg_vsnprintf(const struct mg_connection *conn,
+int *truncated,
+char *buf,
+size_t buflen,
+const char *fmt,
+va_list ap);
+
+static void mg_snprintf(const struct mg_connection *conn,
+int *truncated,
+char *buf,
+size_t buflen,
+PRINTF_FORMAT_STRING(const char *fmt),
+...) PRINTF_ARGS(5, 6);
+
+/* This following lines are just meant as a reminder to use the mg-functions
+ * for memory management */
+#if defined(malloc)
+#undef malloc
+#endif
+#if defined(calloc)
+#undef calloc
+#endif
+#if defined(realloc)
+#undef realloc
+#endif
+#if defined(free)
+#undef free
+#endif
+#if defined(snprintf)
+#undef snprintf
+#endif
+#if defined(vsnprintf)
+#undef vsnprintf
+#endif
+#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
+#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
+#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
+#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
+#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
+#if defined(_WIN32)
+/* vsnprintf must not be used in any system,
+ * but this define only works well for Windows. */
+#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf
+#endif
+
+
+/* mg_init_library counter */
+static int mg_init_library_called = 0;
+
+#if !defined(NO_SSL)
+static int mg_ssl_initialized = 0;
+#endif
+
+static pthread_key_t sTlsKey; /* Thread local storage index */
+static int thread_idx_max = 0;
+
+#if defined(MG_LEGACY_INTERFACE)
+#define MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE
+#endif
+
+struct mg_workerTLS {
+int is_master;
+unsigned long thread_idx;
+#if defined(_WIN32)
+HANDLE pthread_cond_helper_mutex;
+struct mg_workerTLS *next_waiting_thread;
+#endif
+#if defined(MG_ALLOW_USING_GET_REQUEST_INFO_FOR_RESPONSE)
+char txtbuf[4];
+#endif
+};
+
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#if GCC_VERSION >= 40500
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif /* GCC_VERSION >= 40500 */
+#endif /* defined(__GNUC__) || defined(__MINGW32__) */
+#if defined(__clang__)
+/* Show no warning in case system functions are not used. */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+
+/* Get a unique thread ID as unsigned long, independent from the data type
+ * of thread IDs defined by the operating system API.
+ * If two calls to mg_current_thread_id return the same value, they calls
+ * are done from the same thread. If they return different values, they are
+ * done from different threads. (Provided this function is used in the same
+ * process context and threads are not repeatedly created and deleted, but
+ * CivetWeb does not do that).
+ * This function must match the signature required for SSL id callbacks:
+ * CRYPTO_set_id_callback
+ */
+FUNCTION_MAY_BE_UNUSED
+static unsigned long
+mg_current_thread_id(void)
+{
+#if defined(_WIN32)
+return GetCurrentThreadId();
+#else
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+/* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)"
+ * or not, so one of the two conditions will be unreachable by construction.
+ * Unfortunately the C standard does not define a way to check this at
+ * compile time, since the #if preprocessor conditions can not use the sizeof
+ * operator as an argument. */
+#endif
+
+if (sizeof(pthread_t) > sizeof(unsigned long)) {
+/* This is the problematic case for CRYPTO_set_id_callback:
+ * The OS pthread_t can not be cast to unsigned long. */
+struct mg_workerTLS *tls =
+(struct mg_workerTLS *)pthread_getspecific(sTlsKey);
+if (tls == NULL) {
+/* SSL called from an unknown thread: Create some thread index.
+ */
+tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS));
+tls->is_master = -2; /* -2 means "3rd party thread" */
+tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
+pthread_setspecific(sTlsKey, tls);
+}
+return tls->thread_idx;
+} else {
+/* pthread_t may be any data type, so a simple cast to unsigned long
+ * can rise a warning/error, depending on the platform.
+ * Here memcpy is used as an anything-to-anything cast. */
+unsigned long ret = 0;
+pthread_t t = pthread_self();
+memcpy(&ret, &t, sizeof(pthread_t));
+return ret;
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#endif
+}
+
+
+FUNCTION_MAY_BE_UNUSED
+static uint64_t
+mg_get_current_time_ns(void)
+{
+struct timespec tsnow;
+clock_gettime(CLOCK_REALTIME, &tsnow);
+return (((uint64_t)tsnow.tv_sec) * 1000000000) + (uint64_t)tsnow.tv_nsec;
+}
+
+
+#if defined(__GNUC__)
+/* Show no warning in case system functions are not used. */
+#if GCC_VERSION >= 40500
+#pragma GCC diagnostic pop
+#endif /* GCC_VERSION >= 40500 */
+#endif /* defined(__GNUC__) */
+#if defined(__clang__)
+/* Show no warning in case system functions are not used. */
+#pragma clang diagnostic pop
+#endif
+
+
+#if defined(NEED_DEBUG_TRACE_FUNC)
+static void
+DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...)
+{
+va_list args;
+uint64_t nsnow;
+static uint64_t nslast;
+struct timespec tsnow;
+
+/* Get some operating system independent thread id */
+unsigned long thread_id = mg_current_thread_id();
+
+clock_gettime(CLOCK_REALTIME, &tsnow);
+nsnow = ((uint64_t)tsnow.tv_sec) * ((uint64_t)1000000000)
++ ((uint64_t)tsnow.tv_nsec);
+
+if (!nslast) {
+nslast = nsnow;
+}
+
+flockfile(stdout);
+printf("*** %lu.%09lu %12" INT64_FMT " %lu %s:%u: ",
+(unsigned long)tsnow.tv_sec,
+(unsigned long)tsnow.tv_nsec,
+nsnow - nslast,
+thread_id,
+func,
+line);
+va_start(args, fmt);
+vprintf(fmt, args);
+va_end(args);
+putchar('\n');
+fflush(stdout);
+funlockfile(stdout);
+nslast = nsnow;
+}
+#endif /* NEED_DEBUG_TRACE_FUNC */
+
+
+#define MD5_STATIC static
+#include "md5.inl"
+
+/* Darwin prior to 7.0 and Win32 do not have socklen_t */
+#if defined(NO_SOCKLEN_T)
+typedef int socklen_t;
+#endif /* NO_SOCKLEN_T */
+
+#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */
+
+#if !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL (0)
+#endif
+
+
+#if defined(NO_SSL)
+typedef struct SSL SSL; /* dummy for SSL argument to push/pull */
+typedef struct SSL_CTX SSL_CTX;
+#else
+#if defined(NO_SSL_DL)
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/engine.h>
+#include <openssl/conf.h>
+#include <openssl/dh.h>
+#include <openssl/bn.h>
+#include <openssl/opensslv.h>
+
+#if defined(WOLFSSL_VERSION)
+/* Additional defines for WolfSSL, see
+ * https://github.com/civetweb/civetweb/issues/583 */
+#include "wolfssl_extras.inl"
+#endif
+
+#else
+
+/* SSL loaded dynamically from DLL.
+ * I put the prototypes here to be independent from OpenSSL source
+ * installation. */
+
+typedef struct ssl_st SSL;
+typedef struct ssl_method_st SSL_METHOD;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
+typedef struct x509_name X509_NAME;
+typedef struct asn1_integer ASN1_INTEGER;
+typedef struct bignum BIGNUM;
+typedef struct ossl_init_settings_st OPENSSL_INIT_SETTINGS;
+typedef struct evp_md EVP_MD;
+typedef struct x509 X509;
+
+
+#define SSL_CTRL_OPTIONS (32)
+#define SSL_CTRL_CLEAR_OPTIONS (77)
+#define SSL_CTRL_SET_ECDH_AUTO (94)
+
+#define OPENSSL_INIT_NO_LOAD_SSL_STRINGS 0x00100000L
+#define OPENSSL_INIT_LOAD_SSL_STRINGS 0x00200000L
+#define OPENSSL_INIT_LOAD_CRYPTO_STRINGS 0x00000002L
+
+#define SSL_VERIFY_NONE (0)
+#define SSL_VERIFY_PEER (1)
+#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT (2)
+#define SSL_VERIFY_CLIENT_ONCE (4)
+#define SSL_OP_ALL ((long)(0x80000BFFUL))
+#define SSL_OP_NO_SSLv2 (0x01000000L)
+#define SSL_OP_NO_SSLv3 (0x02000000L)
+#define SSL_OP_NO_TLSv1 (0x04000000L)
+#define SSL_OP_NO_TLSv1_2 (0x08000000L)
+#define SSL_OP_NO_TLSv1_1 (0x10000000L)
+#define SSL_OP_SINGLE_DH_USE (0x00100000L)
+#define SSL_OP_CIPHER_SERVER_PREFERENCE (0x00400000L)
+#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (0x00010000L)
+#define SSL_OP_NO_COMPRESSION (0x00020000L)
+
+#define SSL_CB_HANDSHAKE_START (0x10)
+#define SSL_CB_HANDSHAKE_DONE (0x20)
+
+#define SSL_ERROR_NONE (0)
+#define SSL_ERROR_SSL (1)
+#define SSL_ERROR_WANT_READ (2)
+#define SSL_ERROR_WANT_WRITE (3)
+#define SSL_ERROR_WANT_X509_LOOKUP (4)
+#define SSL_ERROR_SYSCALL (5) /* see errno */
+#define SSL_ERROR_ZERO_RETURN (6)
+#define SSL_ERROR_WANT_CONNECT (7)
+#define SSL_ERROR_WANT_ACCEPT (8)
+
+#define TLSEXT_TYPE_server_name (0)
+#define TLSEXT_NAMETYPE_host_name (0)
+#define SSL_TLSEXT_ERR_OK (0)
+#define SSL_TLSEXT_ERR_ALERT_WARNING (1)
+#define SSL_TLSEXT_ERR_ALERT_FATAL (2)
+#define SSL_TLSEXT_ERR_NOACK (3)
+
+struct ssl_func {
+const char *name; /* SSL function name */
+void (*ptr)(void); /* Function pointer */
+};
+
+
+#if defined(OPENSSL_API_1_1)
+
+#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
+#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr)
+#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr)
+#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr)
+#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr)
+#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr)
+#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr)
+#define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr)
+#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr)
+#define TLS_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr)
+#define OPENSSL_init_ssl \
+ (*(int (*)(uint64_t opts, \
+ const OPENSSL_INIT_SETTINGS *settings))ssl_sw[10].ptr)
+#define SSL_CTX_use_PrivateKey_file \
+ (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr)
+#define SSL_CTX_use_certificate_file \
+ (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr)
+#define SSL_CTX_set_default_passwd_cb \
+ (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr)
+#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr)
+#define SSL_CTX_use_certificate_chain_file \
+ (*(int (*)(SSL_CTX *, const char *))ssl_sw[15].ptr)
+#define TLS_client_method (*(SSL_METHOD * (*)(void))ssl_sw[16].ptr)
+#define SSL_pending (*(int (*)(SSL *))ssl_sw[17].ptr)
+#define SSL_CTX_set_verify \
+ (*(void (*)(SSL_CTX *, \
+ int, \
+ int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[18].ptr)
+#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[19].ptr)
+#define SSL_CTX_load_verify_locations \
+ (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[20].ptr)
+#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[21].ptr)
+#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[22].ptr)
+#define SSL_get_peer_certificate (*(X509 * (*)(SSL *))ssl_sw[23].ptr)
+#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[24].ptr)
+#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *))ssl_sw[25].ptr)
+#define SSL_CIPHER_get_name \
+ (*(const char *(*)(const SSL_CIPHER *))ssl_sw[26].ptr)
+#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[27].ptr)
+#define SSL_CTX_set_session_id_context \
+ (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[28].ptr)
+#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[29].ptr)
+#define SSL_CTX_set_cipher_list \
+ (*(int (*)(SSL_CTX *, const char *))ssl_sw[30].ptr)
+#define SSL_CTX_set_options \
+ (*(unsigned long (*)(SSL_CTX *, unsigned long))ssl_sw[31].ptr)
+#define SSL_CTX_set_info_callback \
+ (*(void (*)(SSL_CTX * ctx, \
+ void (*callback)(SSL * s, int, int)))ssl_sw[32].ptr)
+#define SSL_get_ex_data (*(char *(*)(SSL *, int))ssl_sw[33].ptr)
+#define SSL_set_ex_data (*(void (*)(SSL *, int, char *))ssl_sw[34].ptr)
+#define SSL_CTX_callback_ctrl \
+ (*(long (*)(SSL_CTX *, int, void (*)(void)))ssl_sw[35].ptr)
+#define SSL_get_servername \
+ (*(const char *(*)(const SSL *, int type))ssl_sw[36].ptr)
+#define SSL_set_SSL_CTX (*(SSL_CTX * (*)(SSL *, SSL_CTX *))ssl_sw[37].ptr)
+
+#define SSL_CTX_clear_options(ctx, op) \
+ SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
+#define SSL_CTX_set_ecdh_auto(ctx, onoff) \
+ SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL)
+
+#define SSL_CTRL_SET_TLSEXT_SERVERNAME_CB 53
+#define SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG 54
+#define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \
+ SSL_CTX_callback_ctrl(ctx, \
+ SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \
+ (void (*)(void))cb)
+#define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \
+ SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, (void *)arg)
+
+#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
+#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
+
+#define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)arg))
+#define SSL_get_app_data(s) (SSL_get_ex_data(s, 0))
+
+#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[0].ptr)
+#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[1].ptr)
+#define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[2].ptr)
+#define CONF_modules_unload (*(void (*)(int))crypto_sw[3].ptr)
+#define X509_free (*(void (*)(X509 *))crypto_sw[4].ptr)
+#define X509_get_subject_name (*(X509_NAME * (*)(X509 *))crypto_sw[5].ptr)
+#define X509_get_issuer_name (*(X509_NAME * (*)(X509 *))crypto_sw[6].ptr)
+#define X509_NAME_oneline \
+ (*(char *(*)(X509_NAME *, char *, int))crypto_sw[7].ptr)
+#define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *))crypto_sw[8].ptr)
+#define EVP_get_digestbyname \
+ (*(const EVP_MD *(*)(const char *))crypto_sw[9].ptr)
+#define EVP_Digest \
+ (*(int (*)( \
+ const void *, size_t, void *, unsigned int *, const EVP_MD *, void *)) \
+ crypto_sw[10].ptr)
+#define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[11].ptr)
+#define BN_bn2hex (*(char *(*)(const BIGNUM *a))crypto_sw[12].ptr)
+#define ASN1_INTEGER_to_BN \
+ (*(BIGNUM * (*)(const ASN1_INTEGER *ai, BIGNUM *bn))crypto_sw[13].ptr)
+#define BN_free (*(void (*)(const BIGNUM *a))crypto_sw[14].ptr)
+#define CRYPTO_free (*(void (*)(void *addr))crypto_sw[15].ptr)
+
+#define OPENSSL_free(a) CRYPTO_free(a)
+
+
+/* init_ssl_ctx() function updates this array.
+ * It loads SSL library dynamically and changes NULLs to the actual addresses
+ * of respective functions. The macros above (like SSL_connect()) are really
+ * just calling these functions indirectly via the pointer. */
+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},
+{"TLS_server_method", NULL},
+{"OPENSSL_init_ssl", NULL},
+{"SSL_CTX_use_PrivateKey_file", NULL},
+{"SSL_CTX_use_certificate_file", NULL},
+{"SSL_CTX_set_default_passwd_cb", NULL},
+{"SSL_CTX_free", NULL},
+{"SSL_CTX_use_certificate_chain_file", NULL},
+{"TLS_client_method", NULL},
+{"SSL_pending", NULL},
+{"SSL_CTX_set_verify", NULL},
+{"SSL_shutdown", NULL},
+{"SSL_CTX_load_verify_locations", NULL},
+{"SSL_CTX_set_default_verify_paths", NULL},
+{"SSL_CTX_set_verify_depth", NULL},
+{"SSL_get_peer_certificate", NULL},
+{"SSL_get_version", NULL},
+{"SSL_get_current_cipher", NULL},
+{"SSL_CIPHER_get_name", NULL},
+{"SSL_CTX_check_private_key", NULL},
+{"SSL_CTX_set_session_id_context", NULL},
+{"SSL_CTX_ctrl", NULL},
+{"SSL_CTX_set_cipher_list", NULL},
+{"SSL_CTX_set_options", NULL},
+{"SSL_CTX_set_info_callback", NULL},
+{"SSL_get_ex_data", NULL},
+{"SSL_set_ex_data", NULL},
+{"SSL_CTX_callback_ctrl", NULL},
+{"SSL_get_servername", NULL},
+{"SSL_set_SSL_CTX", NULL},
+{NULL, NULL}};
+
+
+/* Similar array as ssl_sw. These functions could be located in different
+ * lib. */
+static struct ssl_func crypto_sw[] = {{"ERR_get_error", NULL},
+{"ERR_error_string", NULL},
+{"ERR_remove_state", NULL},
+{"CONF_modules_unload", NULL},
+{"X509_free", NULL},
+{"X509_get_subject_name", NULL},
+{"X509_get_issuer_name", NULL},
+{"X509_NAME_oneline", NULL},
+{"X509_get_serialNumber", NULL},
+{"EVP_get_digestbyname", NULL},
+{"EVP_Digest", NULL},
+{"i2d_X509", NULL},
+{"BN_bn2hex", NULL},
+{"ASN1_INTEGER_to_BN", NULL},
+{"BN_free", NULL},
+{"CRYPTO_free", NULL},
+{NULL, NULL}};
+#else
+
+#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
+#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr)
+#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr)
+#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr)
+#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr)
+#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr)
+#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr)
+#define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr)
+#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr)
+#define SSLv23_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr)
+#define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr)
+#define SSL_CTX_use_PrivateKey_file \
+ (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr)
+#define SSL_CTX_use_certificate_file \
+ (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr)
+#define SSL_CTX_set_default_passwd_cb \
+ (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr)
+#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr)
+#define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr)
+#define SSL_CTX_use_certificate_chain_file \
+ (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr)
+#define SSLv23_client_method (*(SSL_METHOD * (*)(void))ssl_sw[17].ptr)
+#define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr)
+#define SSL_CTX_set_verify \
+ (*(void (*)(SSL_CTX *, \
+ int, \
+ int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[19].ptr)
+#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr)
+#define SSL_CTX_load_verify_locations \
+ (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr)
+#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[22].ptr)
+#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr)
+#define SSL_get_peer_certificate (*(X509 * (*)(SSL *))ssl_sw[24].ptr)
+#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[25].ptr)
+#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *))ssl_sw[26].ptr)
+#define SSL_CIPHER_get_name \
+ (*(const char *(*)(const SSL_CIPHER *))ssl_sw[27].ptr)
+#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[28].ptr)
+#define SSL_CTX_set_session_id_context \
+ (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[29].ptr)
+#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr)
+#define SSL_CTX_set_cipher_list \
+ (*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr)
+#define SSL_CTX_set_info_callback \
+ (*(void (*)(SSL_CTX *, void (*callback)(SSL * s, int, int)))ssl_sw[32].ptr)
+#define SSL_get_ex_data (*(char *(*)(SSL *, int))ssl_sw[33].ptr)
+#define SSL_set_ex_data (*(void (*)(SSL *, int, char *))ssl_sw[34].ptr)
+#define SSL_CTX_callback_ctrl \
+ (*(long (*)(SSL_CTX *, int, void (*)(void)))ssl_sw[35].ptr)
+#define SSL_get_servername \
+ (*(const char *(*)(const SSL *, int type))ssl_sw[36].ptr)
+#define SSL_set_SSL_CTX (*(SSL_CTX * (*)(SSL *, SSL_CTX *))ssl_sw[37].ptr)
+
+#define SSL_CTX_set_options(ctx, op) \
+ SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL)
+#define SSL_CTX_clear_options(ctx, op) \
+ SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
+#define SSL_CTX_set_ecdh_auto(ctx, onoff) \
+ SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL)
+
+#define SSL_CTRL_SET_TLSEXT_SERVERNAME_CB 53
+#define SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG 54
+#define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \
+ SSL_CTX_callback_ctrl(ctx, \
+ SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, \
+ (void (*)(void))cb)
+#define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \
+ SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, (void *)arg)
+
+#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
+#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
+
+#define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)arg))
+#define SSL_get_app_data(s) (SSL_get_ex_data(s, 0))
+
+#define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr)
+#define CRYPTO_set_locking_callback \
+ (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr)
+#define CRYPTO_set_id_callback \
+ (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr)
+#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr)
+#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr)
+#define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[5].ptr)
+#define ERR_free_strings (*(void (*)(void))crypto_sw[6].ptr)
+#define ENGINE_cleanup (*(void (*)(void))crypto_sw[7].ptr)
+#define CONF_modules_unload (*(void (*)(int))crypto_sw[8].ptr)
+#define CRYPTO_cleanup_all_ex_data (*(void (*)(void))crypto_sw[9].ptr)
+#define EVP_cleanup (*(void (*)(void))crypto_sw[10].ptr)
+#define X509_free (*(void (*)(X509 *))crypto_sw[11].ptr)
+#define X509_get_subject_name (*(X509_NAME * (*)(X509 *))crypto_sw[12].ptr)
+#define X509_get_issuer_name (*(X509_NAME * (*)(X509 *))crypto_sw[13].ptr)
+#define X509_NAME_oneline \
+ (*(char *(*)(X509_NAME *, char *, int))crypto_sw[14].ptr)
+#define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *))crypto_sw[15].ptr)
+#define i2c_ASN1_INTEGER \
+ (*(int (*)(ASN1_INTEGER *, unsigned char **))crypto_sw[16].ptr)
+#define EVP_get_digestbyname \
+ (*(const EVP_MD *(*)(const char *))crypto_sw[17].ptr)
+#define EVP_Digest \
+ (*(int (*)( \
+ const void *, size_t, void *, unsigned int *, const EVP_MD *, void *)) \
+ crypto_sw[18].ptr)
+#define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[19].ptr)
+#define BN_bn2hex (*(char *(*)(const BIGNUM *a))crypto_sw[20].ptr)
+#define ASN1_INTEGER_to_BN \
+ (*(BIGNUM * (*)(const ASN1_INTEGER *ai, BIGNUM *bn))crypto_sw[21].ptr)
+#define BN_free (*(void (*)(const BIGNUM *a))crypto_sw[22].ptr)
+#define CRYPTO_free (*(void (*)(void *addr))crypto_sw[23].ptr)
+
+#define OPENSSL_free(a) CRYPTO_free(a)
+
+/* init_ssl_ctx() function updates this array.
+ * It loads SSL library dynamically and changes NULLs to the actual addresses
+ * of respective functions. The macros above (like SSL_connect()) are really
+ * just calling these functions indirectly via the pointer. */
+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},
+{"SSL_CTX_free", NULL},
+{"SSL_load_error_strings", NULL},
+{"SSL_CTX_use_certificate_chain_file", NULL},
+{"SSLv23_client_method", NULL},
+{"SSL_pending", NULL},
+{"SSL_CTX_set_verify", NULL},
+{"SSL_shutdown", NULL},
+{"SSL_CTX_load_verify_locations", NULL},
+{"SSL_CTX_set_default_verify_paths", NULL},
+{"SSL_CTX_set_verify_depth", NULL},
+{"SSL_get_peer_certificate", NULL},
+{"SSL_get_version", NULL},
+{"SSL_get_current_cipher", NULL},
+{"SSL_CIPHER_get_name", NULL},
+{"SSL_CTX_check_private_key", NULL},
+{"SSL_CTX_set_session_id_context", NULL},
+{"SSL_CTX_ctrl", NULL},
+{"SSL_CTX_set_cipher_list", NULL},
+{"SSL_CTX_set_info_callback", NULL},
+{"SSL_get_ex_data", NULL},
+{"SSL_set_ex_data", NULL},
+{"SSL_CTX_callback_ctrl", NULL},
+{"SSL_get_servername", NULL},
+{"SSL_set_SSL_CTX", NULL},
+{NULL, NULL}};
+
+
+/* Similar array as ssl_sw. These functions could be located in different
+ * lib. */
+static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL},
+{"CRYPTO_set_locking_callback", NULL},
+{"CRYPTO_set_id_callback", NULL},
+{"ERR_get_error", NULL},
+{"ERR_error_string", NULL},
+{"ERR_remove_state", NULL},
+{"ERR_free_strings", NULL},
+{"ENGINE_cleanup", NULL},
+{"CONF_modules_unload", NULL},
+{"CRYPTO_cleanup_all_ex_data", NULL},
+{"EVP_cleanup", NULL},
+{"X509_free", NULL},
+{"X509_get_subject_name", NULL},
+{"X509_get_issuer_name", NULL},
+{"X509_NAME_oneline", NULL},
+{"X509_get_serialNumber", NULL},
+{"i2c_ASN1_INTEGER", NULL},
+{"EVP_get_digestbyname", NULL},
+{"EVP_Digest", NULL},
+{"i2d_X509", NULL},
+{"BN_bn2hex", NULL},
+{"ASN1_INTEGER_to_BN", NULL},
+{"BN_free", NULL},
+{"CRYPTO_free", NULL},
+{NULL, NULL}};
+#endif /* OPENSSL_API_1_1 */
+#endif /* NO_SSL_DL */
+#endif /* NO_SSL */
+
+
+#if !defined(NO_CACHING)
+static const char *month_names[] = {"Jan",
+"Feb",
+"Mar",
+"Apr",
+"May",
+"Jun",
+"Jul",
+"Aug",
+"Sep",
+"Oct",
+"Nov",
+"Dec"};
+#endif /* !NO_CACHING */
+
+/* Unified socket address. For IPv6 support, add IPv6 address structure in
+ * the
+ * union u. */
+union usa {
+struct sockaddr sa;
+struct sockaddr_in sin;
+#if defined(USE_IPV6)
+struct sockaddr_in6 sin6;
+#endif
+};
+
+/* Describes a string (chunk of memory). */
+struct vec {
+const char *ptr;
+size_t len;
+};
+
+struct mg_file_stat {
+/* File properties filled by mg_stat: */
+uint64_t size;
+time_t last_modified;
+int is_directory; /* Set to 1 if mg_stat is called for a directory */
+int is_gzipped; /* Set to 1 if the content is gzipped, in which
+ * case we need a "Content-Eencoding: gzip" header */
+int location; /* 0 = nowhere, 1 = on disk, 2 = in memory */
+};
+
+struct mg_file_in_memory {
+char *p;
+uint32_t pos;
+char mode;
+};
+
+struct mg_file_access {
+/* File properties filled by mg_fopen: */
+FILE *fp;
+#if defined(MG_USE_OPEN_FILE)
+/* TODO (low): Remove obsolete "file in memory" implementation.
+ * In an "early 2017" discussion at Google groups
+ * https://groups.google.com/forum/#!topic/civetweb/h9HT4CmeYqI
+ * we decided to get rid of this feature (after some fade-out
+ * phase). */
+const char *membuf;
+#endif
+};
+
+struct mg_file {
+struct mg_file_stat stat;
+struct mg_file_access access;
+};
+
+#if defined(MG_USE_OPEN_FILE)
+
+#define STRUCT_FILE_INITIALIZER \
+ { \
+ { \
+ (uint64_t)0, (time_t)0, 0, 0, 0 \
+ } \
+ , \
+ { \
+ (FILE *) NULL, (const char *)NULL \
+ } \
+ }
+
+#else
+
+#define STRUCT_FILE_INITIALIZER \
+ { \
+ { \
+ (uint64_t)0, (time_t)0, 0, 0, 0 \
+ } \
+ , \
+ { \
+ (FILE *) NULL \
+ } \
+ }
+
+#endif
+
+
+/* Describes listening socket, or socket which was accept()-ed by the master
+ * thread and queued for future handling by the worker thread. */
+struct socket {
+SOCKET sock; /* Listening socket */
+union usa lsa; /* Local socket address */
+union usa rsa; /* Remote socket address */
+unsigned char is_ssl; /* Is port SSL-ed */
+unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
+ * port */
+unsigned char in_use; /* Is valid */
+};
+
+
+/* Enum const for all options must be in sync with
+ * static struct mg_option config_options[]
+ * This is tested in the unit test (test/private.c)
+ * "Private Config Options"
+ */
+enum {
+/* Once for each server */
+LISTENING_PORTS,
+NUM_THREADS,
+RUN_AS_USER,
+CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
+ * socket option typedef TCP_NODELAY. */
+MAX_REQUEST_SIZE,
+LINGER_TIMEOUT,
+#if defined(__linux__)
+ALLOW_SENDFILE_CALL,
+#endif
+#if defined(_WIN32)
+CASE_SENSITIVE_FILES,
+#endif
+THROTTLE,
+ACCESS_LOG_FILE,
+ERROR_LOG_FILE,
+ENABLE_KEEP_ALIVE,
+REQUEST_TIMEOUT,
+KEEP_ALIVE_TIMEOUT,
+#if defined(USE_WEBSOCKET)
+WEBSOCKET_TIMEOUT,
+ENABLE_WEBSOCKET_PING_PONG,
+#endif
+DECODE_URL,
+#if defined(USE_LUA)
+LUA_BACKGROUND_SCRIPT,
+LUA_BACKGROUND_SCRIPT_PARAMS,
+#endif
+#if defined(USE_TIMERS)
+CGI_TIMEOUT,
+#endif
+
+/* Once for each domain */
+DOCUMENT_ROOT,
+CGI_EXTENSIONS,
+CGI_ENVIRONMENT,
+PUT_DELETE_PASSWORDS_FILE,
+CGI_INTERPRETER,
+PROTECT_URI,
+AUTHENTICATION_DOMAIN,
+ENABLE_AUTH_DOMAIN_CHECK,
+SSI_EXTENSIONS,
+ENABLE_DIRECTORY_LISTING,
+GLOBAL_PASSWORDS_FILE,
+INDEX_FILES,
+ACCESS_CONTROL_LIST,
+EXTRA_MIME_TYPES,
+SSL_CERTIFICATE,
+SSL_CERTIFICATE_CHAIN,
+URL_REWRITE_PATTERN,
+HIDE_FILES,
+SSL_DO_VERIFY_PEER,
+SSL_CA_PATH,
+SSL_CA_FILE,
+SSL_VERIFY_DEPTH,
+SSL_DEFAULT_VERIFY_PATHS,
+SSL_CIPHER_LIST,
+SSL_PROTOCOL_VERSION,
+SSL_SHORT_TRUST,
+
+#if defined(USE_LUA)
+LUA_PRELOAD_FILE,
+LUA_SCRIPT_EXTENSIONS,
+LUA_SERVER_PAGE_EXTENSIONS,
+#if defined(MG_EXPERIMENTAL_INTERFACES)
+LUA_DEBUG_PARAMS,
+#endif
+#endif
+#if defined(USE_DUKTAPE)
+DUKTAPE_SCRIPT_EXTENSIONS,
+#endif
+
+#if defined(USE_WEBSOCKET)
+WEBSOCKET_ROOT,
+#endif
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+LUA_WEBSOCKET_EXTENSIONS,
+#endif
+
+ACCESS_CONTROL_ALLOW_ORIGIN,
+ACCESS_CONTROL_ALLOW_METHODS,
+ACCESS_CONTROL_ALLOW_HEADERS,
+ERROR_PAGES,
+#if !defined(NO_CACHING)
+STATIC_FILE_MAX_AGE,
+#endif
+#if !defined(NO_SSL)
+STRICT_HTTPS_MAX_AGE,
+#endif
+ADDITIONAL_HEADER,
+ALLOW_INDEX_SCRIPT_SUB_RES,
+
+NUM_OPTIONS
+};
+
+
+/* Config option name, config types, default value.
+ * Must be in the same order as the enum const above.
+ */
+static const struct mg_option config_options[] = {
+
+/* Once for each server */
+{"listening_ports", MG_CONFIG_TYPE_STRING_LIST, "8080"},
+{"num_threads", MG_CONFIG_TYPE_NUMBER, "50"},
+{"run_as_user", MG_CONFIG_TYPE_STRING, NULL},
+{"tcp_nodelay", MG_CONFIG_TYPE_NUMBER, "0"},
+{"max_request_size", MG_CONFIG_TYPE_NUMBER, "16384"},
+{"linger_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
+#if defined(__linux__)
+{"allow_sendfile_call", MG_CONFIG_TYPE_BOOLEAN, "yes"},
+#endif
+#if defined(_WIN32)
+{"case_sensitive", MG_CONFIG_TYPE_BOOLEAN, "no"},
+#endif
+{"throttle", MG_CONFIG_TYPE_STRING_LIST, NULL},
+{"access_log_file", MG_CONFIG_TYPE_FILE, NULL},
+{"error_log_file", MG_CONFIG_TYPE_FILE, NULL},
+{"enable_keep_alive", MG_CONFIG_TYPE_BOOLEAN, "no"},
+{"request_timeout_ms", MG_CONFIG_TYPE_NUMBER, "30000"},
+{"keep_alive_timeout_ms", MG_CONFIG_TYPE_NUMBER, "500"},
+#if defined(USE_WEBSOCKET)
+{"websocket_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
+{"enable_websocket_ping_pong", MG_CONFIG_TYPE_BOOLEAN, "no"},
+#endif
+{"decode_url", MG_CONFIG_TYPE_BOOLEAN, "yes"},
+#if defined(USE_LUA)
+{"lua_background_script", MG_CONFIG_TYPE_FILE, NULL},
+{"lua_background_script_params", MG_CONFIG_TYPE_STRING_LIST, NULL},
+#endif
+#if defined(USE_TIMERS)
+{"cgi_timeout_ms", MG_CONFIG_TYPE_NUMBER, NULL},
+#endif
+
+/* Once for each domain */
+{"document_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
+{"cgi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
+{"cgi_environment", MG_CONFIG_TYPE_STRING_LIST, NULL},
+{"put_delete_auth_file", MG_CONFIG_TYPE_FILE, NULL},
+{"cgi_interpreter", MG_CONFIG_TYPE_FILE, NULL},
+{"protect_uri", MG_CONFIG_TYPE_STRING_LIST, NULL},
+{"authentication_domain", MG_CONFIG_TYPE_STRING, "mydomain.com"},
+{"enable_auth_domain_check", MG_CONFIG_TYPE_BOOLEAN, "yes"},
+{"ssi_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"},
+{"enable_directory_listing", MG_CONFIG_TYPE_BOOLEAN, "yes"},
+{"global_auth_file", MG_CONFIG_TYPE_FILE, NULL},
+{"index_files",
+MG_CONFIG_TYPE_STRING_LIST,
+#if defined(USE_LUA)
+"index.xhtml,index.html,index.htm,"
+"index.lp,index.lsp,index.lua,index.cgi,"
+"index.shtml,index.php"},
+#else
+"index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
+#endif
+{"access_control_list", MG_CONFIG_TYPE_STRING_LIST, NULL},
+{"extra_mime_types", MG_CONFIG_TYPE_STRING_LIST, NULL},
+{"ssl_certificate", MG_CONFIG_TYPE_FILE, NULL},
+{"ssl_certificate_chain", MG_CONFIG_TYPE_FILE, NULL},
+{"url_rewrite_patterns", MG_CONFIG_TYPE_STRING_LIST, NULL},
+{"hide_files_patterns", MG_CONFIG_TYPE_EXT_PATTERN, NULL},
+
+{"ssl_verify_peer", MG_CONFIG_TYPE_YES_NO_OPTIONAL, "no"},
+
+{"ssl_ca_path", MG_CONFIG_TYPE_DIRECTORY, NULL},
+{"ssl_ca_file", MG_CONFIG_TYPE_FILE, NULL},
+{"ssl_verify_depth", MG_CONFIG_TYPE_NUMBER, "9"},
+{"ssl_default_verify_paths", MG_CONFIG_TYPE_BOOLEAN, "yes"},
+{"ssl_cipher_list", MG_CONFIG_TYPE_STRING, NULL},
+{"ssl_protocol_version", MG_CONFIG_TYPE_NUMBER, "0"},
+{"ssl_short_trust", MG_CONFIG_TYPE_BOOLEAN, "no"},
+
+#if defined(USE_LUA)
+{"lua_preload_file", MG_CONFIG_TYPE_FILE, NULL},
+{"lua_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
+{"lua_server_page_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
+#if defined(MG_EXPERIMENTAL_INTERFACES)
+{"lua_debug", MG_CONFIG_TYPE_STRING, NULL},
+#endif
+#endif
+#if defined(USE_DUKTAPE)
+/* The support for duktape is still in alpha version state.
+ * The name of this config option might change. */
+{"duktape_script_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
+#endif
+
+#if defined(USE_WEBSOCKET)
+{"websocket_root", MG_CONFIG_TYPE_DIRECTORY, NULL},
+#endif
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+{"lua_websocket_pattern", MG_CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
+#endif
+{"access_control_allow_origin", MG_CONFIG_TYPE_STRING, "*"},
+{"access_control_allow_methods", MG_CONFIG_TYPE_STRING, "*"},
+{"access_control_allow_headers", MG_CONFIG_TYPE_STRING, "*"},
+{"error_pages", MG_CONFIG_TYPE_DIRECTORY, NULL},
+#if !defined(NO_CACHING)
+{"static_file_max_age", MG_CONFIG_TYPE_NUMBER, "3600"},
+#endif
+#if !defined(NO_SSL)
+{"strict_transport_security_max_age", MG_CONFIG_TYPE_NUMBER, NULL},
+#endif
+{"additional_header", MG_CONFIG_TYPE_STRING_MULTILINE, NULL},
+{"allow_index_script_resource", MG_CONFIG_TYPE_BOOLEAN, "no"},
+
+{NULL, MG_CONFIG_TYPE_UNKNOWN, NULL}};
+
+
+/* Check if the config_options and the corresponding enum have compatible
+ * sizes. */
+mg_static_assert((sizeof(config_options) / sizeof(config_options[0]))
+== (NUM_OPTIONS + 1),
+"config_options and enum not sync");
+
+
+enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER };
+
+
+struct mg_handler_info {
+/* Name/Pattern of the URI. */
+char *uri;
+size_t uri_len;
+
+/* handler type */
+int handler_type;
+
+/* Handler for http/https or authorization requests. */
+mg_request_handler handler;
+unsigned int refcount;
+pthread_mutex_t refcount_mutex; /* Protects refcount */
+pthread_cond_t
+refcount_cond; /* Signaled when handler refcount is decremented */
+
+/* Handler for ws/wss (websocket) requests. */
+mg_websocket_connect_handler connect_handler;
+mg_websocket_ready_handler ready_handler;
+mg_websocket_data_handler data_handler;
+mg_websocket_close_handler close_handler;
+
+/* accepted subprotocols for ws/wss requests. */
+struct mg_websocket_subprotocols *subprotocols;
+
+/* Handler for authorization requests */
+mg_authorization_handler auth_handler;
+
+/* User supplied argument for the handler function. */
+void *cbdata;
+
+/* next handler in a linked list */
+struct mg_handler_info *next;
+};
+
+
+enum {
+CONTEXT_INVALID,
+CONTEXT_SERVER,
+CONTEXT_HTTP_CLIENT,
+CONTEXT_WS_CLIENT
+};
+
+
+struct mg_domain_context {
+SSL_CTX *ssl_ctx; /* SSL context */
+char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */
+struct mg_handler_info *handlers; /* linked list of uri handlers */
+
+/* Server nonce */
+uint64_t auth_nonce_mask; /* Mask for all nonce values */
+unsigned long nonce_count; /* Used nonces, used for authentication */
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+/* linked list of shared lua websockets */
+struct mg_shared_lua_websocket_list *shared_lua_websockets;
+#endif
+
+/* Linked list of domains */
+struct mg_domain_context *next;
+};
+
+
+struct mg_context {
+
+/* Part 1 - Physical context:
+ * This holds threads, ports, timeouts, ...
+ * set for the entire server, independent from the
+ * addressed hostname.
+ */
+
+/* Connection related */
+int context_type; /* See CONTEXT_* above */
+
+struct socket *listening_sockets;
+struct pollfd *listening_socket_fds;
+unsigned int num_listening_sockets;
+
+struct mg_connection *worker_connections; /* The connection struct, pre-
+ * allocated for each worker */
+
+#if defined(USE_SERVER_STATS)
+int active_connections;
+int max_connections;
+int64_t total_connections;
+int64_t total_requests;
+int64_t total_data_read;
+int64_t total_data_written;
+#endif
+
+/* Thread related */
+volatile int stop_flag; /* Should we stop event loop */
+pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */
+
+pthread_t masterthreadid; /* The master thread ID */
+unsigned int
+cfg_worker_threads; /* The number of configured worker threads. */
+pthread_t *worker_threadids; /* The worker thread IDs */
+
+/* Connection to thread dispatching */
+#if defined(ALTERNATIVE_QUEUE)
+struct socket *client_socks;
+void **client_wait_events;
+#else
+struct socket queue[MGSQLEN]; /* Accepted sockets */
+volatile int sq_head; /* Head of the socket queue */
+volatile int sq_tail; /* Tail of the socket queue */
+pthread_cond_t sq_full; /* Signaled when socket is produced */
+pthread_cond_t sq_empty; /* Signaled when socket is consumed */
+#endif
+
+/* Memory related */
+unsigned int max_request_size; /* The max request size */
+
+#if defined(USE_SERVER_STATS)
+struct mg_memory_stat ctx_memory;
+#endif
+
+/* Operating system related */
+char *systemName; /* What operating system is running */
+time_t start_time; /* Server start time, used for authentication
+ * and for diagnstics. */
+
+#if defined(USE_TIMERS)
+struct ttimers *timers;
+#endif
+
+/* Lua specific: Background operations and shared websockets */
+#if defined(USE_LUA)
+void *lua_background_state;
+#endif
+
+/* Server nonce */
+pthread_mutex_t nonce_mutex; /* Protects nonce_count */
+
+/* Server callbacks */
+struct mg_callbacks callbacks; /* User-defined callback function */
+void *user_data; /* User-defined data */
+
+/* Part 2 - Logical domain:
+ * This holds hostname, TLS certificate, document root, ...
+ * set for a domain hosted at the server.
+ * There may be multiple domains hosted at one physical server.
+ * The default domain "dd" is the first element of a list of
+ * domains.
+ */
+struct mg_domain_context dd; /* default domain */
+};
+
+
+#if defined(USE_SERVER_STATS)
+static struct mg_memory_stat mg_common_memory = {0, 0, 0};
+
+static struct mg_memory_stat *
+get_memory_stat(struct mg_context *ctx)
+{
+if (ctx) {
+return &(ctx->ctx_memory);
+}
+return &mg_common_memory;
+}
+#endif
+
+enum {
+CONNECTION_TYPE_INVALID,
+CONNECTION_TYPE_REQUEST,
+CONNECTION_TYPE_RESPONSE
+};
+
+struct mg_connection {
+int connection_type; /* see CONNECTION_TYPE_* above */
+
+struct mg_request_info request_info;
+struct mg_response_info response_info;
+
+struct mg_context *phys_ctx;
+struct mg_domain_context *dom_ctx;
+
+#if defined(USE_SERVER_STATS)
+int conn_state; /* 0 = undef, numerical value may change in different
+ * versions. For the current definition, see
+ * mg_get_connection_info_impl */
+#endif
+
+const char *host; /* Host (HTTP/1.1 header or SNI) */
+SSL *ssl; /* SSL descriptor */
+SSL_CTX *client_ssl_ctx; /* SSL context for client connections */
+struct socket client; /* Connected client */
+time_t conn_birth_time; /* Time (wall clock) when connection was
+ * established */
+struct timespec req_time; /* Time (since system start) when the request
+ * was received */
+int64_t num_bytes_sent; /* Total bytes sent to client */
+int64_t content_len; /* Content-Length header value */
+int64_t consumed_content; /* How many bytes of content have been read */
+int is_chunked; /* Transfer-Encoding is chunked:
+ * 0 = not chunked,
+ * 1 = chunked, do data read yet,
+ * 2 = chunked, some data read,
+ * 3 = chunked, all data read
+ */
+size_t chunk_remainder; /* Unread data from the last chunk */
+char *buf; /* Buffer for received data */
+char *path_info; /* PATH_INFO part of the URL */
+
+int must_close; /* 1 if connection must be closed */
+int accept_gzip; /* 1 if gzip encoding is accepted */
+int in_error_handler; /* 1 if in handler for user defined error
+ * pages */
+#if defined(USE_WEBSOCKET)
+int in_websocket_handling; /* 1 if in read_websocket */
+#endif
+int handled_requests; /* Number of requests handled by this connection
+ */
+int buf_size; /* Buffer size */
+int request_len; /* Size of the request + headers in a buffer */
+int data_len; /* Total size of data in a buffer */
+int status_code; /* HTTP reply status code, e.g. 200 */
+int throttle; /* Throttling, bytes/sec. <= 0 means no
+ * throttle */
+
+time_t last_throttle_time; /* Last time throttled data was sent */
+int64_t last_throttle_bytes; /* Bytes sent this second */
+pthread_mutex_t mutex; /* Used by mg_(un)lock_connection to ensure
+ * atomic transmissions for websockets */
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+void *lua_websocket_state; /* Lua_State for a websocket connection */
+#endif
+
+int thread_index; /* Thread index within ctx */
+};
+
+
+/* Directory entry */
+struct de {
+struct mg_connection *conn;
+char *file_name;
+struct mg_file_stat file;
+};
+
+
+#if defined(USE_WEBSOCKET)
+static int is_websocket_protocol(const struct mg_connection *conn);
+#else
+#define is_websocket_protocol(conn) (0)
+#endif
+
+
+#define mg_cry_internal(conn, fmt, ...) \
+ mg_cry_internal_wrap(conn, __func__, __LINE__, fmt, __VA_ARGS__)
+
+static void mg_cry_internal_wrap(const struct mg_connection *conn,
+const char *func,
+unsigned line,
+const char *fmt,
+...) PRINTF_ARGS(4, 5);
+
+
+#if !defined(NO_THREAD_NAME)
+#if defined(_WIN32) && defined(_MSC_VER)
+/* Set the thread name for debugging purposes in Visual Studio
+ * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
+ */
+#pragma pack(push, 8)
+typedef struct tagTHREADNAME_INFO {
+DWORD dwType; /* Must be 0x1000. */
+LPCSTR szName; /* Pointer to name (in user addr space). */
+DWORD dwThreadID; /* Thread ID (-1=caller thread). */
+DWORD dwFlags; /* Reserved for future use, must be zero. */
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+#elif defined(__linux__)
+
+#include <sys/prctl.h>
+#include <sys/sendfile.h>
+#if defined(ALTERNATIVE_QUEUE)
+#include <sys/eventfd.h>
+#endif /* ALTERNATIVE_QUEUE */
+
+
+#if defined(ALTERNATIVE_QUEUE)
+
+static void *
+event_create(void)
+{
+int evhdl = eventfd(0, EFD_CLOEXEC);
+int *ret;
+
+if (evhdl == -1) {
+/* Linux uses -1 on error, Windows NULL. */
+/* However, Linux does not return 0 on success either. */
+return 0;
+}
+
+ret = (int *)mg_malloc(sizeof(int));
+if (ret) {
+*ret = evhdl;
+} else {
+(void)close(evhdl);
+}
+
+return (void *)ret;
+}
+
+
+static int
+event_wait(void *eventhdl)
+{
+uint64_t u;
+int evhdl, s;
+
+if (!eventhdl) {
+/* error */
+return 0;
+}
+evhdl = *(int *)eventhdl;
+
+s = (int)read(evhdl, &u, sizeof(u));
+if (s != sizeof(u)) {
+/* error */
+return 0;
+}
+(void)u; /* the value is not required */
+return 1;
+}
+
+
+static int
+event_signal(void *eventhdl)
+{
+uint64_t u = 1;
+int evhdl, s;
+
+if (!eventhdl) {
+/* error */
+return 0;
+}
+evhdl = *(int *)eventhdl;
+
+s = (int)write(evhdl, &u, sizeof(u));
+if (s != sizeof(u)) {
+/* error */
+return 0;
+}
+return 1;
+}
+
+
+static void
+event_destroy(void *eventhdl)
+{
+int evhdl;
+
+if (!eventhdl) {
+/* error */
+return;
+}
+evhdl = *(int *)eventhdl;
+
+close(evhdl);
+mg_free(eventhdl);
+}
+
+
+#endif
+
+#endif
+
+
+#if !defined(__linux__) && !defined(_WIN32) && defined(ALTERNATIVE_QUEUE)
+
+struct posix_event {
+pthread_mutex_t mutex;
+pthread_cond_t cond;
+};
+
+
+static void *
+event_create(void)
+{
+struct posix_event *ret = mg_malloc(sizeof(struct posix_event));
+if (ret == 0) {
+/* out of memory */
+return 0;
+}
+if (0 != pthread_mutex_init(&(ret->mutex), NULL)) {
+/* pthread mutex not available */
+mg_free(ret);
+return 0;
+}
+if (0 != pthread_cond_init(&(ret->cond), NULL)) {
+/* pthread cond not available */
+pthread_mutex_destroy(&(ret->mutex));
+mg_free(ret);
+return 0;
+}
+return (void *)ret;
+}
+
+
+static int
+event_wait(void *eventhdl)
+{
+struct posix_event *ev = (struct posix_event *)eventhdl;
+pthread_mutex_lock(&(ev->mutex));
+pthread_cond_wait(&(ev->cond), &(ev->mutex));
+pthread_mutex_unlock(&(ev->mutex));
+return 1;
+}
+
+
+static int
+event_signal(void *eventhdl)
+{
+struct posix_event *ev = (struct posix_event *)eventhdl;
+pthread_mutex_lock(&(ev->mutex));
+pthread_cond_signal(&(ev->cond));
+pthread_mutex_unlock(&(ev->mutex));
+return 1;
+}
+
+
+static void
+event_destroy(void *eventhdl)
+{
+struct posix_event *ev = (struct posix_event *)eventhdl;
+pthread_cond_destroy(&(ev->cond));
+pthread_mutex_destroy(&(ev->mutex));
+mg_free(ev);
+}
+#endif
+
+
+static void
+mg_set_thread_name(const char *name)
+{
+char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */
+
+mg_snprintf(
+NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name);
+
+#if defined(_WIN32)
+#if defined(_MSC_VER)
+/* Windows and Visual Studio Compiler */
+__try
+{
+THREADNAME_INFO info;
+info.dwType = 0x1000;
+info.szName = threadName;
+info.dwThreadID = ~0U;
+info.dwFlags = 0;
+
+RaiseException(0x406D1388,
+0,
+sizeof(info) / sizeof(ULONG_PTR),
+(ULONG_PTR *)&info);
+}
+__except(EXCEPTION_EXECUTE_HANDLER)
+{
+}
+#elif defined(__MINGW32__)
+/* No option known to set thread name for MinGW */
+#endif
+#elif defined(_GNU_SOURCE) && defined(__GLIBC__) \
+ && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
+/* pthread_setname_np first appeared in glibc in version 2.12*/
+(void)pthread_setname_np(pthread_self(), threadName);
+#elif defined(__linux__)
+/* on linux we can use the old prctl function */
+(void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
+#endif
+}
+#else /* !defined(NO_THREAD_NAME) */
+void
+mg_set_thread_name(const char *threadName)
+{
+}
+#endif
+
+
+#if defined(MG_LEGACY_INTERFACE)
+const char **
+mg_get_valid_option_names(void)
+{
+/* This function is deprecated. Use mg_get_valid_options instead. */
+static const char *
+
<TRUNCATED>
[4/6] celix git commit: CELIX-451: Adds celix_bundle_dir for copying
files. Also remove mongoose example and adds a new civetweb example.
Posted by pn...@apache.org.
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/civetweb/civetweb.h
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/civetweb/civetweb.h b/examples/celix-examples/civetweb/civetweb/civetweb.h
new file mode 100644
index 0000000..5ecc75b
--- /dev/null
+++ b/examples/celix-examples/civetweb/civetweb/civetweb.h
@@ -0,0 +1,1498 @@
+/* Copyright (c) 2013-2017 the Civetweb developers
+ * Copyright (c) 2004-2013 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.
+ */
+
+#ifndef CIVETWEB_HEADER_INCLUDED
+#define CIVETWEB_HEADER_INCLUDED
+
+#define CIVETWEB_VERSION "1.11"
+#define CIVETWEB_VERSION_MAJOR (1)
+#define CIVETWEB_VERSION_MINOR (11)
+#define CIVETWEB_VERSION_PATCH (0)
+
+#ifndef CIVETWEB_API
+#if defined(_WIN32)
+#if defined(CIVETWEB_DLL_EXPORTS)
+#define CIVETWEB_API __declspec(dllexport)
+#elif defined(CIVETWEB_DLL_IMPORTS)
+#define CIVETWEB_API __declspec(dllimport)
+#else
+#define CIVETWEB_API
+#endif
+#elif __GNUC__ >= 4
+#define CIVETWEB_API __attribute__((visibility("default")))
+#else
+#define CIVETWEB_API
+#endif
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* Init Features */
+enum {
+ MG_FEATURES_DEFAULT = 0x0u,
+
+ /* Support files from local directories */
+ /* Will only work, if NO_FILES is not set. */
+ MG_FEATURES_FILES = 0x1u,
+
+ /* Support transport layer security (TLS). */
+ /* SSL is still often used synonymously for TLS. */
+ /* Will only work, if NO_SSL is not set. */
+ MG_FEATURES_TLS = 0x2u,
+ MG_FEATURES_SSL = 0x2u,
+
+ /* Support common gateway interface (CGI). */
+ /* Will only work, if NO_CGI is not set. */
+ MG_FEATURES_CGI = 0x4u,
+
+ /* Support IPv6. */
+ /* Will only work, if USE_IPV6 is set. */
+ MG_FEATURES_IPV6 = 0x8u,
+
+ /* Support WebSocket protocol. */
+ /* Will only work, if USE_WEBSOCKET is set. */
+ MG_FEATURES_WEBSOCKET = 0x10u,
+
+ /* Support server side Lua scripting. */
+ /* Will only work, if USE_LUA is set. */
+ MG_FEATURES_LUA = 0x20u,
+
+ /* Support server side JavaScript scripting. */
+ /* Will only work, if USE_DUKTAPE is set. */
+ MG_FEATURES_SSJS = 0x40u,
+
+ /* Provide data required for caching files. */
+ /* Will only work, if NO_CACHING is not set. */
+ MG_FEATURES_CACHE = 0x80u,
+
+ /* Collect server status information. */
+ /* Will only work, if USE_SERVER_STATS is set. */
+ MG_FEATURES_STATS = 0x100u,
+
+ /* Support on-the-fly compression. */
+ /* Will only work, if USE_ZLIB is set. */
+ MG_FEATURES_COMPRESSION = 0x200u,
+
+ /* Collect server status information. */
+ /* Will only work, if USE_SERVER_STATS is set. */
+ MG_FEATURES_ALL = 0xFFFFu
+};
+
+
+/* Initialize this library. This should be called once before any other
+ * function from this library. This function is not guaranteed to be
+ * thread safe.
+ * Parameters:
+ * features: bit mask for features to be initialized.
+ * Note: The TLS libraries (like OpenSSL) is initialized
+ * only if the MG_FEATURES_TLS bit is set.
+ * Currently the other bits do not influence
+ * initialization, but this may change in future
+ * versions.
+ * Return value:
+ * initialized features
+ * 0: error
+ */
+CIVETWEB_API unsigned mg_init_library(unsigned features);
+
+
+/* Un-initialize this library.
+ * Return value:
+ * 0: error
+ */
+CIVETWEB_API unsigned mg_exit_library(void);
+
+
+struct mg_context; /* Handle for the HTTP service itself */
+struct mg_connection; /* Handle for the individual connection */
+
+
+/* Maximum number of headers */
+#define MG_MAX_HEADERS (64)
+
+struct mg_header {
+ const char *name; /* HTTP header name */
+ const char *value; /* HTTP header value */
+};
+
+
+/* This structure contains information about the HTTP request. */
+struct mg_request_info {
+ const char *request_method; /* "GET", "POST", etc */
+ const char *request_uri; /* URL-decoded URI (absolute or relative,
+ * as in the request) */
+ const char *local_uri; /* URL-decoded URI (relative). Can be NULL
+ * if the request_uri does not address a
+ * resource at the server host. */
+#if defined(MG_LEGACY_INTERFACE) /* 2017-02-04, deprecated 2014-09-14 */
+ const char *uri; /* Deprecated: use local_uri instead */
+#endif
+ const char *http_version; /* E.g. "1.0", "1.1" */
+ const char *query_string; /* URL part after '?', not including '?', or
+ NULL */
+ const char *remote_user; /* Authenticated user, or NULL if no auth
+ used */
+ char remote_addr[48]; /* Client's IP address as a string. */
+
+ long long content_length; /* Length (in bytes) of the request body,
+ can be -1 if no length was given. */
+ int remote_port; /* Client's port */
+ int is_ssl; /* 1 if SSL-ed, 0 if not */
+ void *user_data; /* User data pointer passed to mg_start() */
+ void *conn_data; /* Connection-specific user data */
+
+ int num_headers; /* Number of HTTP headers */
+ struct mg_header
+ http_headers[MG_MAX_HEADERS]; /* Allocate maximum headers */
+
+ struct mg_client_cert *client_cert; /* Client certificate information */
+
+ const char *acceptedWebSocketSubprotocol; /* websocket subprotocol,
+ * accepted during handshake */
+};
+
+
+/* This structure contains information about the HTTP request. */
+/* This structure may be extended in future versions. */
+struct mg_response_info {
+ int status_code; /* E.g. 200 */
+ const char *status_text; /* E.g. "OK" */
+ const char *http_version; /* E.g. "1.0", "1.1" */
+
+ long long content_length; /* Length (in bytes) of the request body,
+ can be -1 if no length was given. */
+
+ int num_headers; /* Number of HTTP headers */
+ struct mg_header
+ http_headers[MG_MAX_HEADERS]; /* Allocate maximum headers */
+};
+
+
+/* Client certificate information (part of mg_request_info) */
+/* New nomenclature. */
+struct mg_client_cert {
+ void *peer_cert;
+ const char *subject;
+ const char *issuer;
+ const char *serial;
+ const char *finger;
+};
+
+#if defined(MG_LEGACY_INTERFACE) /* 2017-10-05 */
+/* Old nomenclature. */
+struct client_cert {
+ const char *subject;
+ const char *issuer;
+ const char *serial;
+ const char *finger;
+};
+#endif
+
+
+/* This structure needs to be passed to mg_start(), to let civetweb know
+ which callbacks to invoke. For a detailed description, see
+ https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md */
+struct mg_callbacks {
+ /* Called when civetweb has received new HTTP request.
+ If the callback returns one, it must process the request
+ by sending valid HTTP headers and a body. Civetweb will not do
+ any further processing. Otherwise it must return zero.
+ Note that since V1.7 the "begin_request" function is called
+ before an authorization check. If an authorization check is
+ required, use a request_handler instead.
+ Return value:
+ 0: civetweb will process the request itself. In this case,
+ the callback must not send any data to the client.
+ 1-999: callback already processed the request. Civetweb will
+ not send any data after the callback returned. The
+ return code is stored as a HTTP status code for the
+ access log. */
+ int (*begin_request)(struct mg_connection *);
+
+ /* Called when civetweb has finished processing request. */
+ void (*end_request)(const struct mg_connection *, int reply_status_code);
+
+ /* Called when civetweb is about to log a message. If callback returns
+ non-zero, civetweb does not log anything. */
+ int (*log_message)(const struct mg_connection *, const char *message);
+
+ /* Called when civetweb is about to log access. If callback returns
+ non-zero, civetweb does not log anything. */
+ int (*log_access)(const struct mg_connection *, const char *message);
+
+ /* Called when civetweb initializes SSL library.
+ Parameters:
+ user_data: parameter user_data passed when starting the server.
+ Return value:
+ 0: civetweb will set up the SSL certificate.
+ 1: civetweb assumes the callback already set up the certificate.
+ -1: initializing ssl fails. */
+ int (*init_ssl)(void *ssl_context, void *user_data);
+
+ /* Called when civetweb is about to create or free a SSL_CTX.
+ Parameters:
+ ssl_ctx: SSL_CTX pointer. NULL at creation time, Not NULL when mg_context
+ will be freed
+ user_data: parameter user_data passed when starting the server.
+ Return value:
+ 0: civetweb will continue to create the context, just as if the
+ callback would not be present.
+ The value in *ssl_ctx when the function returns is ignored.
+ 1: civetweb will copy the value from *ssl_ctx to the civetweb context
+ and doesn't create its own.
+ -1: initializing ssl fails.*/
+ int (*external_ssl_ctx)(void **ssl_ctx, void *user_data);
+
+#if defined(MG_LEGACY_INTERFACE) /* 2015-08-19 */
+ /* Called when websocket request is received, before websocket handshake.
+ Return value:
+ 0: civetweb proceeds with websocket handshake.
+ 1: connection is closed immediately.
+ This callback is deprecated: Use mg_set_websocket_handler instead. */
+ int (*websocket_connect)(const struct mg_connection *);
+
+ /* Called when websocket handshake is successfully completed, and
+ connection is ready for data exchange.
+ This callback is deprecated: Use mg_set_websocket_handler instead. */
+ void (*websocket_ready)(struct mg_connection *);
+
+ /* Called when data frame has been received from the client.
+ Parameters:
+ bits: first byte of the websocket frame, see websocket RFC at
+ http://tools.ietf.org/html/rfc6455, section 5.2
+ data, data_len: payload, with mask (if any) already applied.
+ Return value:
+ 1: keep this websocket connection open.
+ 0: close this websocket connection.
+ This callback is deprecated: Use mg_set_websocket_handler instead. */
+ int (*websocket_data)(struct mg_connection *,
+ int bits,
+ char *data,
+ size_t data_len);
+#endif /* MG_LEGACY_INTERFACE */
+
+ /* Called when civetweb is closing a connection. The per-context mutex is
+ locked when this is invoked.
+
+ Websockets:
+ Before mg_set_websocket_handler has been added, it was primarily useful
+ for noting when a websocket is closing, and used to remove it from any
+ application-maintained list of clients.
+ Using this callback for websocket connections is deprecated: Use
+ mg_set_websocket_handler instead.
+
+ Connection specific data:
+ If memory has been allocated for the connection specific user data
+ (mg_request_info->conn_data, mg_get_user_connection_data),
+ this is the last chance to free it.
+ */
+ void (*connection_close)(const struct mg_connection *);
+
+ /* Called when civetweb is about to serve Lua server page, if
+ Lua support is enabled.
+ Parameters:
+ conn: current connection.
+ lua_context: "lua_State *" pointer. */
+ void (*init_lua)(const struct mg_connection *conn, void *lua_context);
+
+#if defined(MG_LEGACY_INTERFACE) /* 2016-05-14 */
+ /* Called when civetweb has uploaded a file to a temporary directory as a
+ result of mg_upload() call.
+ Note that mg_upload is deprecated. Use mg_handle_form_request instead.
+ Parameters:
+ file_name: full path name to the uploaded file. */
+ void (*upload)(struct mg_connection *, const char *file_name);
+#endif
+
+ /* Called when civetweb is about to send HTTP error to the client.
+ Implementing this callback allows to create custom error pages.
+ Parameters:
+ conn: current connection.
+ status: HTTP error status code.
+ errmsg: error message text.
+ Return value:
+ 1: run civetweb error handler.
+ 0: callback already handled the error. */
+ int (*http_error)(struct mg_connection *conn,
+ int status,
+ const char *errmsg);
+
+ /* Called after civetweb context has been created, before requests
+ are processed.
+ Parameters:
+ ctx: context handle */
+ void (*init_context)(const struct mg_context *ctx);
+
+ /* Called when a new worker thread is initialized.
+ Parameters:
+ ctx: context handle
+ thread_type:
+ 0 indicates the master thread
+ 1 indicates a worker thread handling client connections
+ 2 indicates an internal helper thread (timer thread)
+ */
+ void (*init_thread)(const struct mg_context *ctx, int thread_type);
+
+ /* Called when civetweb context is deleted.
+ Parameters:
+ ctx: context handle */
+ void (*exit_context)(const struct mg_context *ctx);
+
+ /* Called when initializing a new connection object.
+ * Can be used to initialize the connection specific user data
+ * (mg_request_info->conn_data, mg_get_user_connection_data).
+ * When the callback is called, it is not yet known if a
+ * valid HTTP(S) request will be made.
+ * Parameters:
+ * conn: not yet fully initialized connection object
+ * conn_data: output parameter, set to initialize the
+ * connection specific user data
+ * Return value:
+ * must be 0
+ * Otherwise, the result is undefined
+ */
+ int (*init_connection)(const struct mg_connection *conn, void **conn_data);
+};
+
+
+/* Start web server.
+
+ Parameters:
+ callbacks: mg_callbacks structure with user-defined callbacks.
+ options: NULL terminated list of option_name, option_value pairs that
+ specify Civetweb configuration parameters.
+
+ Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
+ processing is required for these, signal handlers must be set up
+ after calling mg_start().
+
+
+ Example:
+ const char *options[] = {
+ "document_root", "/var/www",
+ "listening_ports", "80,443s",
+ NULL
+ };
+ struct mg_context *ctx = mg_start(&my_func, NULL, options);
+
+ Refer to https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md
+ for the list of valid option and their possible values.
+
+ Return:
+ web server context, or NULL on error. */
+CIVETWEB_API struct mg_context *mg_start(const struct mg_callbacks *callbacks,
+ void *user_data,
+ const char **configuration_options);
+
+
+/* Stop the web server.
+
+ Must be called last, when an application wants to stop the web server and
+ release all associated resources. This function blocks until all Civetweb
+ threads are stopped. Context pointer becomes invalid. */
+CIVETWEB_API void mg_stop(struct mg_context *);
+
+
+#if defined(MG_EXPERIMENTAL_INTERFACES)
+/* Add an additional domain to an already running web server.
+ *
+ * Parameters:
+ * ctx: Context handle of a server started by mg_start.
+ * options: NULL terminated list of option_name, option_value pairs that
+ * specify CivetWeb configuration parameters.
+ *
+ * Return:
+ * < 0 in case of an error
+ * -1 for a parameter error
+ * -2 invalid options
+ * -3 initializing SSL failed
+ * -4 mandatory domain option missing
+ * -5 duplicate domain
+ * -6 out of memory
+ * > 0 index / handle of a new domain
+ */
+CIVETWEB_API int mg_start_domain(struct mg_context *ctx,
+ const char **configuration_options);
+#endif
+
+
+/* mg_request_handler
+
+ Called when a new request comes in. This callback is URI based
+ and configured with mg_set_request_handler().
+
+ Parameters:
+ conn: current connection information.
+ cbdata: the callback data configured with mg_set_request_handler().
+ Returns:
+ 0: the handler could not handle the request, so fall through.
+ 1 - 999: the handler processed the request. The return code is
+ stored as a HTTP status code for the access log. */
+typedef int (*mg_request_handler)(struct mg_connection *conn, void *cbdata);
+
+
+/* mg_set_request_handler
+
+ Sets or removes a URI mapping for a request handler.
+ This function uses mg_lock_context internally.
+
+ URI's are ordered and prefixed URI's are supported. For example,
+ consider two URIs: /a/b and /a
+ /a matches /a
+ /a/b matches /a/b
+ /a/c matches /a
+
+ Parameters:
+ ctx: server context
+ uri: the URI (exact or pattern) for the handler
+ handler: the callback handler to use when the URI is requested.
+ If NULL, an already registered handler for this URI will
+ be removed.
+ The URI used to remove a handler must match exactly the
+ one used to register it (not only a pattern match).
+ cbdata: the callback data to give to the handler when it is called. */
+CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx,
+ const char *uri,
+ mg_request_handler handler,
+ void *cbdata);
+
+
+/* Callback types for websocket handlers in C/C++.
+
+ mg_websocket_connect_handler
+ Is called when the client intends to establish a websocket connection,
+ before websocket handshake.
+ Return value:
+ 0: civetweb proceeds with websocket handshake.
+ 1: connection is closed immediately.
+
+ mg_websocket_ready_handler
+ Is called when websocket handshake is successfully completed, and
+ connection is ready for data exchange.
+
+ mg_websocket_data_handler
+ Is called when a data frame has been received from the client.
+ Parameters:
+ bits: first byte of the websocket frame, see websocket RFC at
+ http://tools.ietf.org/html/rfc6455, section 5.2
+ data, data_len: payload, with mask (if any) already applied.
+ Return value:
+ 1: keep this websocket connection open.
+ 0: close this websocket connection.
+
+ mg_connection_close_handler
+ Is called, when the connection is closed.*/
+typedef int (*mg_websocket_connect_handler)(const struct mg_connection *,
+ void *);
+typedef void (*mg_websocket_ready_handler)(struct mg_connection *, void *);
+typedef int (*mg_websocket_data_handler)(struct mg_connection *,
+ int,
+ char *,
+ size_t,
+ void *);
+typedef void (*mg_websocket_close_handler)(const struct mg_connection *,
+ void *);
+
+/* struct mg_websocket_subprotocols
+ *
+ * List of accepted subprotocols
+ */
+struct mg_websocket_subprotocols {
+ int nb_subprotocols;
+ char **subprotocols;
+};
+
+/* mg_set_websocket_handler
+
+ Set or remove handler functions for websocket connections.
+ This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void
+mg_set_websocket_handler(struct mg_context *ctx,
+ const char *uri,
+ mg_websocket_connect_handler connect_handler,
+ mg_websocket_ready_handler ready_handler,
+ mg_websocket_data_handler data_handler,
+ mg_websocket_close_handler close_handler,
+ void *cbdata);
+
+/* mg_set_websocket_handler
+
+ Set or remove handler functions for websocket connections.
+ This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void mg_set_websocket_handler_with_subprotocols(
+ struct mg_context *ctx,
+ const char *uri,
+ struct mg_websocket_subprotocols *subprotocols,
+ mg_websocket_connect_handler connect_handler,
+ mg_websocket_ready_handler ready_handler,
+ mg_websocket_data_handler data_handler,
+ mg_websocket_close_handler close_handler,
+ void *cbdata);
+
+
+/* mg_authorization_handler
+
+ Callback function definition for mg_set_auth_handler
+
+ Parameters:
+ conn: current connection information.
+ cbdata: the callback data configured with mg_set_request_handler().
+ Returns:
+ 0: access denied
+ 1: access granted
+ */
+typedef int (*mg_authorization_handler)(struct mg_connection *conn,
+ void *cbdata);
+
+
+/* mg_set_auth_handler
+
+ Sets or removes a URI mapping for an authorization handler.
+ This function works similar to mg_set_request_handler - see there. */
+CIVETWEB_API void mg_set_auth_handler(struct mg_context *ctx,
+ const char *uri,
+ mg_authorization_handler handler,
+ void *cbdata);
+
+
+/* Get the value of particular configuration parameter.
+ The value returned is read-only. Civetweb does not allow changing
+ configuration at run time.
+ If given parameter name is not valid, NULL is returned. For valid
+ names, return value is guaranteed to be non-NULL. If parameter is not
+ set, zero-length string is returned. */
+CIVETWEB_API const char *mg_get_option(const struct mg_context *ctx,
+ const char *name);
+
+
+/* Get context from connection. */
+CIVETWEB_API struct mg_context *
+mg_get_context(const struct mg_connection *conn);
+
+
+/* Get user data passed to mg_start from context. */
+CIVETWEB_API void *mg_get_user_data(const struct mg_context *ctx);
+
+
+/* Set user data for the current connection. */
+/* Note: This function is deprecated. Use the init_connection callback
+ instead to initialize the user connection data pointer. It is
+ reccomended to supply a pointer to some user defined data structure
+ as conn_data initializer in init_connection. In case it is required
+ to change some data after the init_connection call, store another
+ data pointer in the user defined data structure and modify that
+ pointer. In either case, after the init_connection callback, only
+ calls to mg_get_user_connection_data should be required. */
+CIVETWEB_API void mg_set_user_connection_data(struct mg_connection *conn,
+ void *data);
+
+
+/* Get user data set for the current connection. */
+CIVETWEB_API void *
+mg_get_user_connection_data(const struct mg_connection *conn);
+
+
+/* Get a formatted link corresponding to the current request
+
+ Parameters:
+ conn: current connection information.
+ buf: string buffer (out)
+ buflen: length of the string buffer
+ Returns:
+ <0: error
+ >=0: ok */
+CIVETWEB_API int
+mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen);
+
+
+#if defined(MG_LEGACY_INTERFACE) /* 2014-02-21 */
+/* Return array of strings that represent valid configuration options.
+ For each option, option name and default value is returned, i.e. the
+ number of entries in the array equals to number_of_options x 2.
+ Array is NULL terminated. */
+/* Deprecated: Use mg_get_valid_options instead. */
+CIVETWEB_API const char **mg_get_valid_option_names(void);
+#endif
+
+
+struct mg_option {
+ const char *name;
+ int type;
+ const char *default_value;
+};
+
+/* Old nomenclature */
+#if defined(MG_LEGACY_INTERFACE) /* 2017-10-05 */
+enum {
+ CONFIG_TYPE_UNKNOWN = 0x0,
+ CONFIG_TYPE_NUMBER = 0x1,
+ CONFIG_TYPE_STRING = 0x2,
+ CONFIG_TYPE_FILE = 0x3,
+ CONFIG_TYPE_DIRECTORY = 0x4,
+ CONFIG_TYPE_BOOLEAN = 0x5,
+ CONFIG_TYPE_EXT_PATTERN = 0x6,
+ CONFIG_TYPE_STRING_LIST = 0x7,
+ CONFIG_TYPE_STRING_MULTILINE = 0x8
+};
+#endif
+
+/* New nomenclature */
+enum {
+ MG_CONFIG_TYPE_UNKNOWN = 0x0,
+ MG_CONFIG_TYPE_NUMBER = 0x1,
+ MG_CONFIG_TYPE_STRING = 0x2,
+ MG_CONFIG_TYPE_FILE = 0x3,
+ MG_CONFIG_TYPE_DIRECTORY = 0x4,
+ MG_CONFIG_TYPE_BOOLEAN = 0x5,
+ MG_CONFIG_TYPE_EXT_PATTERN = 0x6,
+ MG_CONFIG_TYPE_STRING_LIST = 0x7,
+ MG_CONFIG_TYPE_STRING_MULTILINE = 0x8,
+ MG_CONFIG_TYPE_YES_NO_OPTIONAL = 0x9
+};
+
+/* Return array of struct mg_option, representing all valid configuration
+ options of civetweb.c.
+ The array is terminated by a NULL name option. */
+CIVETWEB_API const struct mg_option *mg_get_valid_options(void);
+
+
+struct mg_server_ports {
+ int protocol; /* 1 = IPv4, 2 = IPv6, 3 = both */
+ int port; /* port number */
+ int is_ssl; /* https port: 0 = no, 1 = yes */
+ int is_redirect; /* redirect all requests: 0 = no, 1 = yes */
+ int _reserved1;
+ int _reserved2;
+ int _reserved3;
+ int _reserved4;
+};
+
+
+/* Get the list of ports that civetweb is listening on.
+ The parameter size is the size of the ports array in elements.
+ The caller is responsibility to allocate the required memory.
+ This function returns the number of struct mg_server_ports elements
+ filled in, or <0 in case of an error. */
+CIVETWEB_API int mg_get_server_ports(const struct mg_context *ctx,
+ int size,
+ struct mg_server_ports *ports);
+
+
+#if defined(MG_LEGACY_INTERFACE) /* 2017-04-02 */
+/* Deprecated: Use mg_get_server_ports instead. */
+CIVETWEB_API size_t
+mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl);
+#endif
+
+
+/* Add, edit or delete the entry in the passwords file.
+ *
+ * This function allows an application to manipulate .htpasswd files on the
+ * fly by adding, deleting and changing user records. This is one of the
+ * several ways of implementing authentication on the server side. For another,
+ * cookie-based way please refer to the examples/chat in the source tree.
+ *
+ * Parameter:
+ * passwords_file_name: Path and name of a file storing multiple passwords
+ * realm: HTTP authentication realm (authentication domain) name
+ * user: User name
+ * password:
+ * If password is not NULL, entry modified or added.
+ * If password is NULL, entry is deleted.
+ *
+ * Return:
+ * 1 on success, 0 on error.
+ */
+CIVETWEB_API int mg_modify_passwords_file(const char *passwords_file_name,
+ const char *realm,
+ const char *user,
+ const char *password);
+
+
+/* Return information associated with the request.
+ * Use this function to implement a server and get data about a request
+ * from a HTTP/HTTPS client.
+ * Note: Before CivetWeb 1.10, this function could be used to read
+ * a response from a server, when implementing a client, although the
+ * values were never returned in appropriate mg_request_info elements.
+ * It is strongly advised to use mg_get_response_info for clients.
+ */
+CIVETWEB_API const struct mg_request_info *
+mg_get_request_info(const struct mg_connection *);
+
+
+/* Return information associated with a HTTP/HTTPS response.
+ * Use this function in a client, to check the response from
+ * the server. */
+CIVETWEB_API const struct mg_response_info *
+mg_get_response_info(const struct mg_connection *);
+
+
+/* Send data to the client.
+ Return:
+ 0 when the connection has been closed
+ -1 on error
+ >0 number of bytes written on success */
+CIVETWEB_API int mg_write(struct mg_connection *, const void *buf, size_t len);
+
+
+/* Send data to a websocket client wrapped in a websocket frame. Uses
+ mg_lock_connection to ensure that the transmission is not interrupted,
+ i.e., when the application is proactively communicating and responding to
+ a request simultaneously.
+
+ Send data to a websocket client wrapped in a websocket frame.
+ This function is available when civetweb is compiled with -DUSE_WEBSOCKET
+
+ Return:
+ 0 when the connection has been closed
+ -1 on error
+ >0 number of bytes written on success */
+CIVETWEB_API int mg_websocket_write(struct mg_connection *conn,
+ int opcode,
+ const char *data,
+ size_t data_len);
+
+
+/* Send data to a websocket server wrapped in a masked websocket frame. Uses
+ mg_lock_connection to ensure that the transmission is not interrupted,
+ i.e., when the application is proactively communicating and responding to
+ a request simultaneously.
+
+ Send data to a websocket server wrapped in a masked websocket frame.
+ This function is available when civetweb is compiled with -DUSE_WEBSOCKET
+
+ Return:
+ 0 when the connection has been closed
+ -1 on error
+ >0 number of bytes written on success */
+CIVETWEB_API int mg_websocket_client_write(struct mg_connection *conn,
+ int opcode,
+ const char *data,
+ size_t data_len);
+
+
+/* Blocks until unique access is obtained to this connection. Intended for use
+ with websockets only.
+ Invoke this before mg_write or mg_printf when communicating with a
+ websocket if your code has server-initiated communication as well as
+ communication in direct response to a message. */
+CIVETWEB_API void mg_lock_connection(struct mg_connection *conn);
+CIVETWEB_API void mg_unlock_connection(struct mg_connection *conn);
+
+
+#if defined(MG_LEGACY_INTERFACE) /* 2014-06-21 */
+#define mg_lock mg_lock_connection
+#define mg_unlock mg_unlock_connection
+#endif
+
+
+/* Lock server context. This lock may be used to protect resources
+ that are shared between different connection/worker threads. */
+CIVETWEB_API void mg_lock_context(struct mg_context *ctx);
+CIVETWEB_API void mg_unlock_context(struct mg_context *ctx);
+
+
+/* Opcodes, from http://tools.ietf.org/html/rfc6455 */
+#if defined(MG_LEGACY_INTERFACE) /* 2017-10-05 */
+enum {
+ WEBSOCKET_OPCODE_CONTINUATION = 0x0,
+ WEBSOCKET_OPCODE_TEXT = 0x1,
+ WEBSOCKET_OPCODE_BINARY = 0x2,
+ WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
+ WEBSOCKET_OPCODE_PING = 0x9,
+ WEBSOCKET_OPCODE_PONG = 0xa
+};
+#endif
+
+/* New nomenclature */
+enum {
+ MG_WEBSOCKET_OPCODE_CONTINUATION = 0x0,
+ MG_WEBSOCKET_OPCODE_TEXT = 0x1,
+ MG_WEBSOCKET_OPCODE_BINARY = 0x2,
+ MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
+ MG_WEBSOCKET_OPCODE_PING = 0x9,
+ MG_WEBSOCKET_OPCODE_PONG = 0xa
+};
+
+/* Macros for enabling compiler-specific checks for printf-like arguments. */
+#undef PRINTF_FORMAT_STRING
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#include <sal.h>
+#if defined(_MSC_VER) && _MSC_VER > 1400
+#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
+#else
+#define PRINTF_FORMAT_STRING(s) __format_string s
+#endif
+#else
+#define PRINTF_FORMAT_STRING(s) s
+#endif
+
+#ifdef __GNUC__
+#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
+#else
+#define PRINTF_ARGS(x, y)
+#endif
+
+
+/* Send data to the client using printf() semantics.
+ Works exactly like mg_write(), but allows to do message formatting. */
+CIVETWEB_API int mg_printf(struct mg_connection *,
+ PRINTF_FORMAT_STRING(const char *fmt),
+ ...) PRINTF_ARGS(2, 3);
+
+
+/* Send a part of the message body, if chunked transfer encoding is set.
+ * Only use this function after sending a complete HTTP request or response
+ * header with "Transfer-Encoding: chunked" set. */
+CIVETWEB_API int mg_send_chunk(struct mg_connection *conn,
+ const char *chunk,
+ unsigned int chunk_len);
+
+
+/* Send contents of the entire file together with HTTP headers.
+ * Parameters:
+ * conn: Current connection information.
+ * path: Full path to the file to send.
+ * This function has been superseded by mg_send_mime_file
+ */
+CIVETWEB_API void mg_send_file(struct mg_connection *conn, const char *path);
+
+
+/* Send contents of the file without HTTP headers.
+ * The code must send a valid HTTP response header before using this function.
+ *
+ * Parameters:
+ * conn: Current connection information.
+ * path: Full path to the file to send.
+ *
+ * Return:
+ * < 0 Error
+*/
+CIVETWEB_API int mg_send_file_body(struct mg_connection *conn,
+ const char *path);
+
+
+/* Send HTTP error reply. */
+CIVETWEB_API int mg_send_http_error(struct mg_connection *conn,
+ int status_code,
+ PRINTF_FORMAT_STRING(const char *fmt),
+ ...) PRINTF_ARGS(3, 4);
+
+
+/* Send "HTTP 200 OK" response header.
+ * After calling this function, use mg_write or mg_send_chunk to send the
+ * response body.
+ * Parameters:
+ * conn: Current connection handle.
+ * mime_type: Set Content-Type for the following content.
+ * content_length: Size of the following content, if content_length >= 0.
+ * Will set transfer-encoding to chunked, if set to -1.
+ * Return:
+ * < 0 Error
+ */
+CIVETWEB_API int mg_send_http_ok(struct mg_connection *conn,
+ const char *mime_type,
+ long long content_length);
+
+
+/* Send "HTTP 30x" redirect response.
+ * The response has content-size zero: do not send any body data after calling
+ * this function.
+ * Parameters:
+ * conn: Current connection handle.
+ * target_url: New location.
+ * redirect_code: HTTP redirect type. Could be 301, 302, 303, 307, 308.
+ * Return:
+ * < 0 Error (-1 send error, -2 parameter error)
+ */
+CIVETWEB_API int mg_send_http_redirect(struct mg_connection *conn,
+ const char *target_url,
+ int redirect_code);
+
+
+/* Send HTTP digest access authentication request.
+ * Browsers will send a user name and password in their next request, showing
+ * an authentication dialog if the password is not stored.
+ * Parameters:
+ * conn: Current connection handle.
+ * realm: Authentication realm. If NULL is supplied, the sever domain
+ * set in the authentication_domain configuration is used.
+ * Return:
+ * < 0 Error
+ */
+CIVETWEB_API int
+mg_send_digest_access_authentication_request(struct mg_connection *conn,
+ const char *realm);
+
+
+/* Check if the current request has a valid authentication token set.
+ * A file is used to provide a list of valid user names, realms and
+ * password hashes. The file can be created and modified using the
+ * mg_modify_passwords_file API function.
+ * Parameters:
+ * conn: Current connection handle.
+ * realm: Authentication realm. If NULL is supplied, the sever domain
+ * set in the authentication_domain configuration is used.
+ * filename: Path and name of a file storing multiple password hashes.
+ * Return:
+ * > 0 Valid authentication
+ * 0 Invalid authentication
+ * < 0 Error (all values < 0 should be considered as invalid
+ * authentication, future error codes will have negative
+ * numbers)
+ * -1 Parameter error
+ * -2 File not found
+ */
+CIVETWEB_API int
+mg_check_digest_access_authentication(struct mg_connection *conn,
+ const char *realm,
+ const char *filename);
+
+
+/* Send contents of the entire file together with HTTP headers.
+ * Parameters:
+ * conn: Current connection handle.
+ * path: Full path to the file to send.
+ * mime_type: Content-Type for file. NULL will cause the type to be
+ * looked up by the file extension.
+ */
+CIVETWEB_API void mg_send_mime_file(struct mg_connection *conn,
+ const char *path,
+ const char *mime_type);
+
+
+/* Send contents of the entire file together with HTTP headers.
+ Parameters:
+ conn: Current connection information.
+ path: Full path to the file to send.
+ mime_type: Content-Type for file. NULL will cause the type to be
+ looked up by the file extension.
+ additional_headers: Additional custom header fields appended to the header.
+ Each header should start with an X-, to ensure it is
+ not included twice.
+ NULL does not append anything.
+*/
+CIVETWEB_API void mg_send_mime_file2(struct mg_connection *conn,
+ const char *path,
+ const char *mime_type,
+ const char *additional_headers);
+
+
+/* Store body data into a file. */
+CIVETWEB_API long long mg_store_body(struct mg_connection *conn,
+ const char *path);
+/* Read entire request body and store it in a file "path".
+ Return:
+ < 0 Error
+ >= 0 Number of bytes stored in file "path".
+*/
+
+
+/* Read data from the remote end, return number of bytes read.
+ Return:
+ 0 connection has been closed by peer. No more data could be read.
+ < 0 read error. No more data could be read from the connection.
+ > 0 number of bytes read into the buffer. */
+CIVETWEB_API int mg_read(struct mg_connection *, void *buf, size_t len);
+
+
+/* Get the value of particular HTTP header.
+
+ This is a helper function. It traverses request_info->http_headers array,
+ and if the header is present in the array, returns its value. If it is
+ not present, NULL is returned. */
+CIVETWEB_API const char *mg_get_header(const struct mg_connection *,
+ const char *name);
+
+
+/* Get a value of particular form variable.
+
+ Parameters:
+ data: pointer to form-uri-encoded buffer. This could be either POST data,
+ or request_info.query_string.
+ data_len: length of the encoded data.
+ var_name: variable name to decode from the buffer
+ dst: destination buffer for the decoded variable
+ dst_len: length of the destination buffer
+
+ Return:
+ On success, length of the decoded variable.
+ On error:
+ -1 (variable not found).
+ -2 (destination buffer is NULL, zero length or too small to hold the
+ decoded variable).
+
+ Destination buffer is guaranteed to be '\0' - terminated if it is not
+ NULL or zero length. */
+CIVETWEB_API int mg_get_var(const char *data,
+ size_t data_len,
+ const char *var_name,
+ char *dst,
+ size_t dst_len);
+
+
+/* Get a value of particular form variable.
+
+ Parameters:
+ data: pointer to form-uri-encoded buffer. This could be either POST data,
+ or request_info.query_string.
+ data_len: length of the encoded data.
+ var_name: variable name to decode from the buffer
+ dst: destination buffer for the decoded variable
+ dst_len: length of the destination buffer
+ occurrence: which occurrence of the variable, 0 is the first, 1 the
+ second...
+ this makes it possible to parse a query like
+ b=x&a=y&a=z which will have occurrence values b:0, a:0 and a:1
+
+ Return:
+ On success, length of the decoded variable.
+ On error:
+ -1 (variable not found).
+ -2 (destination buffer is NULL, zero length or too small to hold the
+ decoded variable).
+
+ Destination buffer is guaranteed to be '\0' - terminated if it is not
+ NULL or zero length. */
+CIVETWEB_API int mg_get_var2(const char *data,
+ size_t data_len,
+ const char *var_name,
+ char *dst,
+ size_t dst_len,
+ size_t occurrence);
+
+
+/* Fetch value of certain cookie variable into the destination buffer.
+
+ Destination buffer is guaranteed to be '\0' - terminated. In case of
+ failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
+ parameter. This function returns only first occurrence.
+
+ Return:
+ On success, value length.
+ On error:
+ -1 (either "Cookie:" header is not present at all or the requested
+ parameter is not found).
+ -2 (destination buffer is NULL, zero length or too small to hold the
+ value). */
+CIVETWEB_API int mg_get_cookie(const char *cookie,
+ const char *var_name,
+ char *buf,
+ size_t buf_len);
+
+
+/* Download data from the remote web server.
+ host: host name to connect to, e.g. "foo.com", or "10.12.40.1".
+ port: port number, e.g. 80.
+ use_ssl: whether to use SSL connection.
+ error_buffer, error_buffer_size: error message placeholder.
+ request_fmt,...: HTTP request.
+ Return:
+ On success, valid pointer to the new connection, suitable for mg_read().
+ On error, NULL. error_buffer contains error message.
+ Example:
+ char ebuf[100];
+ struct mg_connection *conn;
+ conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
+ "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
+ */
+CIVETWEB_API struct mg_connection *
+mg_download(const char *host,
+ int port,
+ int use_ssl,
+ char *error_buffer,
+ size_t error_buffer_size,
+ PRINTF_FORMAT_STRING(const char *request_fmt),
+ ...) PRINTF_ARGS(6, 7);
+
+
+/* Close the connection opened by mg_download(). */
+CIVETWEB_API void mg_close_connection(struct mg_connection *conn);
+
+
+#if defined(MG_LEGACY_INTERFACE) /* 2016-05-14 */
+/* File upload functionality. Each uploaded file gets saved into a temporary
+ file and MG_UPLOAD event is sent.
+ Return number of uploaded files.
+ Deprecated: Use mg_handle_form_request instead. */
+CIVETWEB_API int mg_upload(struct mg_connection *conn,
+ const char *destination_dir);
+#endif
+
+
+/* This structure contains callback functions for handling form fields.
+ It is used as an argument to mg_handle_form_request. */
+struct mg_form_data_handler {
+ /* This callback function is called, if a new field has been found.
+ * The return value of this callback is used to define how the field
+ * should be processed.
+ *
+ * Parameters:
+ * key: Name of the field ("name" property of the HTML input field).
+ * filename: Name of a file to upload, at the client computer.
+ * Only set for input fields of type "file", otherwise NULL.
+ * path: Output parameter: File name (incl. path) to store the file
+ * at the server computer. Only used if FORM_FIELD_STORAGE_STORE
+ * is returned by this callback. Existing files will be
+ * overwritten.
+ * pathlen: Length of the buffer for path.
+ * user_data: Value of the member user_data of mg_form_data_handler
+ *
+ * Return value:
+ * The callback must return the intended storage for this field
+ * (See FORM_FIELD_STORAGE_*).
+ */
+ int (*field_found)(const char *key,
+ const char *filename,
+ char *path,
+ size_t pathlen,
+ void *user_data);
+
+ /* If the "field_found" callback returned FORM_FIELD_STORAGE_GET,
+ * this callback will receive the field data.
+ *
+ * Parameters:
+ * key: Name of the field ("name" property of the HTML input field).
+ * value: Value of the input field.
+ * user_data: Value of the member user_data of mg_form_data_handler
+ *
+ * Return value:
+ * TODO: Needs to be defined.
+ */
+ int (*field_get)(const char *key,
+ const char *value,
+ size_t valuelen,
+ void *user_data);
+
+ /* If the "field_found" callback returned FORM_FIELD_STORAGE_STORE,
+ * the data will be stored into a file. If the file has been written
+ * successfully, this callback will be called. This callback will
+ * not be called for only partially uploaded files. The
+ * mg_handle_form_request function will either store the file completely
+ * and call this callback, or it will remove any partial content and
+ * not call this callback function.
+ *
+ * Parameters:
+ * path: Path of the file stored at the server.
+ * file_size: Size of the stored file in bytes.
+ * user_data: Value of the member user_data of mg_form_data_handler
+ *
+ * Return value:
+ * TODO: Needs to be defined.
+ */
+ int (*field_store)(const char *path, long long file_size, void *user_data);
+
+ /* User supplied argument, passed to all callback functions. */
+ void *user_data;
+};
+
+
+/* Return values definition for the "field_found" callback in
+ * mg_form_data_handler. */
+#if defined(MG_LEGACY_INTERFACE) /* 2017-10-05 */
+enum {
+ /* Skip this field (neither get nor store it). Continue with the
+ * next field. */
+ FORM_FIELD_STORAGE_SKIP = 0x0,
+ /* Get the field value. */
+ FORM_FIELD_STORAGE_GET = 0x1,
+ /* Store the field value into a file. */
+ FORM_FIELD_STORAGE_STORE = 0x2,
+ /* Stop parsing this request. Skip the remaining fields. */
+ FORM_FIELD_STORAGE_ABORT = 0x10
+};
+#endif
+/* New nomenclature */
+enum {
+ /* Skip this field (neither get nor store it). Continue with the
+ * next field. */
+ MG_FORM_FIELD_STORAGE_SKIP = 0x0,
+ /* Get the field value. */
+ MG_FORM_FIELD_STORAGE_GET = 0x1,
+ /* Store the field value into a file. */
+ MG_FORM_FIELD_STORAGE_STORE = 0x2,
+ /* Stop parsing this request. Skip the remaining fields. */
+ MG_FORM_FIELD_STORAGE_ABORT = 0x10
+};
+
+/* Process form data.
+ * Returns the number of fields handled, or < 0 in case of an error.
+ * Note: It is possible that several fields are already handled successfully
+ * (e.g., stored into files), before the request handling is stopped with an
+ * error. In this case a number < 0 is returned as well.
+ * In any case, it is the duty of the caller to remove files once they are
+ * no longer required. */
+CIVETWEB_API int mg_handle_form_request(struct mg_connection *conn,
+ struct mg_form_data_handler *fdh);
+
+
+/* Convenience function -- create detached thread.
+ Return: 0 on success, non-0 on error. */
+typedef void *(*mg_thread_func_t)(void *);
+CIVETWEB_API int mg_start_thread(mg_thread_func_t f, void *p);
+
+
+/* Return builtin mime type for the given file name.
+ For unrecognized extensions, "text/plain" is returned. */
+CIVETWEB_API const char *mg_get_builtin_mime_type(const char *file_name);
+
+
+/* Get text representation of HTTP status code. */
+CIVETWEB_API const char *
+mg_get_response_code_text(const struct mg_connection *conn, int response_code);
+
+
+/* Return CivetWeb version. */
+CIVETWEB_API const char *mg_version(void);
+
+
+/* URL-decode input buffer into destination buffer.
+ 0-terminate the destination buffer.
+ 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
+ Return: length of the decoded data, or -1 if dst buffer is too small. */
+CIVETWEB_API int mg_url_decode(const char *src,
+ int src_len,
+ char *dst,
+ int dst_len,
+ int is_form_url_encoded);
+
+
+/* URL-encode input buffer into destination buffer.
+ returns the length of the resulting buffer or -1
+ is the buffer is too small. */
+CIVETWEB_API int mg_url_encode(const char *src, char *dst, size_t dst_len);
+
+
+/* MD5 hash given strings.
+ Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
+ ASCIIz strings. When function returns, buf will contain human-readable
+ MD5 hash. Example:
+ char buf[33];
+ mg_md5(buf, "aa", "bb", NULL); */
+CIVETWEB_API char *mg_md5(char buf[33], ...);
+
+
+/* Print error message to the opened error log stream.
+ This utilizes the provided logging configuration.
+ conn: connection (not used for sending data, but to get perameters)
+ fmt: format string without the line return
+ ...: variable argument list
+ Example:
+ mg_cry(conn,"i like %s", "logging"); */
+CIVETWEB_API void mg_cry(const struct mg_connection *conn,
+ PRINTF_FORMAT_STRING(const char *fmt),
+ ...) PRINTF_ARGS(2, 3);
+
+
+/* utility methods to compare two buffers, case insensitive. */
+CIVETWEB_API int mg_strcasecmp(const char *s1, const char *s2);
+CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len);
+
+
+/* Connect to a websocket as a client
+ Parameters:
+ host: host to connect to, i.e. "echo.websocket.org" or "192.168.1.1" or
+ "localhost"
+ port: server port
+ use_ssl: make a secure connection to server
+ error_buffer, error_buffer_size: buffer for an error message
+ path: server path you are trying to connect to, i.e. if connection to
+ localhost/app, path should be "/app"
+ origin: value of the Origin HTTP header
+ data_func: callback that should be used when data is received from the
+ server
+ user_data: user supplied argument
+
+ Return:
+ On success, valid mg_connection object.
+ On error, NULL. Se error_buffer for details.
+*/
+CIVETWEB_API struct mg_connection *
+mg_connect_websocket_client(const char *host,
+ int port,
+ int use_ssl,
+ char *error_buffer,
+ size_t error_buffer_size,
+ const char *path,
+ const char *origin,
+ mg_websocket_data_handler data_func,
+ mg_websocket_close_handler close_func,
+ void *user_data);
+
+
+/* Connect to a TCP server as a client (can be used to connect to a HTTP server)
+ Parameters:
+ host: host to connect to, i.e. "www.wikipedia.org" or "192.168.1.1" or
+ "localhost"
+ port: server port
+ use_ssl: make a secure connection to server
+ error_buffer, error_buffer_size: buffer for an error message
+
+ Return:
+ On success, valid mg_connection object.
+ On error, NULL. Se error_buffer for details.
+*/
+CIVETWEB_API struct mg_connection *mg_connect_client(const char *host,
+ int port,
+ int use_ssl,
+ char *error_buffer,
+ size_t error_buffer_size);
+
+
+struct mg_client_options {
+ const char *host;
+ int port;
+ const char *client_cert;
+ const char *server_cert;
+ /* TODO: add more data */
+};
+
+
+CIVETWEB_API struct mg_connection *
+mg_connect_client_secure(const struct mg_client_options *client_options,
+ char *error_buffer,
+ size_t error_buffer_size);
+
+
+enum { TIMEOUT_INFINITE = -1 };
+enum { MG_TIMEOUT_INFINITE = -1 };
+
+/* Wait for a response from the server
+ Parameters:
+ conn: connection
+ ebuf, ebuf_len: error message placeholder.
+ timeout: time to wait for a response in milliseconds (if < 0 then wait
+ forever)
+
+ Return:
+ On success, >= 0
+ On error/timeout, < 0
+*/
+CIVETWEB_API int mg_get_response(struct mg_connection *conn,
+ char *ebuf,
+ size_t ebuf_len,
+ int timeout);
+
+
+/* Check which features where set when the civetweb library has been compiled.
+ The function explicitly addresses compile time defines used when building
+ the library - it does not mean, the feature has been initialized using a
+ mg_init_library call.
+ mg_check_feature can be called anytime, even before mg_init_library has
+ been called.
+
+ Parameters:
+ feature: specifies which feature should be checked
+ The value is a bit mask. The individual bits are defined as:
+ 1 serve files (NO_FILES not set)
+ 2 support HTTPS (NO_SSL not set)
+ 4 support CGI (NO_CGI not set)
+ 8 support IPv6 (USE_IPV6 set)
+ 16 support WebSocket (USE_WEBSOCKET set)
+ 32 support Lua scripts and Lua server pages (USE_LUA is set)
+ 64 support server side JavaScript (USE_DUKTAPE is set)
+ 128 support caching (NO_CACHING not set)
+ 256 support server statistics (USE_SERVER_STATS is set)
+ The result is undefined, if bits are set that do not represent a
+ defined feature (currently: feature >= 512).
+ The result is undefined, if no bit is set (feature == 0).
+
+ Return:
+ If feature is available, the corresponding bit is set
+ If feature is not available, the bit is 0
+*/
+CIVETWEB_API unsigned mg_check_feature(unsigned feature);
+
+
+/* Get information on the system. Useful for support requests.
+ Parameters:
+ buffer: Store system information as string here.
+ buflen: Length of buffer (including a byte required for a terminating 0).
+ Return:
+ Available size of system information, exluding a terminating 0.
+ The information is complete, if the return value is smaller than buflen.
+ The result is a JSON formatted string, the exact content may vary.
+ Note:
+ It is possible to determine the required buflen, by first calling this
+ function with buffer = NULL and buflen = NULL. The required buflen is
+ one byte more than the returned value.
+*/
+CIVETWEB_API int mg_get_system_info(char *buffer, int buflen);
+
+
+/* Get context information. Useful for server diagnosis.
+ Parameters:
+ ctx: Context handle
+ buffer: Store context information here.
+ buflen: Length of buffer (including a byte required for a terminating 0).
+ Return:
+ Available size of system information, exluding a terminating 0.
+ The information is complete, if the return value is smaller than buflen.
+ The result is a JSON formatted string, the exact content may vary.
+ Note:
+ It is possible to determine the required buflen, by first calling this
+ function with buffer = NULL and buflen = NULL. The required buflen is
+ one byte more than the returned value. However, since the available
+ context information changes, you should allocate a few bytes more.
+*/
+CIVETWEB_API int
+mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen);
+
+
+#ifdef MG_EXPERIMENTAL_INTERFACES
+/* Get connection information. Useful for server diagnosis.
+ Parameters:
+ ctx: Context handle
+ idx: Connection index
+ buffer: Store context information here.
+ buflen: Length of buffer (including a byte required for a terminating 0).
+ Return:
+ Available size of system information, exluding a terminating 0.
+ The information is complete, if the return value is smaller than buflen.
+ The result is a JSON formatted string, the exact content may vary.
+ Note:
+ It is possible to determine the required buflen, by first calling this
+ function with buffer = NULL and buflen = NULL. The required buflen is
+ one byte more than the returned value. However, since the available
+ context information changes, you should allocate a few bytes more.
+*/
+CIVETWEB_API int mg_get_connection_info(const struct mg_context *ctx,
+ int idx,
+ char *buffer,
+ int buflen);
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* CIVETWEB_HEADER_INCLUDED */
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/civetweb/handle_form.inl
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/civetweb/handle_form.inl b/examples/celix-examples/civetweb/civetweb/handle_form.inl
new file mode 100644
index 0000000..e5511d5
--- /dev/null
+++ b/examples/celix-examples/civetweb/civetweb/handle_form.inl
@@ -0,0 +1,981 @@
+/* Copyright (c) 2016-2018 the Civetweb developers
+ *
+ * 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.
+ */
+
+
+static int
+url_encoded_field_found(const struct mg_connection *conn,
+ const char *key,
+ size_t key_len,
+ const char *filename,
+ size_t filename_len,
+ char *path,
+ size_t path_len,
+ struct mg_form_data_handler *fdh)
+{
+ char key_dec[1024];
+ char filename_dec[1024];
+ int key_dec_len;
+ int filename_dec_len;
+ int ret;
+
+ key_dec_len =
+ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+
+ if (((size_t)key_dec_len >= (size_t)sizeof(key_dec)) || (key_dec_len < 0)) {
+ return MG_FORM_FIELD_STORAGE_SKIP;
+ }
+
+ if (filename) {
+ filename_dec_len = mg_url_decode(filename,
+ (int)filename_len,
+ filename_dec,
+ (int)sizeof(filename_dec),
+ 1);
+
+ if (((size_t)filename_dec_len >= (size_t)sizeof(filename_dec))
+ || (filename_dec_len < 0)) {
+ /* Log error message and skip this field. */
+ mg_cry_internal(conn, "%s: Cannot decode filename", __func__);
+ return MG_FORM_FIELD_STORAGE_SKIP;
+ }
+ } else {
+ filename_dec[0] = 0;
+ }
+
+ ret =
+ fdh->field_found(key_dec, filename_dec, path, path_len, fdh->user_data);
+
+ if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_GET) {
+ if (fdh->field_get == NULL) {
+ mg_cry_internal(conn,
+ "%s: Function \"Get\" not available",
+ __func__);
+ return MG_FORM_FIELD_STORAGE_SKIP;
+ }
+ }
+ if ((ret & 0xF) == MG_FORM_FIELD_STORAGE_STORE) {
+ if (fdh->field_store == NULL) {
+ mg_cry_internal(conn,
+ "%s: Function \"Store\" not available",
+ __func__);
+ return MG_FORM_FIELD_STORAGE_SKIP;
+ }
+ }
+
+ return ret;
+}
+
+
+static int
+url_encoded_field_get(const struct mg_connection *conn,
+ const char *key,
+ size_t key_len,
+ const char *value,
+ size_t value_len,
+ struct mg_form_data_handler *fdh)
+{
+ char key_dec[1024];
+
+ char *value_dec = (char *)mg_malloc_ctx(value_len + 1, conn->phys_ctx);
+ int value_dec_len, ret;
+
+ if (!value_dec) {
+ /* Log error message and stop parsing the form data. */
+ mg_cry_internal(conn,
+ "%s: Not enough memory (required: %lu)",
+ __func__,
+ (unsigned long)(value_len + 1));
+ return MG_FORM_FIELD_STORAGE_ABORT;
+ }
+
+ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+
+ value_dec_len =
+ mg_url_decode(value, (int)value_len, value_dec, (int)value_len + 1, 1);
+
+ ret = fdh->field_get(key_dec,
+ value_dec,
+ (size_t)value_dec_len,
+ fdh->user_data);
+
+ mg_free(value_dec);
+
+ return ret;
+}
+
+
+static int
+unencoded_field_get(const struct mg_connection *conn,
+ const char *key,
+ size_t key_len,
+ const char *value,
+ size_t value_len,
+ struct mg_form_data_handler *fdh)
+{
+ char key_dec[1024];
+ (void)conn;
+
+ mg_url_decode(key, (int)key_len, key_dec, (int)sizeof(key_dec), 1);
+
+ return fdh->field_get(key_dec, value, value_len, fdh->user_data);
+}
+
+
+static int
+field_stored(const struct mg_connection *conn,
+ const char *path,
+ long long file_size,
+ struct mg_form_data_handler *fdh)
+{
+ /* Equivalent to "upload" callback of "mg_upload". */
+
+ (void)conn; /* we do not need mg_cry here, so conn is currently unused */
+
+ return fdh->field_store(path, file_size, fdh->user_data);
+}
+
+
+static const char *
+search_boundary(const char *buf,
+ size_t buf_len,
+ const char *boundary,
+ size_t boundary_len)
+{
+ /* We must do a binary search here, not a string search, since the buffer
+ * may contain '\x00' bytes, if binary data is transferred. */
+ int clen = (int)buf_len - (int)boundary_len - 4;
+ int i;
+
+ for (i = 0; i <= clen; i++) {
+ if (!memcmp(buf + i, "\r\n--", 4)) {
+ if (!memcmp(buf + i + 4, boundary, boundary_len)) {
+ return buf + i;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+int
+mg_handle_form_request(struct mg_connection *conn,
+ struct mg_form_data_handler *fdh)
+{
+ const char *content_type;
+ char path[512];
+ char buf[1024]; /* Must not be smaller than ~900 - see sanity check */
+ int field_storage;
+ int buf_fill = 0;
+ int r;
+ int field_count = 0;
+ struct mg_file fstore = STRUCT_FILE_INITIALIZER;
+ int64_t file_size = 0; /* init here, to a avoid a false positive
+ "uninitialized variable used" warning */
+
+ int has_body_data =
+ (conn->request_info.content_length > 0) || (conn->is_chunked);
+
+ /* There are three ways to encode data from a HTML form:
+ * 1) method: GET (default)
+ * The form data is in the HTTP query string.
+ * 2) method: POST, enctype: "application/x-www-form-urlencoded"
+ * The form data is in the request body.
+ * The body is url encoded (the default encoding for POST).
+ * 3) method: POST, enctype: "multipart/form-data".
+ * The form data is in the request body of a multipart message.
+ * This is the typical way to handle file upload from a form.
+ */
+
+ if (!has_body_data) {
+ const char *data;
+
+ if (0 != strcmp(conn->request_info.request_method, "GET")) {
+ /* No body data, but not a GET request.
+ * This is not a valid form request. */
+ return -1;
+ }
+
+ /* GET request: form data is in the query string. */
+ /* The entire data has already been loaded, so there is no nead to
+ * call mg_read. We just need to split the query string into key-value
+ * pairs. */
+ data = conn->request_info.query_string;
+ if (!data) {
+ /* No query string. */
+ return -1;
+ }
+
+ /* Split data in a=1&b=xy&c=3&c=4 ... */
+ while (*data) {
+ const char *val = strchr(data, '=');
+ const char *next;
+ ptrdiff_t keylen, vallen;
+
+ if (!val) {
+ break;
+ }
+ keylen = val - data;
+
+ /* In every "field_found" callback we ask what to do with the
+ * data ("field_storage"). This could be:
+ * MG_FORM_FIELD_STORAGE_SKIP (0):
+ * ignore the value of this field
+ * MG_FORM_FIELD_STORAGE_GET (1):
+ * read the data and call the get callback function
+ * MG_FORM_FIELD_STORAGE_STORE (2):
+ * store the data in a file
+ * MG_FORM_FIELD_STORAGE_READ (3):
+ * let the user read the data (for parsing long data on the fly)
+ * MG_FORM_FIELD_STORAGE_ABORT (flag):
+ * stop parsing
+ */
+ memset(path, 0, sizeof(path));
+ field_count++;
+ field_storage = url_encoded_field_found(conn,
+ data,
+ (size_t)keylen,
+ NULL,
+ 0,
+ path,
+ sizeof(path) - 1,
+ fdh);
+
+ val++;
+ next = strchr(val, '&');
+ if (next) {
+ vallen = next - val;
+ next++;
+ } else {
+ vallen = (ptrdiff_t)strlen(val);
+ next = val + vallen;
+ }
+
+ if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
+ /* Call callback */
+ url_encoded_field_get(
+ conn, data, (size_t)keylen, val, (size_t)vallen, fdh);
+ }
+ if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
+ /* Store the content to a file */
+ if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
+ fstore.access.fp = NULL;
+ }
+ file_size = 0;
+ if (fstore.access.fp != NULL) {
+ size_t n = (size_t)
+ fwrite(val, 1, (size_t)vallen, fstore.access.fp);
+ if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
+ mg_cry_internal(conn,
+ "%s: Cannot write file %s",
+ __func__,
+ path);
+ (void)mg_fclose(&fstore.access);
+ remove_bad_file(conn, path);
+ }
+ file_size += (int64_t)n;
+
+ if (fstore.access.fp) {
+ r = mg_fclose(&fstore.access);
+ if (r == 0) {
+ /* stored successfully */
+ field_stored(conn, path, file_size, fdh);
+ } else {
+ mg_cry_internal(conn,
+ "%s: Error saving file %s",
+ __func__,
+ path);
+ remove_bad_file(conn, path);
+ }
+ fstore.access.fp = NULL;
+ }
+
+ } else {
+ mg_cry_internal(conn,
+ "%s: Cannot create file %s",
+ __func__,
+ path);
+ }
+ }
+
+ /* if (field_storage == MG_FORM_FIELD_STORAGE_READ) { */
+ /* The idea of "field_storage=read" is to let the API user read
+ * data chunk by chunk and to some data processing on the fly.
+ * This should avoid the need to store data in the server:
+ * It should neither be stored in memory, like
+ * "field_storage=get" does, nor in a file like
+ * "field_storage=store".
+ * However, for a "GET" request this does not make any much
+ * sense, since the data is already stored in memory, as it is
+ * part of the query string.
+ */
+ /* } */
+
+ if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
+ == MG_FORM_FIELD_STORAGE_ABORT) {
+ /* Stop parsing the request */
+ break;
+ }
+
+ /* Proceed to next entry */
+ data = next;
+ }
+
+ return field_count;
+ }
+
+ content_type = mg_get_header(conn, "Content-Type");
+
+ if (!content_type
+ || !mg_strcasecmp(content_type, "APPLICATION/X-WWW-FORM-URLENCODED")
+ || !mg_strcasecmp(content_type, "APPLICATION/WWW-FORM-URLENCODED")) {
+ /* The form data is in the request body data, encoded in key/value
+ * pairs. */
+ int all_data_read = 0;
+
+ /* Read body data and split it in keys and values.
+ * The encoding is like in the "GET" case above: a=1&b&c=3&c=4.
+ * Here we use "POST", and read the data from the request body.
+ * The data read on the fly, so it is not required to buffer the
+ * entire request in memory before processing it. */
+ for (;;) {
+ const char *val;
+ const char *next;
+ ptrdiff_t keylen, vallen;
+ ptrdiff_t used;
+ int end_of_key_value_pair_found = 0;
+ int get_block;
+
+ if ((size_t)buf_fill < (sizeof(buf) - 1)) {
+
+ size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
+ r = mg_read(conn, buf + (size_t)buf_fill, to_read);
+ if (r < 0) {
+ /* read error */
+ return -1;
+ }
+ if (r != (int)to_read) {
+ /* TODO: Create a function to get "all_data_read" from
+ * the conn object. All data is read if the Content-Length
+ * has been reached, or if chunked encoding is used and
+ * the end marker has been read, or if the connection has
+ * been closed. */
+ all_data_read = 1;
+ }
+ buf_fill += r;
+ buf[buf_fill] = 0;
+ if (buf_fill < 1) {
+ break;
+ }
+ }
+
+ val = strchr(buf, '=');
+
+ if (!val) {
+ break;
+ }
+ keylen = val - buf;
+ val++;
+
+ /* Call callback */
+ memset(path, 0, sizeof(path));
+ field_count++;
+ field_storage = url_encoded_field_found(conn,
+ buf,
+ (size_t)keylen,
+ NULL,
+ 0,
+ path,
+ sizeof(path) - 1,
+ fdh);
+
+ if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
+ == MG_FORM_FIELD_STORAGE_ABORT) {
+ /* Stop parsing the request */
+ break;
+ }
+
+ if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
+ if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
+ fstore.access.fp = NULL;
+ }
+ file_size = 0;
+ if (!fstore.access.fp) {
+ mg_cry_internal(conn,
+ "%s: Cannot create file %s",
+ __func__,
+ path);
+ }
+ }
+
+ get_block = 0;
+ /* Loop to read values larger than sizeof(buf)-keylen-2 */
+ do {
+ next = strchr(val, '&');
+ if (next) {
+ vallen = next - val;
+ next++;
+ end_of_key_value_pair_found = 1;
+ } else {
+ vallen = (ptrdiff_t)strlen(val);
+ next = val + vallen;
+ end_of_key_value_pair_found = all_data_read;
+ }
+
+ if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
+#if 0
+ if (!end_of_key_value_pair_found && !all_data_read) {
+ /* This callback will deliver partial contents */
+ }
+#endif
+
+ /* Call callback */
+ url_encoded_field_get(conn,
+ ((get_block > 0) ? NULL : buf),
+ ((get_block > 0) ? 0
+ : (size_t)keylen),
+ val,
+ (size_t)vallen,
+ fdh);
+ get_block++;
+ }
+ if (fstore.access.fp) {
+ size_t n = (size_t)
+ fwrite(val, 1, (size_t)vallen, fstore.access.fp);
+ if ((n != (size_t)vallen) || (ferror(fstore.access.fp))) {
+ mg_cry_internal(conn,
+ "%s: Cannot write file %s",
+ __func__,
+ path);
+ mg_fclose(&fstore.access);
+ remove_bad_file(conn, path);
+ }
+ file_size += (int64_t)n;
+ }
+
+ if (!end_of_key_value_pair_found) {
+ used = next - buf;
+ memmove(buf,
+ buf + (size_t)used,
+ sizeof(buf) - (size_t)used);
+ next = buf;
+ buf_fill -= (int)used;
+ if ((size_t)buf_fill < (sizeof(buf) - 1)) {
+
+ size_t to_read = sizeof(buf) - 1 - (size_t)buf_fill;
+ r = mg_read(conn, buf + (size_t)buf_fill, to_read);
+ if (r < 0) {
+ /* read error */
+ if (fstore.access.fp) {
+ mg_fclose(&fstore.access);
+ remove_bad_file(conn, path);
+ }
+ return -1;
+ }
+ if (r != (int)to_read) {
+ /* TODO: Create a function to get "all_data_read"
+ * from the conn object. All data is read if the
+ * Content-Length has been reached, or if chunked
+ * encoding is used and the end marker has been
+ * read, or if the connection has been closed. */
+ all_data_read = 1;
+ }
+ buf_fill += r;
+ buf[buf_fill] = 0;
+ if (buf_fill < 1) {
+ break;
+ }
+ val = buf;
+ }
+ }
+
+ } while (!end_of_key_value_pair_found);
+
+ if (fstore.access.fp) {
+ r = mg_fclose(&fstore.access);
+ if (r == 0) {
+ /* stored successfully */
+ field_stored(conn, path, file_size, fdh);
+ } else {
+ mg_cry_internal(conn,
+ "%s: Error saving file %s",
+ __func__,
+ path);
+ remove_bad_file(conn, path);
+ }
+ fstore.access.fp = NULL;
+ }
+
+ if (all_data_read && (buf_fill == 0)) {
+ /* nothing more to process */
+ break;
+ }
+
+ /* Proceed to next entry */
+ used = next - buf;
+ memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
+ buf_fill -= (int)used;
+ }
+
+ return field_count;
+ }
+
+ if (!mg_strncasecmp(content_type, "MULTIPART/FORM-DATA;", 20)) {
+ /* The form data is in the request body data, encoded as multipart
+ * content (see https://www.ietf.org/rfc/rfc1867.txt,
+ * https://www.ietf.org/rfc/rfc2388.txt). */
+ char *boundary;
+ size_t bl;
+ ptrdiff_t used;
+ struct mg_request_info part_header;
+ char *hbuf;
+ const char *content_disp, *hend, *fbeg, *fend, *nbeg, *nend;
+ const char *next;
+ unsigned part_no;
+
+ memset(&part_header, 0, sizeof(part_header));
+
+ /* Skip all spaces between MULTIPART/FORM-DATA; and BOUNDARY= */
+ bl = 20;
+ while (content_type[bl] == ' ') {
+ bl++;
+ }
+
+ /* There has to be a BOUNDARY definition in the Content-Type header */
+ if (mg_strncasecmp(content_type + bl, "BOUNDARY=", 9)) {
+ /* Malformed request */
+ return -1;
+ }
+
+ /* Copy boundary string to variable "boundary" */
+ fbeg = content_type + bl + 9;
+ bl = strlen(fbeg);
+ boundary = (char *)mg_malloc(bl + 1);
+ if (!boundary) {
+ /* Out of memory */
+ mg_cry_internal(conn,
+ "%s: Cannot allocate memory for boundary [%lu]",
+ __func__,
+ (unsigned long)bl);
+ return -1;
+ }
+ memcpy(boundary, fbeg, bl);
+ boundary[bl] = 0;
+
+ /* RFC 2046 permits the boundary string to be quoted. */
+ /* If the boundary is quoted, trim the quotes */
+ if (boundary[0] == '"') {
+ hbuf = strchr(boundary + 1, '"');
+ if ((!hbuf) || (*hbuf != '"')) {
+ /* Malformed request */
+ mg_free(boundary);
+ return -1;
+ }
+ *hbuf = 0;
+ memmove(boundary, boundary + 1, bl);
+ bl = strlen(boundary);
+ }
+
+ /* Do some sanity checks for boundary lengths */
+ if (bl > 70) {
+ /* From RFC 2046:
+ * Boundary delimiters must not appear within the
+ * encapsulated material, and must be no longer
+ * than 70 characters, not counting the two
+ * leading hyphens.
+ */
+
+ /* The initial sanity check
+ * (bl + 800 > sizeof(buf))
+ * is no longer required, since sizeof(buf) == 1024
+ *
+ * Original comment:
+ */
+ /* Sanity check: The algorithm can not work if bl >= sizeof(buf),
+ * and it will not work effectively, if the buf is only a few byte
+ * larger than bl, or if buf can not hold the multipart header
+ * plus the boundary.
+ * Check some reasonable number here, that should be fulfilled by
+ * any reasonable request from every browser. If it is not
+ * fulfilled, it might be a hand-made request, intended to
+ * interfere with the algorithm. */
+ mg_free(boundary);
+ return -1;
+ }
+ if (bl < 4) {
+ /* Sanity check: A boundary string of less than 4 bytes makes
+ * no sense either. */
+ mg_free(boundary);
+ return -1;
+ }
+
+ for (part_no = 0;; part_no++) {
+ size_t towrite, fnlen, n;
+ int get_block;
+
+ r = mg_read(conn,
+ buf + (size_t)buf_fill,
+ sizeof(buf) - 1 - (size_t)buf_fill);
+ if (r < 0) {
+ /* read error */
+ mg_free(boundary);
+ return -1;
+ }
+ buf_fill += r;
+ buf[buf_fill] = 0;
+ if (buf_fill < 1) {
+ /* No data */
+ mg_free(boundary);
+ return -1;
+ }
+
+ if (part_no == 0) {
+ int d = 0;
+ while ((buf[d] != '-') && (d < buf_fill)) {
+ d++;
+ }
+ if ((d > 0) && (buf[d] == '-')) {
+ memmove(buf, buf + d, (unsigned)buf_fill - (unsigned)d);
+ buf_fill -= d;
+ buf[buf_fill] = 0;
+ }
+ }
+
+ if (buf[0] != '-' || buf[1] != '-') {
+ /* Malformed request */
+ mg_free(boundary);
+ return -1;
+ }
+ if (0 != strncmp(buf + 2, boundary, bl)) {
+ /* Malformed request */
+ mg_free(boundary);
+ return -1;
+ }
+ if (buf[bl + 2] != '\r' || buf[bl + 3] != '\n') {
+ /* Every part must end with \r\n, if there is another part.
+ * The end of the request has an extra -- */
+ if (((size_t)buf_fill != (size_t)(bl + 6))
+ || (strncmp(buf + bl + 2, "--\r\n", 4))) {
+ /* Malformed request */
+ mg_free(boundary);
+ return -1;
+ }
+ /* End of the request */
+ break;
+ }
+
+ /* Next, we need to get the part header: Read until \r\n\r\n */
+ hbuf = buf + bl + 4;
+ hend = strstr(hbuf, "\r\n\r\n");
+ if (!hend) {
+ /* Malformed request */
+ mg_free(boundary);
+ return -1;
+ }
+
+ part_header.num_headers =
+ parse_http_headers(&hbuf, part_header.http_headers);
+ if ((hend + 2) != hbuf) {
+ /* Malformed request */
+ mg_free(boundary);
+ return -1;
+ }
+
+ /* Skip \r\n\r\n */
+ hend += 4;
+
+ /* According to the RFC, every part has to have a header field like:
+ * Content-Disposition: form-data; name="..." */
+ content_disp = get_header(part_header.http_headers,
+ part_header.num_headers,
+ "Content-Disposition");
+ if (!content_disp) {
+ /* Malformed request */
+ mg_free(boundary);
+ return -1;
+ }
+
+ /* Get the mandatory name="..." part of the Content-Disposition
+ * header. */
+ nbeg = strstr(content_disp, "name=\"");
+ while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
+ /* It could be somethingname= instead of name= */
+ nbeg = strstr(nbeg + 1, "name=\"");
+ }
+
+ /* This line is not required, but otherwise some compilers
+ * generate spurious warnings. */
+ nend = nbeg;
+ /* And others complain, the result is unused. */
+ (void)nend;
+
+ /* If name=" is found, search for the closing " */
+ if (nbeg) {
+ nbeg += 6;
+ nend = strchr(nbeg, '\"');
+ if (!nend) {
+ /* Malformed request */
+ mg_free(boundary);
+ return -1;
+ }
+ } else {
+ /* name= without quotes is also allowed */
+ nbeg = strstr(content_disp, "name=");
+ while ((nbeg != NULL) && (strcspn(nbeg - 1, ":,; \t") != 0)) {
+ /* It could be somethingname= instead of name= */
+ nbeg = strstr(nbeg + 1, "name=");
+ }
+ if (!nbeg) {
+ /* Malformed request */
+ mg_free(boundary);
+ return -1;
+ }
+ nbeg += 5;
+
+ /* RFC 2616 Sec. 2.2 defines a list of allowed
+ * separators, but many of them make no sense
+ * here, e.g. various brackets or slashes.
+ * If they are used, probably someone is
+ * trying to attack with curious hand made
+ * requests. Only ; , space and tab seem to be
+ * reasonable here. Ignore everything else. */
+ nend = nbeg + strcspn(nbeg, ",; \t");
+ }
+
+ /* Get the optional filename="..." part of the Content-Disposition
+ * header. */
+ fbeg = strstr(content_disp, "filename=\"");
+ while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
+ /* It could be somethingfilename= instead of filename= */
+ fbeg = strstr(fbeg + 1, "filename=\"");
+ }
+
+ /* This line is not required, but otherwise some compilers
+ * generate spurious warnings. */
+ fend = fbeg;
+
+ /* If filename=" is found, search for the closing " */
+ if (fbeg) {
+ fbeg += 10;
+ fend = strchr(fbeg, '\"');
+
+ if (!fend) {
+ /* Malformed request (the filename field is optional, but if
+ * it exists, it needs to be terminated correctly). */
+ mg_free(boundary);
+ return -1;
+ }
+
+ /* TODO: check Content-Type */
+ /* Content-Type: application/octet-stream */
+ }
+ if (!fbeg) {
+ /* Try the same without quotes */
+ fbeg = strstr(content_disp, "filename=");
+ while ((fbeg != NULL) && (strcspn(fbeg - 1, ":,; \t") != 0)) {
+ /* It could be somethingfilename= instead of filename= */
+ fbeg = strstr(fbeg + 1, "filename=");
+ }
+ if (fbeg) {
+ fbeg += 9;
+ fend = fbeg + strcspn(fbeg, ",; \t");
+ }
+ }
+
+ if (!fbeg || !fend) {
+ fbeg = NULL;
+ fend = NULL;
+ fnlen = 0;
+ } else {
+ fnlen = (size_t)(fend - fbeg);
+ }
+
+ /* In theory, it could be possible that someone crafts
+ * a request like name=filename=xyz. Check if name and
+ * filename do not overlap. */
+ if (!(((ptrdiff_t)fbeg > (ptrdiff_t)nend)
+ || ((ptrdiff_t)nbeg > (ptrdiff_t)fend))) {
+ mg_free(boundary);
+ return -1;
+ }
+
+ /* Call callback for new field */
+ memset(path, 0, sizeof(path));
+ field_count++;
+ field_storage = url_encoded_field_found(conn,
+ nbeg,
+ (size_t)(nend - nbeg),
+ ((fnlen > 0) ? fbeg : NULL),
+ fnlen,
+ path,
+ sizeof(path) - 1,
+ fdh);
+
+ /* If the boundary is already in the buffer, get the address,
+ * otherwise next will be NULL. */
+ next = search_boundary(hbuf,
+ (size_t)((buf - hbuf) + buf_fill),
+ boundary,
+ bl);
+
+ if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
+ /* Store the content to a file */
+ if (mg_fopen(conn, path, MG_FOPEN_MODE_WRITE, &fstore) == 0) {
+ fstore.access.fp = NULL;
+ }
+ file_size = 0;
+
+ if (!fstore.access.fp) {
+ mg_cry_internal(conn,
+ "%s: Cannot create file %s",
+ __func__,
+ path);
+ }
+ }
+
+ get_block = 0;
+ while (!next) {
+ /* Set "towrite" to the number of bytes available
+ * in the buffer */
+ towrite = (size_t)(buf - hend + buf_fill);
+ /* Subtract the boundary length, to deal with
+ * cases the boundary is only partially stored
+ * in the buffer. */
+ towrite -= bl + 4;
+
+ if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
+ unencoded_field_get(conn,
+ ((get_block > 0) ? NULL : nbeg),
+ ((get_block > 0)
+ ? 0
+ : (size_t)(nend - nbeg)),
+ hend,
+ towrite,
+ fdh);
+ get_block++;
+ }
+
+ if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
+ if (fstore.access.fp) {
+
+ /* Store the content of the buffer. */
+ n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
+ if ((n != towrite) || (ferror(fstore.access.fp))) {
+ mg_cry_internal(conn,
+ "%s: Cannot write file %s",
+ __func__,
+ path);
+ mg_fclose(&fstore.access);
+ remove_bad_file(conn, path);
+ }
+ file_size += (int64_t)n;
+ }
+ }
+
+ memmove(buf, hend + towrite, bl + 4);
+ buf_fill = (int)(bl + 4);
+ hend = buf;
+
+ /* Read new data */
+ r = mg_read(conn,
+ buf + (size_t)buf_fill,
+ sizeof(buf) - 1 - (size_t)buf_fill);
+ if (r < 0) {
+ /* read error */
+ if (fstore.access.fp) {
+ mg_fclose(&fstore.access);
+ remove_bad_file(conn, path);
+ }
+ mg_free(boundary);
+ return -1;
+ }
+ buf_fill += r;
+ buf[buf_fill] = 0;
+ /* buf_fill is at least 8 here */
+
+ /* Find boundary */
+ next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
+ }
+
+ towrite = (size_t)(next - hend);
+
+ if (field_storage == MG_FORM_FIELD_STORAGE_GET) {
+ /* Call callback */
+ unencoded_field_get(conn,
+ ((get_block > 0) ? NULL : nbeg),
+ ((get_block > 0) ? 0
+ : (size_t)(nend - nbeg)),
+ hend,
+ towrite,
+ fdh);
+ }
+
+ if (field_storage == MG_FORM_FIELD_STORAGE_STORE) {
+
+ if (fstore.access.fp) {
+ n = (size_t)fwrite(hend, 1, towrite, fstore.access.fp);
+ if ((n != towrite) || (ferror(fstore.access.fp))) {
+ mg_cry_internal(conn,
+ "%s: Cannot write file %s",
+ __func__,
+ path);
+ mg_fclose(&fstore.access);
+ remove_bad_file(conn, path);
+ } else {
+ file_size += (int64_t)n;
+ r = mg_fclose(&fstore.access);
+ if (r == 0) {
+ /* stored successfully */
+ field_stored(conn, path, file_size, fdh);
+ } else {
+ mg_cry_internal(conn,
+ "%s: Error saving file %s",
+ __func__,
+ path);
+ remove_bad_file(conn, path);
+ }
+ }
+ fstore.access.fp = NULL;
+ }
+ }
+
+ if ((field_storage & MG_FORM_FIELD_STORAGE_ABORT)
+ == MG_FORM_FIELD_STORAGE_ABORT) {
+ /* Stop parsing the request */
+ break;
+ }
+
+ /* Remove from the buffer */
+ used = next - buf + 2;
+ memmove(buf, buf + (size_t)used, sizeof(buf) - (size_t)used);
+ buf_fill -= (int)used;
+ }
+
+ /* All parts handled */
+ mg_free(boundary);
+ return field_count;
+ }
+
+ /* Unknown Content-Type */
+ return -1;
+}
+
+
+/* End of handle_form.inl */
[3/6] celix git commit: CELIX-451: Adds celix_bundle_dir for copying
files. Also remove mongoose example and adds a new civetweb example.
Posted by pn...@apache.org.
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/civetweb/md5.inl
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/civetweb/md5.inl b/examples/celix-examples/civetweb/civetweb/md5.inl
new file mode 100644
index 0000000..beb1cf9
--- /dev/null
+++ b/examples/celix-examples/civetweb/civetweb/md5.inl
@@ -0,0 +1,471 @@
+/*
+ * This an amalgamation of md5.c and md5.h into a single file
+ * with all static declaration to reduce linker conflicts
+ * in Civetweb.
+ *
+ * The MD5_STATIC declaration was added to facilitate static
+ * inclusion.
+ * No Face Press, LLC
+ */
+
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <gh...@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <pu...@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#if !defined(md5_INCLUDED)
+#define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Initialize the algorithm. */
+MD5_STATIC void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+MD5_STATIC void
+md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes);
+
+/* Finish the message and return the digest. */
+MD5_STATIC void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#if defined(__cplusplus)
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
+
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <gh...@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#if !defined(MD5_STATIC)
+#include <string.h>
+#endif
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#if defined(ARCH_IS_BIG_ENDIAN)
+#define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+#define BYTE_ORDER (0)
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 (0x242070db)
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 (0x4787c62a)
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 (0x698098d8)
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 (0x6b901122)
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 (0x49b40821)
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 (0x265e5a51)
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 (0x02441453)
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 (0x21e1cde6)
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 (0x455a14ed)
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 (0x676f02d9)
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 (0x6d9d6122)
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 (0x4bdecfa9)
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 (0x289b7ec6)
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 (0x04881d05)
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 (0x1fa27cf8)
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 (0x432aff97)
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 (0x655b59c3)
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 (0x6fa87e4f)
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 (0x4e0811a1)
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 (0x2ad7d2bb)
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2],
+ d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned, a direct assignment is possible */
+ /* cast through a (void *) should avoid a compiler warning,
+ see
+ https://github.com/bel2125/civetweb/issues/94#issuecomment-98112861
+ */
+ X = (const md5_word_t *)(const void *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+#if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+#else
+#define xbuf X /* (static only) */
+#endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = (md5_word_t)(xp[0]) + (md5_word_t)(xp[1] << 8)
+ + (md5_word_t)(xp[2] << 16)
+ + (md5_word_t)(xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+/* Round 1. */
+/* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti) \
+ t = a + F(b, c, d) + X[k] + Ti; \
+ a = ROTATE_LEFT(t, s) + b
+
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+/* Round 2. */
+/* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti) \
+ t = a + G(b, c, d) + X[k] + Ti; \
+ a = ROTATE_LEFT(t, s) + b
+
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+/* Round 3. */
+/* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti) \
+ t = a + H(b, c, d) + X[k] + Ti; \
+ a = ROTATE_LEFT(t, s) + b
+
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+/* Round 4. */
+/* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti) \
+ t = a + I(b, c, d) + X[k] + Ti; \
+ a = ROTATE_LEFT(t, s) + b
+
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+MD5_STATIC void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+MD5_STATIC void
+md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes)
+{
+ const md5_byte_t *p = data;
+ size_t left = nbytes;
+ size_t offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += (md5_word_t)(nbytes >> 29);
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+MD5_STATIC void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+
+/* End of md5.inl */
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/civetweb/sha1.inl
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/civetweb/sha1.inl b/examples/celix-examples/civetweb/civetweb/sha1.inl
new file mode 100644
index 0000000..0a2ae46
--- /dev/null
+++ b/examples/celix-examples/civetweb/civetweb/sha1.inl
@@ -0,0 +1,323 @@
+/*
+SHA-1 in C
+By Steve Reid <sr...@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jb...@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+ void SHA1Update(SHA_CTX* context, unsigned char* data, unsigned int
+len)
+to
+ void SHA1Update(SHA_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it. This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sr...@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Sa...@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.
+
+-----------------
+Modified 07/2002
+By Ralph Giles <gi...@ghostscript.com>
+Still 100% public domain
+modified for use with stdint types, autoconf
+code cleanup, removed attribution comments
+switched SHA1Final() argument order for consistency
+use SHA1_ prefix for public api
+move public api to sha1.h
+*/
+
+/*
+11/2016 adapted for CivetWeb:
+ include sha1.h in sha1.c,
+ rename to sha1.inl
+ remove unused #ifdef sections
+ make endian independent
+ align buffer to 4 bytes
+ remove unused variable assignments
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#include <string.h>
+#include <stdint.h>
+
+typedef struct {
+ uint32_t state[5];
+ uint32_t count[2];
+ uint8_t buffer[64];
+} SHA_CTX;
+
+#define SHA1_DIGEST_SIZE 20
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+
+
+typedef union {
+ uint8_t c[64];
+ uint32_t l[16];
+} CHAR64LONG16;
+
+
+static uint32_t
+blk0(CHAR64LONG16 *block, int i)
+{
+ static const uint32_t n = 1u;
+ if ((*((uint8_t *)(&n))) == 1) {
+ /* little endian / intel byte order */
+ block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00)
+ | (rol(block->l[i], 8) & 0x00FF00FF);
+ }
+ return block->l[i];
+}
+
+#define blk(block, i) \
+ ((block)->l[(i)&15] = \
+ rol((block)->l[((i) + 13) & 15] ^ (block)->l[((i) + 8) & 15] \
+ ^ (block)->l[((i) + 2) & 15] ^ (block)->l[(i)&15], \
+ 1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v, w, x, y, z, i) \
+ z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R1(v, w, x, y, z, i) \
+ z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R2(v, w, x, y, z, i) \
+ z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + rol(v, 5); \
+ w = rol(w, 30);
+#define R3(v, w, x, y, z, i) \
+ z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + rol(v, 5); \
+ w = rol(w, 30);
+#define R4(v, w, x, y, z, i) \
+ z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + rol(v, 5); \
+ w = rol(w, 30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+static void
+SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
+{
+ uint32_t a, b, c, d, e;
+
+ /* Must use an aligned, read/write buffer */
+ CHAR64LONG16 block[1];
+ memcpy(block, buffer, sizeof(block));
+
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a, b, c, d, e, 0);
+ R0(e, a, b, c, d, 1);
+ R0(d, e, a, b, c, 2);
+ R0(c, d, e, a, b, 3);
+ R0(b, c, d, e, a, 4);
+ R0(a, b, c, d, e, 5);
+ R0(e, a, b, c, d, 6);
+ R0(d, e, a, b, c, 7);
+ R0(c, d, e, a, b, 8);
+ R0(b, c, d, e, a, 9);
+ R0(a, b, c, d, e, 10);
+ R0(e, a, b, c, d, 11);
+ R0(d, e, a, b, c, 12);
+ R0(c, d, e, a, b, 13);
+ R0(b, c, d, e, a, 14);
+ R0(a, b, c, d, e, 15);
+ R1(e, a, b, c, d, 16);
+ R1(d, e, a, b, c, 17);
+ R1(c, d, e, a, b, 18);
+ R1(b, c, d, e, a, 19);
+ R2(a, b, c, d, e, 20);
+ R2(e, a, b, c, d, 21);
+ R2(d, e, a, b, c, 22);
+ R2(c, d, e, a, b, 23);
+ R2(b, c, d, e, a, 24);
+ R2(a, b, c, d, e, 25);
+ R2(e, a, b, c, d, 26);
+ R2(d, e, a, b, c, 27);
+ R2(c, d, e, a, b, 28);
+ R2(b, c, d, e, a, 29);
+ R2(a, b, c, d, e, 30);
+ R2(e, a, b, c, d, 31);
+ R2(d, e, a, b, c, 32);
+ R2(c, d, e, a, b, 33);
+ R2(b, c, d, e, a, 34);
+ R2(a, b, c, d, e, 35);
+ R2(e, a, b, c, d, 36);
+ R2(d, e, a, b, c, 37);
+ R2(c, d, e, a, b, 38);
+ R2(b, c, d, e, a, 39);
+ R3(a, b, c, d, e, 40);
+ R3(e, a, b, c, d, 41);
+ R3(d, e, a, b, c, 42);
+ R3(c, d, e, a, b, 43);
+ R3(b, c, d, e, a, 44);
+ R3(a, b, c, d, e, 45);
+ R3(e, a, b, c, d, 46);
+ R3(d, e, a, b, c, 47);
+ R3(c, d, e, a, b, 48);
+ R3(b, c, d, e, a, 49);
+ R3(a, b, c, d, e, 50);
+ R3(e, a, b, c, d, 51);
+ R3(d, e, a, b, c, 52);
+ R3(c, d, e, a, b, 53);
+ R3(b, c, d, e, a, 54);
+ R3(a, b, c, d, e, 55);
+ R3(e, a, b, c, d, 56);
+ R3(d, e, a, b, c, 57);
+ R3(c, d, e, a, b, 58);
+ R3(b, c, d, e, a, 59);
+ R4(a, b, c, d, e, 60);
+ R4(e, a, b, c, d, 61);
+ R4(d, e, a, b, c, 62);
+ R4(c, d, e, a, b, 63);
+ R4(b, c, d, e, a, 64);
+ R4(a, b, c, d, e, 65);
+ R4(e, a, b, c, d, 66);
+ R4(d, e, a, b, c, 67);
+ R4(c, d, e, a, b, 68);
+ R4(b, c, d, e, a, 69);
+ R4(a, b, c, d, e, 70);
+ R4(e, a, b, c, d, 71);
+ R4(d, e, a, b, c, 72);
+ R4(c, d, e, a, b, 73);
+ R4(b, c, d, e, a, 74);
+ R4(a, b, c, d, e, 75);
+ R4(e, a, b, c, d, 76);
+ R4(d, e, a, b, c, 77);
+ R4(c, d, e, a, b, 78);
+ R4(b, c, d, e, a, 79);
+
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+}
+
+
+/* SHA1Init - Initialize new context */
+SHA_API void
+SHA1_Init(SHA_CTX *context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+SHA_API void
+SHA1_Update(SHA_CTX *context, const uint8_t *data, const uint32_t len)
+{
+ uint32_t i, j;
+
+ j = context->count[0];
+ if ((context->count[0] += (len << 3)) < j) {
+ context->count[1]++;
+ }
+ context->count[1] += (len >> 29);
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ i = 64 - j;
+ memcpy(&context->buffer[j], data, i);
+ SHA1_Transform(context->state, context->buffer);
+ for (; i + 63 < len; i += 64) {
+ SHA1_Transform(context->state, &data[i]);
+ }
+ j = 0;
+ } else {
+ i = 0;
+ }
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+SHA_API void
+SHA1_Final(unsigned char *digest, SHA_CTX *context)
+{
+ uint32_t i;
+ uint8_t finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] =
+ (uint8_t)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8))
+ & 255); /* Endian independent */
+ }
+ SHA1_Update(context, (uint8_t *)"\x80", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1_Update(context, (uint8_t *)"\x00", 1);
+ }
+ SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
+ for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+ digest[i] =
+ (uint8_t)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
+ }
+
+ /* Wipe variables */
+ memset(context, '\0', sizeof(*context));
+}
+
+
+/* End of sha1.inl */
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/civetweb/timer.inl
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/civetweb/timer.inl b/examples/celix-examples/civetweb/civetweb/timer.inl
new file mode 100644
index 0000000..eebbec0
--- /dev/null
+++ b/examples/celix-examples/civetweb/civetweb/timer.inl
@@ -0,0 +1,246 @@
+/* This file is part of the CivetWeb web server.
+ * See https://github.com/civetweb/civetweb/
+ * (C) 2014-2018 by the CivetWeb authors, MIT license.
+ */
+
+#if !defined(MAX_TIMERS)
+#define MAX_TIMERS MAX_WORKER_THREADS
+#endif
+
+typedef int (*taction)(void *arg);
+
+struct ttimer {
+ double time;
+ double period;
+ taction action;
+ void *arg;
+};
+
+struct ttimers {
+ pthread_t threadid; /* Timer thread ID */
+ pthread_mutex_t mutex; /* Protects timer lists */
+ struct ttimer timers[MAX_TIMERS]; /* List of timers */
+ unsigned timer_count; /* Current size of timer list */
+};
+
+
+TIMER_API double
+timer_getcurrenttime(void)
+{
+#if defined(_WIN32)
+ /* GetTickCount returns milliseconds since system start as
+ * unsigned 32 bit value. It will wrap around every 49.7 days.
+ * We need to use a 64 bit counter (will wrap in 500 mio. years),
+ * by adding the 32 bit difference since the last call to a
+ * 64 bit counter. This algorithm will only work, if this
+ * function is called at least once every 7 weeks. */
+ static DWORD last_tick;
+ static uint64_t now_tick64;
+
+ DWORD now_tick = GetTickCount();
+
+ now_tick64 += ((DWORD)(now_tick - last_tick));
+ last_tick = now_tick;
+ return (double)now_tick64 * 1.0E-3;
+#else
+ struct timespec now_ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &now_ts);
+ return (double)now_ts.tv_sec + (double)now_ts.tv_nsec * 1.0E-9;
+#endif
+}
+
+
+TIMER_API int
+timer_add(struct mg_context *ctx,
+ double next_time,
+ double period,
+ int is_relative,
+ taction action,
+ void *arg)
+{
+ unsigned u, v;
+ int error = 0;
+ double now;
+
+ if (ctx->stop_flag) {
+ return 0;
+ }
+
+ now = timer_getcurrenttime();
+
+ /* HCP24: if is_relative = 0 and next_time < now
+ * action will be called so fast as possible
+ * if additional period > 0
+ * action will be called so fast as possible
+ * n times until (next_time + (n * period)) > now
+ * then the period is working
+ * Solution:
+ * if next_time < now then we set next_time = now.
+ * The first callback will be so fast as possible (now)
+ * but the next callback on period
+ */
+ if (is_relative) {
+ next_time += now;
+ }
+
+ /* You can not set timers into the past */
+ if (next_time < now) {
+ next_time = now;
+ }
+
+ pthread_mutex_lock(&ctx->timers->mutex);
+ if (ctx->timers->timer_count == MAX_TIMERS) {
+ error = 1;
+ } else {
+ /* Insert new timer into a sorted list. */
+ /* The linear list is still most efficient for short lists (small
+ * number of timers) - if there are many timers, different
+ * algorithms will work better. */
+ for (u = 0; u < ctx->timers->timer_count; u++) {
+ if (ctx->timers->timers[u].time > next_time) {
+ /* HCP24: moving all timers > next_time */
+ for (v = ctx->timers->timer_count; v > u; v--) {
+ ctx->timers->timers[v] = ctx->timers->timers[v - 1];
+ }
+ break;
+ }
+ }
+ ctx->timers->timers[u].time = next_time;
+ ctx->timers->timers[u].period = period;
+ ctx->timers->timers[u].action = action;
+ ctx->timers->timers[u].arg = arg;
+ ctx->timers->timer_count++;
+ }
+ pthread_mutex_unlock(&ctx->timers->mutex);
+ return error;
+}
+
+
+static void
+timer_thread_run(void *thread_func_param)
+{
+ struct mg_context *ctx = (struct mg_context *)thread_func_param;
+ double d;
+ unsigned u;
+ int re_schedule;
+ struct ttimer t;
+
+ mg_set_thread_name("timer");
+
+ if (ctx->callbacks.init_thread) {
+ /* Timer thread */
+ ctx->callbacks.init_thread(ctx, 2);
+ }
+
+ d = timer_getcurrenttime();
+
+ while (ctx->stop_flag == 0) {
+ pthread_mutex_lock(&ctx->timers->mutex);
+ if ((ctx->timers->timer_count > 0)
+ && (d >= ctx->timers->timers[0].time)) {
+ t = ctx->timers->timers[0];
+ for (u = 1; u < ctx->timers->timer_count; u++) {
+ ctx->timers->timers[u - 1] = ctx->timers->timers[u];
+ }
+ ctx->timers->timer_count--;
+ pthread_mutex_unlock(&ctx->timers->mutex);
+ re_schedule = t.action(t.arg);
+ if (re_schedule && (t.period > 0)) {
+ timer_add(ctx, t.time + t.period, t.period, 0, t.action, t.arg);
+ }
+ continue;
+ } else {
+ pthread_mutex_unlock(&ctx->timers->mutex);
+ }
+
+/* 10 ms seems reasonable.
+ * A faster loop (smaller sleep value) increases CPU load,
+ * a slower loop (higher sleep value) decreases timer accuracy.
+ */
+#if defined(_WIN32)
+ Sleep(10);
+#else
+ usleep(10000);
+#endif
+
+ d = timer_getcurrenttime();
+ }
+
+ pthread_mutex_lock(&ctx->timers->mutex);
+ ctx->timers->timer_count = 0;
+ pthread_mutex_unlock(&ctx->timers->mutex);
+}
+
+
+#if defined(_WIN32)
+static unsigned __stdcall timer_thread(void *thread_func_param)
+{
+ timer_thread_run(thread_func_param);
+ return 0;
+}
+#else
+static void *
+timer_thread(void *thread_func_param)
+{
+ struct sigaction sa;
+
+ /* Ignore SIGPIPE */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, NULL);
+
+ timer_thread_run(thread_func_param);
+ return NULL;
+}
+#endif /* _WIN32 */
+
+
+TIMER_API int
+timers_init(struct mg_context *ctx)
+{
+ /* Initialize timers data structure */
+ ctx->timers =
+ (struct ttimers *)mg_calloc_ctx(sizeof(struct ttimers), 1, ctx);
+
+ if (!ctx->timers) {
+ return -1;
+ }
+
+ /* Initialize mutex */
+ if (0 != pthread_mutex_init(&ctx->timers->mutex, NULL)) {
+ mg_free((void *)(ctx->timers));
+ return -1;
+ }
+
+ /* For some systems timer_getcurrenttime does some initialization
+ * during the first call. Call it once now, ignore the result. */
+ (void)timer_getcurrenttime();
+
+ /* Start timer thread */
+ mg_start_thread_with_id(timer_thread, ctx, &ctx->timers->threadid);
+
+ return 0;
+}
+
+
+TIMER_API void
+timers_exit(struct mg_context *ctx)
+{
+ if (ctx->timers) {
+ pthread_mutex_lock(&ctx->timers->mutex);
+ ctx->timers->timer_count = 0;
+
+ mg_join_thread(ctx->timers->threadid);
+
+ /* TODO: Do we really need to unlock the mutex, before
+ * destroying it, if it's destroyed by the thread currently
+ * owning the mutex? */
+ pthread_mutex_unlock(&ctx->timers->mutex);
+ (void)pthread_mutex_destroy(&ctx->timers->mutex);
+ mg_free(ctx->timers);
+ }
+}
+
+
+/* End of timer.inl */
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/resources/index.html
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/resources/index.html b/examples/celix-examples/civetweb/resources/index.html
new file mode 100644
index 0000000..ab46896
--- /dev/null
+++ b/examples/celix-examples/civetweb/resources/index.html
@@ -0,0 +1,34 @@
+<!--
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+-->
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8"/>
+ <title>Apache Celix Embedded Civetweb example</title>
+ <script src="script.js"></script>
+</head>
+<body>
+ <div>
+ <input type="text" id="command_input"/>
+ <input type="button" id="command_button" value="send"/>
+ </div>
+ <textarea rows="50" cols="80" id="console_output"></textarea>
+ <script>docReady();</script>
+</body>
+</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/resources/script.js
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/resources/script.js b/examples/celix-examples/civetweb/resources/script.js
new file mode 100644
index 0000000..588fbf6
--- /dev/null
+++ b/examples/celix-examples/civetweb/resources/script.js
@@ -0,0 +1,43 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+function docReady() {
+ var host = window.location.host;
+ var shellSocket = new WebSocket("ws://" + host + "/shellsocket");
+
+ shellSocket.onmessage = function (event) {
+ document.getElementById("console_output").value = event.data;
+ };
+ shellSocket.onopen = function (event) {
+ shellSocket.send("lb");
+ };
+
+ document.getElementById("command_button").onclick = function() {
+ input = document.getElementById("command_input").value;
+ shellSocket.send(input);
+ };
+
+ var input = document.getElementById("command_input");
+ input.addEventListener("keyup", function(event) {
+ event.preventDefault();
+ if (event.key === "Enter") {
+ document.getElementById("command_button").click();
+ }
+ });
+}
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/src/bundle_activator.c
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/src/bundle_activator.c b/examples/celix-examples/civetweb/src/bundle_activator.c
new file mode 100644
index 0000000..cae9616
--- /dev/null
+++ b/examples/celix-examples/civetweb/src/bundle_activator.c
@@ -0,0 +1,108 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+
+#include "celix_bundle_activator.h"
+#include "celix_bundle.h"
+#include "shell.h"
+
+#include <civetweb.h>
+#include <string.h>
+
+typedef struct activator_data {
+ celix_bundle_context_t *ctx;
+ char *root;
+ struct mg_context *mgCtx;
+} activator_data_t;
+
+struct use_shell_arg {
+ char *command;
+ struct mg_connection *conn;
+};
+
+static void useShell(void *handle, void *svc) {
+ shell_service_t *shell = svc;
+ struct use_shell_arg *arg = handle;
+ char *buf = NULL;
+ size_t size;
+ FILE *out = open_memstream(&buf, &size);
+ shell->executeCommand(shell->shell, (char*)arg->command, out, out);
+ fclose(out);
+ mg_websocket_write(arg->conn, MG_WEBSOCKET_OPCODE_TEXT, buf, size);
+};
+
+static int websocket_data_handler(struct mg_connection *conn, int bits, char *data, size_t data_len, void *handle) {
+ activator_data_t *act = handle;
+ struct use_shell_arg arg;
+ arg.conn = conn;
+
+ //NOTE data is a not null terminated string..
+ arg.command = calloc(data_len+1, sizeof(char));
+ memcpy(arg.command, data, data_len);
+ arg.command[data_len] = '\0';
+
+
+ bool called = celix_bundleContext_useService(act->ctx, OSGI_SHELL_SERVICE_NAME, &arg, useShell);
+ if (!called) {
+ fprintf(stderr, "No shell available!\n");
+ }
+
+ free(arg.command);
+ return 1; //keep open
+}
+
+
+static celix_status_t activator_start(activator_data_t *data, celix_bundle_context_t *ctx) {
+ data->ctx = ctx;
+
+ bundle_t *bnd = celix_bundleContext_getBundle(ctx);
+ data->root = celix_bundle_getEntry(bnd, "resources");
+
+ const char *options[] = {
+ "document_root", data->root,
+ "listening_ports", "8081",
+ "websocket_timeout_ms", "3600000",
+ NULL
+ };
+
+ if (data->root != NULL) {
+ data->mgCtx = mg_start(NULL, data, options);
+ mg_set_websocket_handler(data->mgCtx, "/shellsocket", NULL, NULL, websocket_data_handler, NULL, data);
+ }
+
+ if (data->mgCtx != NULL) {
+ fprintf(stdout, "Started civetweb at port %s\n", mg_get_option(data->mgCtx, "listening_ports"));
+ } else {
+ fprintf(stderr, "Error starting civetweb bundle\n");
+ }
+
+ return CELIX_SUCCESS;
+}
+
+static celix_status_t activator_stop(activator_data_t *data, celix_bundle_context_t *ctx) {
+ if (data->mgCtx != NULL) {
+ printf("Stopping civetweb\n");
+ mg_stop(data->mgCtx);
+ data->mgCtx = NULL;
+ }
+ free(data->root);
+ return CELIX_SUCCESS;
+}
+
+CELIX_GEN_BUNDLE_ACTIVATOR(activator_data_t, activator_start, activator_stop)
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/mongoose/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/examples/celix-examples/mongoose/CMakeLists.txt b/examples/celix-examples/mongoose/CMakeLists.txt
deleted file mode 100644
index d0e94ae..0000000
--- a/examples/celix-examples/mongoose/CMakeLists.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-add_library(mongooselib STATIC private/src/mongoose.c)
-SET_TARGET_PROPERTIES(mongooselib PROPERTIES COMPILE_FLAGS -fPIC)
-
-SET(BUNDLE_SYMBOLICNAME "apache_celix_examples_mongoose")
-SET(BUNDLE_VERSION "0.0.1")
-
-include_directories("private/include")
-include_directories("${PROJECT_SOURCE_DIR}/utils/public/include")
-
-if(WIN32)
- set(LIBS wsock32)
-endif(WIN32)
-
-add_celix_bundle(mongoose
- VERSION 0.0.1
- SOURCES
- private/src/activator
- private/include/mongoose.h
-)
-
-celix_bundle_files(mongoose ${CMAKE_CURRENT_LIST_DIR}/root)
-
-target_link_libraries(mongoose PRIVATE mongooselib ${LIBS})
-
-add_celix_container("mongoose_deploy" BUNDLES Celix::shell Celix::shell_tui Celix::log_service mongoose)
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/mongoose/private/include/mongoose.h
----------------------------------------------------------------------
diff --git a/examples/celix-examples/mongoose/private/include/mongoose.h b/examples/celix-examples/mongoose/private/include/mongoose.h
deleted file mode 100644
index a846df4..0000000
--- a/examples/celix-examples/mongoose/private/include/mongoose.h
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright (c) 2004-2010 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.
-
-#ifndef MONGOOSE_HEADER_INCLUDED
-#define MONGOOSE_HEADER_INCLUDED
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __cplusplus
-
-struct mg_context; // Handle for the HTTP service itself
-struct mg_connection; // Handle for the individual connection
-
-
-// This structure contains information about the HTTP request.
-struct mg_request_info {
- char *request_method; // "GET", "POST", etc
- char *uri; // URL-decoded URI
- char *http_version; // E.g. "1.0", "1.1"
- char *query_string; // \0 - terminated
- char *remote_user; // Authenticated user
- char *log_message; // Mongoose error log message
- long remote_ip; // Client's IP address
- int remote_port; // Client's port
- int status_code; // HTTP reply status code
- int is_ssl; // 1 if SSL-ed, 0 if not
- int num_headers; // Number of headers
- struct mg_header {
- char *name; // HTTP header name
- char *value; // HTTP header value
- } http_headers[64]; // Maximum 64 headers
-};
-
-// Various events on which user-defined function is called by Mongoose.
-enum mg_event {
- MG_NEW_REQUEST, // New HTTP request has arrived from the client
- MG_HTTP_ERROR, // HTTP error must be returned to the client
- MG_EVENT_LOG, // Mongoose logs an event, request_info.log_message
- MG_INIT_SSL, // Mongoose initializes SSL. Instead of mg_connection *,
- // SSL context is passed to the callback function.
-};
-
-// Prototype for the user-defined function. Mongoose calls this function
-// on every event mentioned above.
-//
-// Parameters:
-// event: which event has been triggered.
-// conn: opaque connection handler. Could be used to read, write data to the
-// client, etc. See functions below that accept "mg_connection *".
-// request_info: Information about HTTP request.
-//
-// Return:
-// If handler returns non-NULL, that means that handler has processed the
-// request by sending appropriate HTTP reply to the client. Mongoose treats
-// the request as served.
-// If callback returns NULL, that means that callback has not processed
-// the request. Handler must not send any data to the client in this case.
-// Mongoose proceeds with request handling as if nothing happened.
-typedef void * (*mg_callback_pt)(enum mg_event event,
- struct mg_connection *conn,
- const struct mg_request_info *request_info);
-
-
-// Start web server.
-//
-// Parameters:
-// callback: user defined event handling function or NULL.
-// options: NULL terminated list of option_name, option_value pairs that
-// specify Mongoose configuration parameters.
-//
-// Example:
-// const char *options[] = {
-// "document_root", "/var/www",
-// "listening_ports", "80,443s",
-// NULL
-// };
-// struct mg_context *ctx = mg_start(&my_func, options);
-//
-// Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual
-// for the list of valid option and their possible values.
-//
-// Return:
-// web server context, or NULL on error.
-struct mg_context *mg_start(mg_callback_pt callback, const char **options);
-
-
-// Stop the web server.
-//
-// Must be called last, when an application wants to stop the web server and
-// release all associated resources. This function blocks until all Mongoose
-// threads are stopped. Context pointer becomes invalid.
-void mg_stop(struct mg_context *);
-
-
-// Get the value of particular configuration parameter.
-// The value returned is read-only. Mongoose does not allow changing
-// configuration at run time.
-// If given parameter name is not valid, NULL is returned. For valid
-// names, return value is guaranteed to be non-NULL. If parameter is not
-// set, zero-length string is returned.
-const char *mg_get_option(const struct mg_context *ctx, const char *name);
-
-
-// Return array of strings that represent valid configuration options.
-// For each option, a short name, long name, and default value is returned.
-// Array is NULL terminated.
-const char **mg_get_valid_option_names(void);
-
-
-// Add, edit or delete the entry in the passwords file.
-//
-// This function allows an application to manipulate .htpasswd files on the
-// fly by adding, deleting and changing user records. This is one of the
-// several ways of implementing authentication on the server side. For another,
-// cookie-based way please refer to the examples/chat.c in the source tree.
-//
-// If password is not NULL, entry is added (or modified if already exists).
-// If password is NULL, entry is deleted.
-//
-// Return:
-// 1 on success, 0 on error.
-int mg_modify_passwords_file(struct mg_context *ctx,
- const char *passwords_file_name, const char *user, const char *password);
-
-// Send data to the client.
-int mg_write(struct mg_connection *, const void *buf, size_t len);
-
-
-// Send data to the browser using printf() semantics.
-//
-// Works exactly like mg_write(), but allows to do message formatting.
-// Note that mg_printf() uses internal buffer of size IO_BUF_SIZE
-// (8 Kb by default) as temporary message storage for formatting. Do not
-// print data that is bigger than that, otherwise it will be truncated.
-int mg_printf(struct mg_connection *, const char *fmt, ...);
-
-
-// Read data from the remote end, return number of bytes read.
-int mg_read(struct mg_connection *, void *buf, size_t len);
-
-
-// Get the value of particular HTTP header.
-//
-// This is a helper function. It traverses request_info->http_headers array,
-// and if the header is present in the array, returns its value. If it is
-// not present, NULL is returned.
-const char *mg_get_header(const struct mg_connection *, const char *name);
-
-
-// Get a value of particular form variable.
-//
-// Parameters:
-// data: pointer to form-uri-encoded buffer. This could be either POST data,
-// or request_info.query_string.
-// data_len: length of the encoded data.
-// var_name: variable name to decode from the buffer
-// buf: destination buffer for the decoded variable
-// buf_len: length of the destination buffer
-//
-// Return:
-// On success, length of the decoded variable.
-// On error, -1 (variable not found, or destination buffer is too small).
-//
-// Destination buffer is guaranteed to be '\0' - terminated. In case of
-// failure, dst[0] == '\0'.
-int mg_get_var(const char *data, size_t data_len,
- const char *var_name, char *buf, size_t buf_len);
-
-// Fetch value of certain cookie variable into the destination buffer.
-//
-// Destination buffer is guaranteed to be '\0' - terminated. In case of
-// failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
-// parameter. This function returns only first occurrence.
-//
-// Return:
-// On success, value length.
-// On error, -1 (either "Cookie:" header is not present at all, or the
-// requested parameter is not found, or destination buffer is too small
-// to hold the value).
-int mg_get_cookie(const struct mg_connection *,
- const char *cookie_name, char *buf, size_t buf_len);
-
-
-// Return Mongoose version.
-const char *mg_version(void);
-
-
-// MD5 hash given strings.
-// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
-// asciiz strings. When function returns, buf will contain human-readable
-// MD5 hash. Example:
-// char buf[33];
-// mg_md5(buf, "aa", "bb", NULL);
-void mg_md5(char *buf, ...);
-
-
-#ifdef __cplusplus
-}
-#endif // __cplusplus
-
-#endif // MONGOOSE_HEADER_INCLUDED
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/mongoose/private/src/activator.c
----------------------------------------------------------------------
diff --git a/examples/celix-examples/mongoose/private/src/activator.c b/examples/celix-examples/mongoose/private/src/activator.c
deleted file mode 100644
index beea9df..0000000
--- a/examples/celix-examples/mongoose/private/src/activator.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- *Licensed to the Apache Software Foundation (ASF) under one
- *or more contributor license agreements. See the NOTICE file
- *distributed with this work for additional information
- *regarding copyright ownership. The ASF licenses this file
- *to you under the Apache License, Version 2.0 (the
- *"License"); you may not use this file except in compliance
- *with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- *Unless required by applicable law or agreed to in writing,
- *software distributed under the License is distributed on an
- *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- *specific language governing permissions and limitations
- *under the License.
- */
-/*
- * activator.c
- *
- * \date Aug 20, 2010
- * \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- * \copyright Apache License, Version 2.0
- */
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "bundle_activator.h"
-#include "mongoose.h"
-
-struct userData {
- struct mg_context *ctx;
- char* entry;
-};
-
-celix_status_t bundleActivator_create(bundle_context_pt __attribute__((unused)) context, void **userData) {
- *userData = calloc(1, sizeof(struct userData));
- return CELIX_SUCCESS;
-}
-
-celix_status_t bundleActivator_start(void * userData, bundle_context_pt context) {
- bundle_pt bundle;
- celix_status_t status = CELIX_SUCCESS;
- struct userData * data = (struct userData *) userData;
-
- if (bundleContext_getBundle(context, &bundle) == CELIX_SUCCESS) {
- bundle_getEntry(bundle, "root", &data->entry);
- const char *options[] = {
- "document_root", data->entry,
- "listening_ports", "8081",
- NULL
- };
-
- data->ctx = mg_start(NULL, options);
-
- if (data->ctx) {
- printf("Mongoose started on: %s\n", mg_get_option(data->ctx, "listening_ports"));
- }
- } else {
- status = CELIX_START_ERROR;
- }
-
- return status;
-}
-
-celix_status_t bundleActivator_stop(void * userData, bundle_context_pt __attribute__((unused)) context) {
- struct userData * data = (struct userData *) userData;
- mg_stop(data->ctx);
- printf("Mongoose stopped\n");
- return CELIX_SUCCESS;
-}
-
-celix_status_t bundleActivator_destroy(void * userData, bundle_context_pt __attribute__((unused)) context) {
- struct userData * data = (struct userData *) userData;
- free(data->entry);
- free(data);
- return CELIX_SUCCESS;
-}
[6/6] celix git commit: CELIX-451: Adds celix_bundle_dir for copying
files. Also remove mongoose example and adds a new civetweb example.
Posted by pn...@apache.org.
CELIX-451: Adds celix_bundle_dir for copying files. Also remove mongoose example and adds a new civetweb example.
- The celix_bundle_dir also copy files again if they are updated
- The mongoose example was removed, because the updated version of mongoose is now GPL
- The civetweb example also uses a websocket
Project: http://git-wip-us.apache.org/repos/asf/celix/repo
Commit: http://git-wip-us.apache.org/repos/asf/celix/commit/c4de9077
Tree: http://git-wip-us.apache.org/repos/asf/celix/tree/c4de9077
Diff: http://git-wip-us.apache.org/repos/asf/celix/diff/c4de9077
Branch: refs/heads/develop
Commit: c4de9077dce47e13f4a084aa493f3e05a94b8408
Parents: 3f24edf
Author: Pepijn Noltes <pe...@gmail.com>
Authored: Tue Jul 3 22:43:27 2018 +0200
Committer: Pepijn Noltes <pe...@gmail.com>
Committed: Tue Jul 3 22:43:27 2018 +0200
----------------------------------------------------------------------
cmake/cmake_celix/BundlePackaging.cmake | 67 +-
examples/celix-examples/CMakeLists.txt | 2 +-
examples/celix-examples/civetweb/CMakeLists.txt | 37 +
.../celix-examples/civetweb/civetweb/civetweb.c | 19792 +++++++++++++++++
.../celix-examples/civetweb/civetweb/civetweb.h | 1498 ++
.../civetweb/civetweb/handle_form.inl | 981 +
.../celix-examples/civetweb/civetweb/md5.inl | 471 +
.../celix-examples/civetweb/civetweb/sha1.inl | 323 +
.../celix-examples/civetweb/civetweb/timer.inl | 246 +
.../civetweb/resources/index.html | 34 +
.../celix-examples/civetweb/resources/script.js | 43 +
.../civetweb/src/bundle_activator.c | 108 +
examples/celix-examples/mongoose/CMakeLists.txt | 42 -
.../mongoose/private/include/mongoose.h | 218 -
.../mongoose/private/src/activator.c | 79 -
.../mongoose/private/src/mongoose.c | 4076 ----
.../celix-examples/mongoose/root/index.html | 23 -
17 files changed, 23595 insertions(+), 4445 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/cmake/cmake_celix/BundlePackaging.cmake
----------------------------------------------------------------------
diff --git a/cmake/cmake_celix/BundlePackaging.cmake b/cmake/cmake_celix/BundlePackaging.cmake
index 6666d20..35f5c0a 100644
--- a/cmake/cmake_celix/BundlePackaging.cmake
+++ b/cmake/cmake_celix/BundlePackaging.cmake
@@ -340,15 +340,26 @@ function(celix_bundle_libs)
endif()
list(APPEND DEPS ${OUT})
elseif (TARGET ${LIB})
+ get_target_property(TARGET_TYPE ${LIB} TYPE)
#Assuming target
#NOTE add_custom_command does not support generator expression in OUTPUT value (e.g. $<TARGET_FILE:${LIB}>)
#Using a two step approach to be able to use add_custom_command instead of add_custom_target
set(OUT "${BUNDLE_GEN_DIR}/lib-${LIBID}-copy-timestamp")
- add_custom_command(OUTPUT ${OUT}
- COMMAND ${CMAKE_COMMAND} -E touch ${OUT}
- COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:${LIB}>" "${BUNDLE_DIR}/$<TARGET_SONAME_FILE_NAME:${LIB}>"
- DEPENDS ${LIB}
- )
+ if ("${TARGET_TYPE}" STREQUAL "STATIC_LIBRARY")
+ add_custom_command(OUTPUT ${OUT}
+ COMMAND ${CMAKE_COMMAND} -E touch ${OUT}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:${LIB}>" "${BUNDLE_DIR}/$<TARGET_FILE_NAME:${LIB}>"
+ DEPENDS ${LIB}
+ )
+ elseif ("${TARGET_TYPE}" STREQUAL "SHARED_LIBRARY")
+ add_custom_command(OUTPUT ${OUT}
+ COMMAND ${CMAKE_COMMAND} -E touch ${OUT}
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:${LIB}>" "${BUNDLE_DIR}/$<TARGET_SONAME_FILE_NAME:${LIB}>"
+ DEPENDS ${LIB}
+ )
+ else()
+ message(FATAL_ERROR "Unexptected target type (${TARGET_TYPE}) for target ${LIB}. Not a library")
+ endif()
if (ADD_TO_MANIFEST)
list(APPEND LIBS "$<TARGET_SONAME_FILE_NAME:${LIB}>")
endif()
@@ -404,9 +415,9 @@ function(bundle_files)
message(DEPRECATION "bundle_files is deprecated, use celix_bundle_files instead.")
celix_bundle_files(${ARGN})
endfunction()
+#Note with celix_bundle_files, files are copied cmake generation time. Updates are not copied !!
function(celix_bundle_files)
#0 is bundle TARGET
- #1..n is header name / header value
list(GET ARGN 0 BUNDLE)
list(REMOVE_AT ARGN 0)
@@ -427,6 +438,50 @@ function(celix_bundle_files)
file(COPY ${FILES_UNPARSED_ARGUMENTS} DESTINATION ${DESTINATION})
endfunction()
+#Note celix_bundle_dir copies the dir and can track changes.
+function(celix_bundle_dir)
+ #0 is bundle TARGET
+ list(GET ARGN 0 BUNDLE)
+ list(REMOVE_AT ARGN 0)
+
+ #1 is the input dir
+ list(GET ARGN 0 INPUT_DIR)
+ list(REMOVE_AT ARGN 0)
+
+ if (NOT BUNDLE OR NOT INPUT_DIR)
+ message(FATAL_ERROR "celix_bundle_dir must have atleast two arguments: BUNDLE_TARGET and INPUT_DIR!")
+ endif()
+
+ set(OPTIONS )
+ set(ONE_VAL_ARGS DESTINATION)
+ set(MULTI_VAL_ARGS )
+ cmake_parse_arguments(COPY "${OPTIONS}" "${ONE_VAL_ARGS}" "${MULTI_VAL_ARGS}" ${ARGN})
+
+ get_target_property(BUNDLE_DIR ${BUNDLE} "BUNDLE_CONTENT_DIR")
+ if (NOT COPY_DESTINATION)
+ set(DESTINATION "${BUNDLE_DIR}/${FILES_DESTINATION}")
+ else()
+ set(DESTINATION "${BUNDLE_DIR}")
+ endif()
+
+ set(COPY_CMAKE_SCRIPT "${CMAKE_BINARY_DIR}/celix/gen/bundles/${BUNDLE}/copy-${INPUT_DIR}.cmake")
+ file(WRITE ${COPY_CMAKE_SCRIPT}
+ "file(COPY ${CMAKE_CURRENT_LIST_DIR}/${INPUT_DIR} DESTINATION ${DESTINATION})")
+
+ set(TIMESTAMP "${CMAKE_BINARY_DIR}/celix/gen/bundles/${BUNDLE}/copy-${INPUT_DIR}.timestamp")
+ file(GLOB DIR_FILES ${INPUT_DIR})
+ add_custom_command(OUTPUT ${TIMESTAMP}
+ COMMAND ${CMAKE_COMMAND} -E touch ${TIMESTAMP}
+ COMMAND ${CMAKE_COMMAND} -P ${COPY_CMAKE_SCRIPT}
+ DEPENDS ${DIR_FILES}
+ COMMENT "Copying dir ${INPUT_DIR} to ${DESTINATION}"
+ )
+
+ get_target_property(DEPS ${BUNDLE} "BUNDLE_DEPEND_TARGETS")
+ list(APPEND DEPS "${TIMESTAMP}")
+ set_target_properties(${BUNDLE} PROPERTIES "BUNDLE_DEPEND_TARGETS" "${DEPS}")
+endfunction()
+
function(bundle_headers)
message(DEPRECATION "bundle_headers is deprecated, use celix_bundle_headers instead.")
celix_bundle_headers(${ARGN})
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/examples/celix-examples/CMakeLists.txt b/examples/celix-examples/CMakeLists.txt
index f11979f..86ed757 100644
--- a/examples/celix-examples/CMakeLists.txt
+++ b/examples/celix-examples/CMakeLists.txt
@@ -35,7 +35,7 @@ if (EXAMPLES)
add_subdirectory(dm_example_cxx)
if (NOT ANDROID)
- add_subdirectory(mongoose)
+ add_subdirectory(civetweb)
endif()
add_subdirectory(embedding)
add_subdirectory(service_hook_example)
http://git-wip-us.apache.org/repos/asf/celix/blob/c4de9077/examples/celix-examples/civetweb/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/examples/celix-examples/civetweb/CMakeLists.txt b/examples/celix-examples/civetweb/CMakeLists.txt
new file mode 100644
index 0000000..99926f9
--- /dev/null
+++ b/examples/celix-examples/civetweb/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+add_library(civetweb_static STATIC
+ civetweb/civetweb.c
+)
+target_include_directories(civetweb_static PUBLIC SYSTEM civetweb)
+target_compile_options(civetweb_static PRIVATE -Wno-format -Wno-implicit-function-declaration -DUSE_WEBSOCKET)
+
+add_celix_bundle(embedded_civetweb
+ VERSION 1.0.0
+ SOURCES src/bundle_activator.c
+)
+target_link_libraries(embedded_civetweb PRIVATE Celix::shell_api)
+celix_bundle_private_libs(embedded_civetweb civetweb_static)
+celix_bundle_dir(embedded_civetweb resources)
+
+add_celix_container(civetweb_example
+ BUNDLES
+ Celix::shell
+ Celix::shell_tui
+ embedded_civetweb
+)
\ No newline at end of file