1
0
mirror of https://github.com/lammertb/libhttp.git synced 2025-09-04 12:42:09 +03:00

Merge branch 'master' of https://github.com/bel2125/civetweb into unit_test

This commit is contained in:
jmc-
2014-03-18 12:51:47 -07:00
20 changed files with 1241 additions and 378 deletions

View File

@@ -24,6 +24,8 @@ DOCDIR = $(DATAROOTDIR)/doc/$(CPROG)
SYSCONFDIR = $(PREFIX)/etc
HTMLDIR = $(DOCDIR)
UNAME := $(shell uname)
# desired configuration of the document root
# never assume that the document_root actually
# exists on the build machine. When building
@@ -100,6 +102,13 @@ ifeq ($(TARGET_OS),LINUX)
CAN_INSTALL = 1
endif
ifneq (, $(findstring MINGW32, $(UNAME)))
LIBS += -lws2_32 -lcomdlg32
SHARED_LIB=dll
else
SHARED_LIB=so
endif
all: build
help:
@@ -174,7 +183,7 @@ endif
lib: lib$(CPROG).a
slib: lib$(CPROG).so
slib: lib$(CPROG).$(SHARED_LIB)
clean:
rm -rf $(BUILD_DIR)
@@ -182,7 +191,7 @@ clean:
distclean: clean
@rm -rf VS2012/Debug VS2012/*/Debug VS2012/*/*/Debug
@rm -rf VS2012/Release VS2012/*/Release VS2012/*/*/Release
rm -f $(CPROG) lib$(CPROG).so lib$(CPROG).a *.dmg *.msi *.exe
rm -f $(CPROG) lib$(CPROG).so lib$(CPROG).a *.dmg *.msi *.exe lib$(CPROG).dll lib$(CPROG).dll.a
lib$(CPROG).a: $(LIB_OBJECTS)
@rm -f $@
@@ -192,6 +201,10 @@ lib$(CPROG).so: CFLAGS += -fPIC
lib$(CPROG).so: $(LIB_OBJECTS)
$(LCC) -shared -o $@ $(CFLAGS) $(LDFLAGS) $(LIB_OBJECTS)
lib$(CPROG).dll: CFLAGS += -fPIC
lib$(CPROG).dll: $(LIB_OBJECTS)
$(LCC) -shared -o $@ $(CFLAGS) $(LDFLAGS) $(LIB_OBJECTS) $(LIBS) -Wl,--out-implib,lib$(CPROG).dll.a
$(CPROG): $(BUILD_OBJECTS)
$(LCC) -o $@ $(CFLAGS) $(LDFLAGS) $(BUILD_OBJECTS) $(LIBS)

View File

@@ -1,10 +1,20 @@
Release Notes v1.6 (Under Development)
===
### Objectives: *???*
### Objectives: *Enhance Lua support, bug fixes and updates"
Changes
-------
- Allow to specify title and tray icon for the Windows standalone server (bel)
- Fix minor memory leaks (bel)
- Redirect all memory allocation/deallocation through mg functions which may be overwritten (bel)
- Support Cross-Origin Resource Sharing (CORS) for static files and scripts (bel)
- Win32: Replace dll.def file by export macros in civetweb.h (CSTAJ)
- Base64 encode and decode functions for Lua (bel)
- Support pre-loaded files for the Lua environment (bel)
- Server should check the nonce for http digest access authentication (bel)
- Hide read-only flag in file dialogs opened by the Edit Settings dialog for the Windows executable (bel)
- Add all functions to dll.def, that are in the header (bel)
- Added Lua extensions: send_file, get_var, get_mime_type, get_cookie, url_decode, url_encode (bel)
- mg_set_request_handler() mod to use pattern (bel, Patch from Toni Wilk)
- Solved, tested and documented SSL support for Windows (bel)

View File

@@ -88,7 +88,7 @@
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
@@ -102,7 +102,7 @@
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
@@ -118,7 +118,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
@@ -136,7 +136,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>

View File

@@ -316,6 +316,13 @@ Timeout for network read and network write operations, in milliseconds.
If client intends to keep long-running connection, either increase this value
or use keep-alive messages.
### lua_preload_file
This configuration option can be used to specify a Lua script file, which
is executed before the actual web page script (Lua script, Lua server page
or Lua websocket). It can be used to modify the Lua environment of all web
page scripts, e.g., by loading additional libraries required by all scripts
or to achieve backward compatibility by defining obsolete functions.
### lua_script_pattern `"**.lua$`
A pattern for files that are interpreted as Lua scripts by the server.
In contrast to Lua server pages, Lua scripts use plain Lua syntax.
@@ -407,6 +414,8 @@ mg (table):
mg.send_file(filename) -- send a file, including MIME type
mg.url_encode(str) -- URL encode a string
mg.url_decode(str) -- URL decode a string
mg.base64_encode(str) -- BASE64 encode a string
mg.base64_decode(str) -- BASE64 decode a string
mg.md5(str) -- return the MD5 hash of a string
mg.keep_alive(bool) -- allow/forbid to use http keep-alive for this request
mg.request_info -- a table with the following request information

View File

@@ -3,6 +3,10 @@
#include <time.h>
#include "WebSockCallbacks.h"
#ifdef __APPLE__
#include <string.h>
#endif
#ifdef _WIN32
#include <Windows.h>
typedef HANDLE pthread_mutex_t;
@@ -73,7 +77,7 @@ void websocket_ready_handler(struct mg_connection *conn) {
break;
}
}
printf("\nNew websocket attached: %08x:%u\n", rq->remote_ip, rq->remote_port);
printf("\nNew websocket attached: %08lx:%u\n", rq->remote_ip, rq->remote_port);
pthread_mutex_unlock(&sMutex);
}
@@ -88,7 +92,7 @@ static void websocket_done(tWebSockInfo * wsock) {
break;
}
}
printf("\nClose websocket attached: %08x:%u\n", mg_get_request_info(wsock->conn)->remote_ip, mg_get_request_info(wsock->conn)->remote_port);
printf("\nClose websocket attached: %08lx:%u\n", mg_get_request_info(wsock->conn)->remote_ip, mg_get_request_info(wsock->conn)->remote_port);
free(wsock);
}
}
@@ -107,7 +111,7 @@ int websocket_data_handler(struct mg_connection *conn, int flags, char *data, si
pthread_mutex_unlock(&sMutex);
return 1;
}
if ((data_len>=5) && (data_len<100) && (flags==129) || (flags==130)) {
if (((data_len>=5) && (data_len<100) && (flags==129)) || (flags==130)) {
// init command
if ((wsock->webSockState==1) && (!memcmp(data,"init ",5))) {

View File

@@ -1,4 +1,5 @@
/* Copyright (c) 2004-2013 Sergey Lyubka
/* Copyright (c) 2013-2014 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
@@ -26,6 +27,20 @@
#define CIVETWEB_VERSION "1.6"
#endif
#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
#else
#define CIVETWEB_API
#endif
#endif
#include <stdio.h>
#include <stddef.h>
@@ -140,6 +155,7 @@ struct mg_callbacks {
int (*http_error)(struct mg_connection *, int status);
};
/* Start web server.
Parameters:
@@ -165,7 +181,7 @@ struct mg_callbacks {
Return:
web server context, or NULL on error. */
struct mg_context *mg_start(const struct mg_callbacks *callbacks,
CIVETWEB_API struct mg_context *mg_start(const struct mg_callbacks *callbacks,
void *user_data,
const char **configuration_options);
@@ -175,7 +191,8 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks,
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. */
void mg_stop(struct mg_context *);
CIVETWEB_API void mg_stop(struct mg_context *);
/* mg_request_handler
@@ -190,6 +207,7 @@ void mg_stop(struct mg_context *);
1: the handler processed the request. */
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.
@@ -206,7 +224,7 @@ typedef int (* mg_request_handler)(struct mg_connection *conn, void *cbdata);
handler: the callback handler to use when the URI is requested.
If NULL, the URI will be removed.
cbdata: the callback data to give to the handler when it s requested. */
void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata);
CIVETWEB_API void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_handler handler, void *cbdata);
/* Get the value of particular configuration parameter.
@@ -215,14 +233,41 @@ void mg_set_request_handler(struct mg_context *ctx, const char *uri, mg_request_
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);
CIVETWEB_API const char *mg_get_option(const struct mg_context *ctx, const char *name);
#if defined(MG_LEGACY_INTERFACE)
/* 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. */
const char **mg_get_valid_option_names(void);
/* 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;
};
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
};
/* 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);
/* Get the list of ports that civetweb is listening on.
size is the size of the ports int array and ssl int array to fill.
@@ -231,7 +276,8 @@ const char **mg_get_valid_option_names(void);
Return value is the number of ports and ssl information filled in.
The value returned is read-only. Civetweb does not allow changing
configuration at run time. */
size_t mg_get_ports(const struct mg_context *ctx, size_t size, int* ports, int* ssl);
CIVETWEB_API size_t mg_get_ports(const struct mg_context *ctx, size_t size, int* ports, int* ssl);
/* Add, edit or delete the entry in the passwords file.
@@ -245,14 +291,14 @@ size_t mg_get_ports(const struct mg_context *ctx, size_t size, int* ports, int*
Return:
1 on success, 0 on error. */
int mg_modify_passwords_file(const char *passwords_file_name,
CIVETWEB_API int mg_modify_passwords_file(const char *passwords_file_name,
const char *domain,
const char *user,
const char *password);
/* Return information associated with the request. */
struct mg_request_info *mg_get_request_info(struct mg_connection *);
CIVETWEB_API struct mg_request_info *mg_get_request_info(struct mg_connection *);
/* Send data to the client.
@@ -260,7 +306,7 @@ struct mg_request_info *mg_get_request_info(struct mg_connection *);
0 when the connection has been closed
-1 on error
>0 number of bytes written on success */
int mg_write(struct mg_connection *, const void *buf, size_t len);
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
@@ -275,16 +321,18 @@ int mg_write(struct mg_connection *, const void *buf, size_t len);
0 when the connection has been closed
-1 on error
>0 number of bytes written on success */
int mg_websocket_write(struct mg_connection* conn, int opcode,
CIVETWEB_API int mg_websocket_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. */
void mg_lock(struct mg_connection* conn);
void mg_unlock(struct mg_connection* conn);
CIVETWEB_API void mg_lock(struct mg_connection* conn);
CIVETWEB_API void mg_unlock(struct mg_connection* conn);
/* Opcodes, from http://tools.ietf.org/html/rfc6455 */
enum {
@@ -317,14 +365,13 @@ enum {
#endif
/* Send data to the client using printf() semantics.
Works exactly like mg_write(), but allows to do message formatting. */
int mg_printf(struct mg_connection *,
CIVETWEB_API int mg_printf(struct mg_connection *,
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
/* Send contents of the entire file together with HTTP headers. */
void mg_send_file(struct mg_connection *conn, const char *path);
CIVETWEB_API void mg_send_file(struct mg_connection *conn, const char *path);
/* Read data from the remote end, return number of bytes read.
@@ -332,7 +379,7 @@ void mg_send_file(struct mg_connection *conn, const char *path);
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. */
int mg_read(struct mg_connection *, void *buf, size_t len);
CIVETWEB_API int mg_read(struct mg_connection *, void *buf, size_t len);
/* Get the value of particular HTTP header.
@@ -340,7 +387,7 @@ int mg_read(struct mg_connection *, void *buf, size_t len);
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);
CIVETWEB_API const char *mg_get_header(const struct mg_connection *, const char *name);
/* Get a value of particular form variable.
@@ -362,9 +409,10 @@ const char *mg_get_header(const struct mg_connection *, const char *name);
Destination buffer is guaranteed to be '\0' - terminated if it is not
NULL or zero length. */
int mg_get_var(const char *data, size_t data_len,
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:
@@ -388,9 +436,10 @@ int mg_get_var(const char *data, size_t data_len,
Destination buffer is guaranteed to be '\0' - terminated if it is not
NULL or zero length. */
int mg_get_var2(const char *data, size_t data_len,
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
@@ -404,7 +453,7 @@ int mg_get_var2(const char *data, size_t data_len,
parameter is not found).
-2 (destination buffer is NULL, zero length or too small to hold the
value). */
int mg_get_cookie(const char *cookie, const char *var_name,
CIVETWEB_API int mg_get_cookie(const char *cookie, const char *var_name,
char *buf, size_t buf_len);
@@ -423,35 +472,36 @@ int mg_get_cookie(const char *cookie, const char *var_name,
conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
"%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
*/
struct mg_connection *mg_download(const char *host, int port, int use_ssl,
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(). */
void mg_close_connection(struct mg_connection *conn);
CIVETWEB_API void mg_close_connection(struct mg_connection *conn);
/* File upload functionality. Each uploaded file gets saved into a temporary
file and MG_UPLOAD event is sent.
Return number of uploaded files. */
int mg_upload(struct mg_connection *conn, const char *destination_dir);
CIVETWEB_API int mg_upload(struct mg_connection *conn, const char *destination_dir);
/* Convenience function -- create detached thread.
Return: 0 on success, non-0 on error. */
typedef void * (*mg_thread_func_t)(void *);
int mg_start_thread(mg_thread_func_t f, void *p);
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. */
const char *mg_get_builtin_mime_type(const char *file_name);
CIVETWEB_API const char *mg_get_builtin_mime_type(const char *file_name);
/* Return Civetweb version. */
const char *mg_version(void);
CIVETWEB_API const char *mg_version(void);
/* URL-decode input buffer into destination buffer.
0-terminate the destination buffer.
@@ -459,13 +509,15 @@ const char *mg_version(void);
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. */
int mg_url_decode(const char *src, int src_len, char *dst,
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. */
int mg_url_encode(const char *src, char *dst, size_t dst_len);
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
@@ -473,7 +525,7 @@ int mg_url_encode(const char *src, char *dst, size_t dst_len);
MD5 hash. Example:
char buf[33];
mg_md5(buf, "aa", "bb", NULL); */
char *mg_md5(char buf[33], ...);
CIVETWEB_API char *mg_md5(char buf[33], ...);
/* Print error message to the opened error log stream.
@@ -483,11 +535,13 @@ char *mg_md5(char buf[33], ...);
...: variable argument list
Example:
mg_cry(conn,"i like %s", "logging"); */
void mg_cry(struct mg_connection *conn,
CIVETWEB_API void mg_cry(struct mg_connection *conn,
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
/* utility method to compare two buffers, case incensitive. */
int mg_strncasecmp(const char *s1, const char *s2, size_t len);
CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len);
#ifdef __cplusplus
}

View File

@@ -1,15 +0,0 @@
LIBRARY
EXPORTS
mg_start
mg_stop
mg_read
mg_write
mg_printf
mg_get_header
mg_get_var
mg_get_cookie
mg_get_option
mg_get_valid_option_names
mg_version
mg_modify_passwords_file
mg_md5

View File

@@ -150,8 +150,19 @@ bool
CivetServer::getParam(struct mg_connection *conn, const char *name,
std::string &dst, size_t occurrence)
{
const char *query = mg_get_request_info(conn)->query_string;
return getParam(query, strlen(query), name, dst, occurrence);
const char *formParams = NULL;
mg_request_info * ri = mg_get_request_info(conn);
if (ri) {
// get requests do store html <form> field values in the http query_string
formParams = ri->query_string;
}
if (formParams) {
return getParam(formParams, strlen(formParams), name, dst, occurrence);
}
return false;
}
bool

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
/* Copyright (c) 2004-2013 Sergey Lyubka
/* Copyright (c) 2013-2014 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
@@ -39,6 +40,7 @@
#include <stddef.h>
#include <stdarg.h>
#include <ctype.h>
#include <assert.h>
#include "civetweb.h"
@@ -76,9 +78,10 @@ extern char *_getcwd(char *buf, size_t size);
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
static int exit_flag;
static char server_name[40]; /* Set by init_server_name() */
static char config_file[PATH_MAX] = ""; /* Set by
process_command_line_arguments() */
static char server_base_name[40]; /* Set by init_server_name() */
static char *server_name; /* Set by init_server_name() */
static char *icon_name; /* Set by init_server_name() */
static char config_file[PATH_MAX] = ""; /* Set by process_command_line_arguments() */
static struct mg_context *ctx; /* Set by start_civetweb() */
#if !defined(CONFIG_FILE)
@@ -90,6 +93,18 @@ static struct mg_context *ctx; /* Set by start_civetweb() */
#define CONFIG_FILE2 "/usr/local/etc/civetweb.conf"
#endif
enum {
OPTION_TITLE,
OPTION_ICON,
NUM_MAIN_OPTIONS
};
static struct mg_option main_config_options[] = {
{"title", CONFIG_TYPE_STRING, NULL},
{"icon", CONFIG_TYPE_STRING, NULL},
{NULL, CONFIG_TYPE_UNKNOWN, NULL}
};
static void WINCDECL signal_handler(int sig_num)
{
exit_flag = sig_num;
@@ -116,9 +131,18 @@ static void die(const char *fmt, ...)
static void show_usage_and_exit(void)
{
const char **names;
const struct mg_option *options;
int i;
#ifdef WIN32
if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
AllocConsole();
AttachConsole(GetCurrentProcessId());
}
freopen("CON", "a", stdout);
freopen("CON", "a", stderr);
#endif
fprintf(stderr, "Civetweb v%s, built on %s\n",
mg_version(), __DATE__);
fprintf(stderr, "Usage:\n");
@@ -127,11 +151,16 @@ static void show_usage_and_exit(void)
fprintf(stderr, " civetweb [-option value ...]\n");
fprintf(stderr, "\nOPTIONS:\n");
names = mg_get_valid_option_names();
for (i = 0; names[i] != NULL; i += 2) {
fprintf(stderr, " -%s %s\n",
names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
options = mg_get_valid_options();
for (i = 0; options[i].name != NULL; i++) {
fprintf(stderr, " -%s %s\n", options[i].name, ((options[i].default_value == NULL) ? "<empty>" : options[i].default_value));
}
options = main_config_options;
for (i = 0; options[i].name != NULL; i++) {
fprintf(stderr, " -%s %s\n", options[i].name, ((options[i].default_value == NULL) ? "<empty>" : options[i].default_value));
}
exit(EXIT_FAILURE);
}
@@ -165,7 +194,8 @@ static const char *get_url_to_first_open_port(const struct mg_context *ctx)
static void create_config_file(const char *path)
{
const char **names, *value;
const struct mg_option *options;
const char *value;
FILE *fp;
int i;
@@ -174,10 +204,10 @@ static void create_config_file(const char *path)
fclose(fp);
} else if ((fp = fopen(path, "a+")) != NULL) {
fprintf(fp, "%s", config_file_top_comment);
names = mg_get_valid_option_names();
for (i = 0; names[i * 2] != NULL; i++) {
value = mg_get_option(ctx, names[i * 2]);
fprintf(fp, "# %s %s\n", names[i * 2], value ? value : "<value>");
options = mg_get_valid_options();
for (i = 0; options[i].name != NULL; i++) {
value = mg_get_option(ctx, options[i].name);
fprintf(fp, "# %s %s\n", options[i].name, value ? value : "<value>");
}
fclose(fp);
}
@@ -196,9 +226,41 @@ static char *sdup(const char *str)
return p;
}
static void set_option(char **options, const char *name, const char *value)
static int set_option(char **options, const char *name, const char *value)
{
int i;
int i, type;
const struct mg_option *default_options = mg_get_valid_options();
for (i = 0; main_config_options[i].name != 0; i++) {
if (0==strcmp(name, main_config_options[i].name)) {
/* This option is evaluated by main.c, not civetweb.c - just skip it and return OK */
return 1;
}
}
type = CONFIG_TYPE_UNKNOWN;
for (i = 0; default_options[i].name != 0; i++) {
if (!strcmp(default_options[i].name, name)) {
type = default_options[i].type;
}
}
switch (type) {
case CONFIG_TYPE_UNKNOWN:
/* unknown option */
return 0;
case CONFIG_TYPE_NUMBER:
if (atol(value)<1) {
/* invalid number */
return 0;
}
break;
case CONFIG_TYPE_BOOLEAN:
if ((0!=strcmp(value,"yes")) && (0!=strcmp(value,"no"))) {
/* invalid boolean */
return 0;
}
break;
}
for (i = 0; i < MAX_OPTIONS - 3; i++) {
if (options[i] == NULL) {
@@ -216,14 +278,16 @@ static void set_option(char **options, const char *name, const char *value)
if (i == MAX_OPTIONS - 3) {
die("%s", "Too many options specified");
}
}
/* TODO: check if this option is defined and the correct data type, return 1 (OK) or 0 (false) */
return 1;
}
static void read_config_file(const char *config_file, char **options)
{
char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], *p;
char line[MAX_CONF_FILE_LINE_SIZE], *p;
FILE *fp = NULL;
size_t i, cmd_line_opts_start = 1, line_no = 0;
size_t i, j, cmd_line_opts_start = 1, line_no = 0;
fp = fopen(config_file, "r");
@@ -253,11 +317,23 @@ static void read_config_file(const char *config_file, char **options)
continue;
}
if (sscanf(p, "%s %[^\r\n#]", opt, val) != 2) {
/* Skip spaces, \r and \n at the end of the line */
for (j = strlen(line)-1; isspace(* (unsigned char *) &line[j]) || iscntrl(* (unsigned char *) &line[j]); ) line[j--]=0;
/* Find the space character between option name and value */
for (j=i; !isspace(* (unsigned char *) &line[j]) && (line[j]!=0); ) j++;
/* Terminate the string - then the string at (line+i) contains the option name */
line[j] = 0;
j++;
/* Trim additional spaces between option name and value - then (line+j) contains the option value */
while (isspace(line[j])) j++;
/* Set option */
if (!set_option(options, line+i, line+j)) {
printf("%s: line %d is invalid, ignoring it:\n %s",
config_file, (int) line_no, p);
} else {
set_option(options, opt, val);
}
}
@@ -265,7 +341,6 @@ static void read_config_file(const char *config_file, char **options)
}
}
static void process_command_line_arguments(char *argv[], char **options)
{
char *p;
@@ -314,15 +389,34 @@ static void process_command_line_arguments(char *argv[], char **options)
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
show_usage_and_exit();
}
set_option(options, &argv[i][1], argv[i + 1]);
if (!set_option(options, &argv[i][1], argv[i + 1])) {
printf("command line option is invalid, ignoring it:\n %s %s\n",
argv[i], argv[i + 1]);
}
}
}
}
static void init_server_name(void)
static void init_server_name(int argc, const char *argv[])
{
snprintf(server_name, sizeof(server_name), "Civetweb v%s",
int i;
assert(sizeof(main_config_options)/sizeof(main_config_options[0]) == NUM_MAIN_OPTIONS+1);
assert((strlen(mg_version())+12)<sizeof(server_base_name));
snprintf(server_base_name, sizeof(server_base_name), "Civetweb V%s",
mg_version());
server_name = server_base_name;
for (i=0; i<argc-1; i++) {
if ((argv[i][0]=='-') && (0==strcmp(argv[i]+1, main_config_options[OPTION_TITLE].name))) {
server_name = (char*)(argv[i+1]);
}
}
icon_name = 0;
for (i=0; i<argc-1; i++) {
if ((argv[i][0]=='-') && (0==strcmp(argv[i]+1, main_config_options[OPTION_ICON].name))) {
icon_name = (char*)(argv[i+1]);
}
}
}
static int log_message(const struct mg_connection *conn, const char *message)
@@ -448,12 +542,18 @@ static void start_civetweb(int argc, char *argv[])
set_absolute_path(options, "access_log_file", argv[0]);
set_absolute_path(options, "error_log_file", argv[0]);
set_absolute_path(options, "global_auth_file", argv[0]);
#ifdef USE_LUA
set_absolute_path(options, "lua_preload_file", argv[0]);
#endif
set_absolute_path(options, "ssl_certificate", argv[0]);
/* Make extra verification for certain options */
verify_existence(options, "document_root", 1);
verify_existence(options, "cgi_interpreter", 0);
verify_existence(options, "ssl_certificate", 0);
#ifdef USE_LUA
verify_existence(options, "lua_preload_file", 0);
#endif
/* Setup signal handler: quit on Ctrl-C */
signal(SIGTERM, signal_handler);
@@ -541,54 +641,28 @@ static void *align(void *ptr, DWORD alig)
return ((void *) ul);
}
static int is_boolean_option(const char *option_name)
{
return !strcmp(option_name, "enable_directory_listing") ||
!strcmp(option_name, "enable_keep_alive");
}
static int is_filename_option(const char *option_name)
{
return !strcmp(option_name, "cgi_interpreter") ||
!strcmp(option_name, "global_auth_file") ||
!strcmp(option_name, "put_delete_auth_file") ||
!strcmp(option_name, "access_log_file") ||
!strcmp(option_name, "error_log_file") ||
!strcmp(option_name, "ssl_certificate");
}
static int is_directory_option(const char *option_name)
{
return !strcmp(option_name, "document_root");
}
static int is_numeric_options(const char *option_name)
{
return !strcmp(option_name, "num_threads");
}
static void save_config(HWND hDlg, FILE *fp)
{
char value[2000] = "";
const char **options, *name, *default_value;
const char *default_value;
const struct mg_option *options;
int i, id;
fprintf(fp, "%s", config_file_top_comment);
options = mg_get_valid_option_names();
for (i = 0; options[i * 2] != NULL; i++) {
name = options[i * 2];
options = mg_get_valid_options();
for (i = 0; options[i].name != NULL; i++) {
id = ID_CONTROLS + i;
if (is_boolean_option(name)) {
if (options[i].type == CONFIG_TYPE_BOOLEAN) {
snprintf(value, sizeof(value)-1, "%s",
IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
value[sizeof(value)-1] = 0;
} else {
GetDlgItemText(hDlg, id, value, sizeof(value));
}
default_value = options[i * 2 + 1] == NULL ? "" : options[i * 2 + 1];
default_value = options[i].default_value == NULL ? "" : options[i].default_value;
/* If value is the same as default, skip it */
if (strcmp(value, default_value) != 0) {
fprintf(fp, "%s %s\n", name, value);
fprintf(fp, "%s %s\n", options[i].name, value);
}
}
}
@@ -598,8 +672,9 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP)
FILE *fp;
int i, j;
const char *name, *value;
const char **default_options = mg_get_valid_option_names();
const struct mg_option *default_options = mg_get_valid_options();
char *file_options[MAX_OPTIONS] = {0};
char *title;
switch (msg) {
case WM_CLOSE:
@@ -608,6 +683,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP)
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_SAVE:
EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE);
if ((fp = fopen(config_file, "w+")) != NULL) {
@@ -618,11 +694,12 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP)
}
EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
break;
case ID_RESET_DEFAULTS:
for (i = 0; default_options[i * 2] != NULL; i++) {
name = default_options[i * 2];
value = default_options[i * 2 + 1] == NULL ? "" : default_options[i * 2 + 1];
if (is_boolean_option(name)) {
for (i = 0; default_options[i].name != NULL; i++) {
name = default_options[i].name;
value = default_options[i].default_value == NULL ? "" : default_options[i].default_value;
if (default_options[i].type == CONFIG_TYPE_BOOLEAN) {
CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ?
BST_CHECKED : BST_UNCHECKED);
} else {
@@ -630,11 +707,12 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP)
}
}
break;
case ID_RESET_FILE:
read_config_file(config_file, file_options);
for (i = 0; default_options[i * 2] != NULL; i++) {
name = default_options[i * 2];
value = default_options[i * 2 + 1];
for (i = 0; default_options[i].name != NULL; i++) {
name = default_options[i].name;
value = default_options[i].default_value;
for (j = 0; file_options[j * 2] != NULL; j++) {
if (!strcmp(name, file_options[j * 2])) {
value = file_options[j * 2 + 1];
@@ -643,7 +721,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP)
if (value == NULL) {
value = "";
}
if (is_boolean_option(name)) {
if (default_options[i].type == CONFIG_TYPE_BOOLEAN) {
CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ? BST_CHECKED : BST_UNCHECKED);
} else {
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), value);
@@ -653,11 +731,12 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP)
free(file_options[i]);
}
break;
case ID_RESET_ACTIVE:
for (i = 0; default_options[i * 2] != NULL; i++) {
name = default_options[i * 2];
for (i = 0; default_options[i].name != NULL; i++) {
name = default_options[i].name;
value = mg_get_option(ctx, name);
if (is_boolean_option(name)) {
if (default_options[i].type == CONFIG_TYPE_BOOLEAN) {
CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ?
BST_CHECKED : BST_UNCHECKED);
} else {
@@ -667,9 +746,9 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP)
break;
}
for (i = 0; default_options[i * 2] != NULL; i++) {
name = default_options[i * 2];
if ((is_filename_option(name) || is_directory_option(name)) &&
for (i = 0; default_options[i].name != NULL; i++) {
name = default_options[i].name;
if (((default_options[i].type == CONFIG_TYPE_FILE) || (default_options[i].type == CONFIG_TYPE_DIRECTORY)) &&
LOWORD(wParam) == ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA) {
OPENFILENAME of;
BROWSEINFO bi;
@@ -681,14 +760,14 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP)
of.lpstrFile = path;
of.nMaxFile = sizeof(path);
of.lpstrInitialDir = mg_get_option(ctx, "document_root");
of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR;
of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
memset(&bi, 0, sizeof(bi));
bi.hwndOwner = (HWND) hDlg;
bi.lpszTitle = "Choose WWW root directory:";
bi.ulFlags = BIF_RETURNONLYFSDIRS;
if (is_directory_option(name)) {
if (default_options[i].type == CONFIG_TYPE_DIRECTORY) {
SHGetPathFromIDList(SHBrowseForFolder(&bi), path);
} else {
GetOpenFileName(&of);
@@ -699,18 +778,23 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP)
}
}
}
break;
case WM_INITDIALOG:
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_SMALL, (LPARAM) hIcon);
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_BIG, (LPARAM) hIcon);
SetWindowText(hDlg, "Civetweb settings");
title = malloc(strlen(server_name)+16);
if (title) {
strcpy(title, server_name);
strcat(title, " settings");
SetWindowText(hDlg, title);
free(title);
}
SetFocus(GetDlgItem(hDlg, ID_SAVE));
for (i = 0; default_options[i * 2] != NULL; i++) {
name = default_options[i * 2];
for (i = 0; default_options[i].name != NULL; i++) {
name = default_options[i].name;
value = mg_get_option(ctx, name);
if (is_boolean_option(name)) {
if (default_options[i].type == CONFIG_TYPE_BOOLEAN) {
CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ?
BST_CHECKED : BST_UNCHECKED);
} else {
@@ -718,6 +802,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP)
}
}
break;
default:
break;
}
@@ -762,15 +847,15 @@ static void add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type,
static void show_settings_dialog()
{
#define HEIGHT 15
#define WIDTH 400
#define LABEL_WIDTH 80
#define WIDTH 460
#define LABEL_WIDTH 90
unsigned char mem[4096], *p;
const char **option_names, *long_option_name;
const struct mg_option *options;
DWORD style;
DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
WORD i, cl, x, y, width, nelems = 0;
static int guard;
static int guard = 0; /* test if dialog is already open */
static struct {
DLGTEMPLATE template; /* 18 bytes */
@@ -795,22 +880,21 @@ static void show_settings_dialog()
(void) memcpy(mem, &dialog_header, sizeof(dialog_header));
p = mem + sizeof(dialog_header);
option_names = mg_get_valid_option_names();
for (i = 0; option_names[i * 2] != NULL; i++) {
long_option_name = option_names[i * 2];
options = mg_get_valid_options();
for (i = 0; options[i].name != NULL; i++) {
style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
x = 10 + (WIDTH / 2) * (nelems % 2);
y = (nelems/2 + 1) * HEIGHT + 5;
width = WIDTH / 2 - 20 - LABEL_WIDTH;
if (is_numeric_options(long_option_name)) {
if (options[i].type == CONFIG_TYPE_NUMBER) {
style |= ES_NUMBER;
cl = 0x81;
style |= WS_BORDER | ES_AUTOHSCROLL;
} else if (is_boolean_option(long_option_name)) {
} else if (options[i].type == CONFIG_TYPE_BOOLEAN) {
cl = 0x80;
style |= BS_AUTOCHECKBOX;
} else if (is_filename_option(long_option_name) ||
is_directory_option(long_option_name)) {
} else if ((options[i].type == CONFIG_TYPE_FILE) ||
(options[i].type == CONFIG_TYPE_DIRECTORY)) {
style |= WS_BORDER | ES_AUTOHSCROLL;
width -= 20;
cl = 0x81;
@@ -824,10 +908,12 @@ static void show_settings_dialog()
style |= WS_BORDER | ES_AUTOHSCROLL;
}
add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
x, y, LABEL_WIDTH, HEIGHT, long_option_name);
x, y, LABEL_WIDTH, HEIGHT, options[i].name);
add_control(&p, dia, cl, ID_CONTROLS + i, style,
(WORD) (x + LABEL_WIDTH), y, width, 12, "");
nelems++;
assert((int)p - (int)mem < sizeof(mem));
}
y = (WORD) (((nelems + 1) / 2 + 1) * HEIGHT + 5);
@@ -848,7 +934,9 @@ static void show_settings_dialog()
WIDTH - 280, y, 65, 12, "Reload active");
add_control(&p, dia, 0x82, ID_STATIC,
WS_CHILD | WS_VISIBLE | WS_DISABLED,
5, y, 100, 12, server_name);
5, y, 100, 12, server_base_name);
assert((int)p - (int)mem < sizeof(mem));
dia->cy = ((nelems + 1) / 2 + 1) * HEIGHT + 30;
DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) NULL);
@@ -857,7 +945,7 @@ static void show_settings_dialog()
static int manage_service(int action)
{
static const char *service_name = "Civetweb";
static const char *service_name = "Civetweb"; /* TODO: check using server_name instead of service_name */
SC_HANDLE hSCM = NULL, hService = NULL;
SERVICE_DESCRIPTION descr = {server_name};
char path[PATH_MAX + 20] = "";/* Path to executable plus magic argument */
@@ -902,16 +990,16 @@ static int manage_service(int action)
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
LPARAM lParam)
{
static SERVICE_TABLE_ENTRY service_table[] = {
{server_name, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{NULL, NULL}
};
static SERVICE_TABLE_ENTRY service_table[2] = {0};
int service_installed;
char buf[200], *service_argv[] = {__argv[0], NULL};
POINT pt;
HMENU hMenu;
static UINT s_uTaskbarRestart; /* for taskbar creation */
service_table[0].lpServiceName = server_name;
service_table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
switch (msg) {
case WM_CREATE:
if (__argv[1] != NULL &&
@@ -964,7 +1052,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
ID_REMOVE_SERVICE, "Deinstall service");
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
AppendMenu(hMenu, MF_STRING, ID_CONNECT, "Start browser");
AppendMenu(hMenu, MF_STRING, ID_SETTINGS, "Edit Settings");
AppendMenu(hMenu, MF_STRING, ID_SETTINGS, "Edit settings");
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
AppendMenu(hMenu, MF_STRING, ID_QUIT, "Exit");
GetCursorPos(&pt);
@@ -994,23 +1082,27 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show)
HWND hWnd;
MSG msg;
init_server_name();
init_server_name(__argc, __argv);
memset(&cls, 0, sizeof(cls));
cls.lpfnWndProc = (WNDPROC) WindowProc;
cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
cls.lpszClassName = server_name;
cls.lpszClassName = server_base_name;
RegisterClass(&cls);
hWnd = CreateWindow(cls.lpszClassName, server_name, WS_OVERLAPPEDWINDOW,
0, 0, 0, 0, NULL, NULL, NULL, NULL);
ShowWindow(hWnd, SW_HIDE);
if (icon_name) {
hIcon = LoadImage(NULL, icon_name, IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
} else {
hIcon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON), IMAGE_ICON, 16, 16, 0);
}
TrayIcon.cbSize = sizeof(TrayIcon);
TrayIcon.uID = ID_ICON;
TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
TrayIcon.hIcon = hIcon = LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(ID_ICON),
IMAGE_ICON, 16, 16, 0);
TrayIcon.hIcon = hIcon;
TrayIcon.hWnd = hWnd;
snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", server_name);
TrayIcon.uCallbackMessage = WM_USER;
@@ -1052,7 +1144,7 @@ withApplication:@"TextEdit"];
int main(int argc, char *argv[])
{
init_server_name();
init_server_name(argc, argv);
start_civetweb(argc, argv);
[NSAutoreleasePool new];
@@ -1112,7 +1204,7 @@ int main(int argc, char *argv[])
#else
int main(int argc, char *argv[])
{
init_server_name();
init_server_name(argc, argv);
start_civetweb(argc, argv);
printf("%s started on port(s) %s with web root [%s]\n",
server_name, mg_get_option(ctx, "listening_ports"),

View File

@@ -559,6 +559,70 @@ static int lsp_url_decode(lua_State *L)
return 1;
}
/* mg.base64_encode */
static int lsp_base64_encode(lua_State *L)
{
int num_args = lua_gettop(L);
const char *text;
size_t text_len;
char *dst;
if (num_args==1) {
text = lua_tolstring(L, 1, &text_len);
if (text) {
dst = mg_malloc(text_len*8/6+4);
if (dst) {
base64_encode(text, text_len, dst);
lua_pushstring(L, dst);
mg_free(dst);
} else {
return luaL_error(L, "out of memory in base64_encode() call");
}
} else {
lua_pushnil(L);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid base64_encode() call");
}
return 1;
}
/* mg.base64_encode */
static int lsp_base64_decode(lua_State *L)
{
int num_args = lua_gettop(L);
const char *text;
size_t text_len, dst_len;
int ret;
char *dst;
if (num_args==1) {
text = lua_tolstring(L, 1, &text_len);
if (text) {
dst = mg_malloc(text_len);
if (dst) {
ret = base64_decode(text, text_len, dst, &dst_len);
if (ret != -1) {
mg_free(dst);
return luaL_error(L, "illegal character in lsp_base64_decode() call");
} else {
lua_pushlstring(L, dst, dst_len);
mg_free(dst);
}
} else {
return luaL_error(L, "out of memory in lsp_base64_decode() call");
}
} else {
lua_pushnil(L);
}
} else {
/* Syntax error */
return luaL_error(L, "invalid lsp_base64_decode() call");
}
return 1;
}
/* mg.write for websockets */
static int lwebsock_write(lua_State *L)
{
@@ -605,6 +669,7 @@ static void prepare_lua_environment(struct mg_connection *conn, lua_State *L, co
{
const struct mg_request_info *ri = mg_get_request_info(conn);
char src_addr[IP_ADDR_STR_LEN];
const char * preload_file = conn->ctx->config[LUA_PRELOAD_FILE];
int i;
extern void luaL_openlibs(lua_State *);
@@ -676,6 +741,8 @@ static void prepare_lua_environment(struct mg_connection *conn, lua_State *L, co
reg_function(L, "md5", lsp_md5, conn);
reg_function(L, "url_encode", lsp_url_encode, conn);
reg_function(L, "url_decode", lsp_url_decode, conn);
reg_function(L, "base64_encode", lsp_base64_encode, conn);
reg_function(L, "base64_decode", lsp_base64_decode, conn);
reg_string(L, "version", CIVETWEB_VERSION);
reg_string(L, "document_root", conn->ctx->config[DOCUMENT_ROOT]);
@@ -684,6 +751,10 @@ static void prepare_lua_environment(struct mg_connection *conn, lua_State *L, co
reg_string(L, "websocket_root", conn->ctx->config[WEBSOCKET_ROOT]);
#endif
if (conn->ctx->systemName) {
reg_string(L, "system", conn->ctx->systemName);
}
/* Export request_info */
lua_pushstring(L, "request_info");
lua_newtable(L);
@@ -719,6 +790,11 @@ static void prepare_lua_environment(struct mg_connection *conn, lua_State *L, co
/* Register default mg.onerror function */
IGNORE_UNUSED_RESULT(luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
"debug.traceback(e, 1)) end"));
/* Preload */
if ((preload_file != NULL) && (*preload_file != 0)) {
IGNORE_UNUSED_RESULT(luaL_dofile(L, preload_file));
}
}
static int lua_error_handler(lua_State *L)
@@ -741,6 +817,17 @@ static int lua_error_handler(lua_State *L)
return 0;
}
static void * lua_allocator(void *ud, void *ptr, size_t osize, size_t nsize) {
(void)ud; (void)osize; /* not used */
if (nsize == 0) {
mg_free(ptr);
return NULL;
}
return mg_realloc(ptr, nsize);
}
void mg_exec_lua_script(struct mg_connection *conn, const char *path,
const void **exports)
{
@@ -751,7 +838,7 @@ void mg_exec_lua_script(struct mg_connection *conn, const char *path,
conn->must_close=1;
/* Execute a plain Lua script. */
if (path != NULL && (L = luaL_newstate()) != NULL) {
if (path != NULL && (L = lua_newstate(lua_allocator, NULL)) != NULL) {
prepare_lua_environment(conn, L, path, LUA_ENV_TYPE_PLAIN_LUA_PAGE);
lua_pushcclosure(L, &lua_error_handler, 0);
@@ -809,7 +896,7 @@ struct file *filep, struct lua_State *ls)
fileno(filep->fp), 0)) == MAP_FAILED) {
lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
fileno(filep->fp), strerror(errno));
} else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
} else if ((L = ls != NULL ? ls : lua_newstate(lua_allocator, NULL)) == NULL) {
send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
} else {
/* We're not sending HTTP headers here, Lua page must do it. */
@@ -833,7 +920,15 @@ struct file *filep, struct lua_State *ls)
struct lua_websock_data {
lua_State *main;
lua_State *thread;
char * script;
unsigned shared;
struct mg_connection *conn;
pthread_mutex_t mutex;
};
struct mg_shared_lua_websocket {
struct lua_websock_data *sock;
struct mg_shared_lua_websocket *next;
};
static void websock_cry(struct mg_connection *conn, int err, lua_State * L, const char * ws_operation, const char * lua_operation)
@@ -866,22 +961,62 @@ static void websock_cry(struct mg_connection *conn, int err, lua_State * L, cons
static void * lua_websocket_new(const char * script, struct mg_connection *conn, int is_shared)
{
struct lua_websock_data *lws_data;
struct mg_shared_lua_websocket **shared_websock_list = &(conn->ctx->shared_lua_websockets);
int ok = 0;
int found = 0;
int err, nargs;
assert(conn->lua_websocket_state == NULL);
lws_data = (struct lua_websock_data *) malloc(sizeof(*lws_data));
if (lws_data) {
lws_data->conn = conn;
/*
lock list (mg_context global)
check if in list
yes: inc rec counter
no: create state, add to list
lock list element
unlock list (mg_context global)
call add
unlock list element
*/
if (is_shared) {
(void)pthread_mutex_lock(&conn->ctx->mutex);
// TODO: add_to_websocket_list(lws_data);
while (*shared_websock_list) {
if (!strcmp((*shared_websock_list)->sock->script, script)) {
lws_data = (*shared_websock_list)->sock;
lws_data->shared++;
found = 1;
}
shared_websock_list = &((*shared_websock_list)->next);
}
(void)pthread_mutex_unlock(&conn->ctx->mutex);
}
lws_data->main = luaL_newstate();
if (!found) {
lws_data = (struct lua_websock_data *) mg_malloc(sizeof(*lws_data));
}
if (lws_data) {
if (!found) {
lws_data->shared = is_shared;
lws_data->conn = conn;
lws_data->script = mg_strdup(script);
lws_data->main = lua_newstate(lua_allocator, NULL);
if (is_shared) {
(void)pthread_mutex_lock(&conn->ctx->mutex);
shared_websock_list = &(conn->ctx->shared_lua_websockets);
while (*shared_websock_list) {
shared_websock_list = &((*shared_websock_list)->next);
}
*shared_websock_list = (struct mg_shared_lua_websocket *)mg_malloc(sizeof(struct mg_shared_lua_websocket));
if (*shared_websock_list) {
(*shared_websock_list)->sock = lws_data;
(*shared_websock_list)->next = 0;
}
(void)pthread_mutex_unlock(&conn->ctx->mutex);
}
}
if (lws_data->main) {
prepare_lua_environment(conn, lws_data->main, script, LUA_ENV_TYPE_LUA_WEBSOCKET);
if (conn->ctx->callbacks.init_lua != NULL) {
@@ -908,7 +1043,8 @@ static void * lua_websocket_new(const char * script, struct mg_connection *conn,
if (!ok) {
if (lws_data->main) lua_close(lws_data->main);
free(lws_data);
mg_free(lws_data->script);
mg_free(lws_data);
lws_data=0;
}
} else {
@@ -928,6 +1064,12 @@ static int lua_websocket_data(struct mg_connection *conn, int bits, char *data,
assert(lws_data->main != NULL);
assert(lws_data->thread != NULL);
/*
lock list element
call data
unlock list element
*/
do {
retry=0;
@@ -978,17 +1120,53 @@ static int lua_websocket_ready(struct mg_connection *conn)
static void lua_websocket_close(struct mg_connection *conn)
{
struct lua_websock_data *lws_data = (struct lua_websock_data *)(conn->lua_websocket_state);
struct mg_shared_lua_websocket **shared_websock_list;
int err;
assert(lws_data != NULL);
assert(lws_data->main != NULL);
assert(lws_data->thread != NULL);
/*
lock list element
lock list (mg_context global)
call remove
dec ref counter
if ref counter == 0 close state and remove from list
unlock list element
unlock list (mg_context global)
*/
lua_pushboolean(lws_data->thread, 0);
err = lua_resume(lws_data->thread, NULL, 1);
if (lws_data->shared) {
(void)pthread_mutex_lock(&conn->ctx->mutex);
lws_data->shared--;
if (lws_data->shared==0) {
/*
shared_websock_list = &(conn->ctx->shared_lua_websockets);
while (*shared_websock_list) {
if ((*shared_websock_list)->sock == lws_data) {
*shared_websock_list = (*shared_websock_list)->next;
} else {
shared_websock_list = &((*shared_websock_list)->next);
}
}
lua_close(lws_data->main);
free(lws_data);
mg_free(lws_data->script);
lws_data->script=0;
mg_free(lws_data);
*/
}
(void)pthread_mutex_unlock(&conn->ctx->mutex);
} else {
lua_close(lws_data->main);
mg_free(lws_data->script);
mg_free(lws_data);
}
conn->lua_websocket_state = NULL;
}
#endif

View File

@@ -1,6 +1,9 @@
<?
-- This *.lp file simply runs the *.lua file in the same directory.
n = string.match(mg.request_info.uri, "^(.*)%.lp$")
n = string.gsub(n, [[/]], [[\]])
if mg.system:find("Windows") then
n = string.gsub(n, [[/]], [[\]])
end
n = mg.document_root .. n .. ".lua"
dofile(n)
?>

View File

@@ -24,12 +24,12 @@ resp = resp .. "}";
mg.write("HTTP/1.1 200 OK\n")
mg.write("Connection: close\n")
mg.write("Content-Type: text/html\n")
mg.write("Cache-Control: no-cache\n")
mg.write("HTTP/1.1 200 OK\r\n")
mg.write("Connection: close\r\n")
mg.write("Content-Type: text/html\r\n")
mg.write("Cache-Control: no-cache\r\n")
--mg.write("Content-Length: " .. resp:len() .. "\n")
mg.write("\n")
mg.write("\r\n")
mg.write(resp)

75
test/cors.html Normal file
View File

@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html>
<head>
<title>CORS test</title>
<style>
html,body{font:normal 1em arial,helvetica;}
</style>
<script> // http://www.html5rocks.com/en/tutorials/cors/
// Create the XHR object.
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// XHR for Chrome/Firefox/Opera/Safari.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// XDomainRequest for IE.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// CORS not supported.
xhr = null;
}
return xhr;
}
// Helper method to parse the title tag from the response.
function getTitle(text) {
return text.match('<title>(.*)?</title>')[1];
}
// Make the actual CORS request.
function makeCorsRequest(method, resource) {
var url = "http://localhost/cors.reply." + resource;
var xhr = createCORSRequest(method, url);
if (!xhr) {
alert('ERROR: CORS not supported');
return;
}
// Response handlers.
xhr.onload = function() {
var text = xhr.responseText;
var title = getTitle(text);
alert('Response from CORS request to ' + url + ':\n' + title);
};
xhr.onerror = function() {
alert('ERROR: the request failed.');
};
xhr.send();
}
function start() {
var el = document.getElementById("from");
el.innerHTML = "Test CORS from " + document.URL + " to http://localhost/cors.reply.*";
if ((document.URL.indexOf("localhost") >= 0) || (document.URL.indexOf("127.0.0.1") >= 0)) {
alert("This CORS test is only meaningful, if you open this site with a different url than \'localhost\' (127.0.0.1).\nYou may use a different IP of the same machine.");
}
}
</script>
</head>
<body onload="start()">
<h1>Cross-origin resource sharing test</h1>
<p id="from">*** Error: Javascript is not activated. This test will not work. ***</p>
<button onclick="makeCorsRequest('GET', 'html')">Run CORS GET request (static resource)</button>
<button onclick="makeCorsRequest('GET', 'shtml')">Run CORS GET request (ssi)</button>
<button onclick="makeCorsRequest('GET', 'lua/getit')">Run CORS GET request (dynamic resource)</button>
<button onclick="makeCorsRequest('PUT', 'lua/putit')">Run CORS PUT request (dynamic resource)</button>
<p>More information on CORS: See <a href="http://enable-cors.org/">enable-cors.org</a> and <a href="http://www.html5rocks.com/en/tutorials/cors/">html5rocks.com</a>.</p>
</body>
</html>

7
test/cors.reply.html Normal file
View File

@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head><title>CORS test reply - test OK</title></head>
<body>
Do not load this page directly - use cors.html instead!
</body>
</html>

73
test/cors.reply.lua Normal file
View File

@@ -0,0 +1,73 @@
-- http://www.html5rocks.com/static/images/cors_server_flowchart.png
if not mg.request_info.http_headers.Origin then
mg.write("HTTP/1.0 200 OK\r\n")
mg.write("Connection: close\r\n")
mg.write("Content-Type: text/html; charset=utf-8\r\n")
mg.write("\r\n")
mg.write("This test page should not be used directly. Open cors.html instead.")
return
end
if mg.request_info.request_method == "OPTIONS" then
local acrm = mg.request_info.http_headers['Access-Control-Request-Method'];
if (acrm) then
local acrh = nil -- mg.request_info.http_headers['Access-Control-Request-Header'];
if (acrm~='PUT') then
-- invalid request
mg.write("HTTP/1.0 403 Forbidden\r\n")
mg.write("Connection: close\r\n")
mg.write("\r\n")
return
else
-- preflight request
mg.write("HTTP/1.0 200 OK\r\n")
mg.write("Access-Control-Allow-Methods: PUT\r\n")
if (acrh) then
mg.write("Access-Control-Allow-Headers: " .. acrh .. "\r\n")
end
mg.write("Access-Control-Allow-Origin: *\r\n")
mg.write("Connection: close\r\n")
mg.write("Content-Type: text/html; charset=utf-8\r\n")
mg.write("\r\n")
return
end
end
end
-- actual request
if mg.request_info.request_method == "GET" then
mg.write("HTTP/1.0 200 OK\r\n")
mg.write("Access-Control-Allow-Origin: *\r\n")
mg.write("Connection: close\r\n")
mg.write("Content-Type: text/html; charset=utf-8\r\n")
mg.write("\r\n")
mg.write([[<!DOCTYPE html>
<html>
<head><title>CORS dynamic GET test reply - test OK</title></head>
<body>This should never be shown</body>
</html>
]])
return
end
if mg.request_info.request_method == "PUT" then
mg.write("HTTP/1.0 200 OK\r\n")
mg.write("Access-Control-Allow-Origin: *\r\n")
mg.write("Connection: close\r\n")
mg.write("Content-Type: text/html; charset=utf-8\r\n")
mg.write("\r\n")
mg.write([[<!DOCTYPE html>
<html>
<head><title>CORS dynamic PUT test reply - test OK</title></head>
<body>This should never be shown</body>
</html>
]])
return
end
mg.write("HTTP/1.0 403 Forbidden\r\n")
mg.write("Connection: close\r\n")
mg.write("\r\n")

7
test/cors.reply.shtml Normal file
View File

@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head><title>CORS test reply - test OK</title></head>
<body>
Do not load this page directly - use cors.html instead!
</body>
</html>

View File

@@ -0,0 +1,7 @@
--[[
Load this test file by adding
lua_preload_file ./lua_preload_file.lua
to the civetweb.conf file
]]
mg.preload = "lua_preload_file successfully loaded"

View File

@@ -11,6 +11,7 @@ end
mg.write("HTTP/1.0 200 OK\r\n")
mg.write("Connection: close\r\n")
mg.write("Content-Type: text/html; charset=utf-8\r\n")
mg.write("Cache-Control: max-age=0, must-revalidate\r\n")
if not cookie_value then
mg.write("Set-Cookie: " .. cookie_name .. "=" .. tostring(now) .. "\r\n")
end
@@ -106,23 +107,47 @@ else
end
raw_string = [[ !"#$%&'()*+,-./0123456789:;<=>?@]]
mg.write(" original string: " .. htmlEscape(raw_string) .. "\r\n")
url_string = mg.url_encode(raw_string):upper()
mg_string = mg.url_encode(raw_string):upper()
ref_string = "%20!%22%23%24%25%26'()*%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40" -- from http://www.w3schools.com/tags/ref_urlencode.asp
mg.write(" mg-url: " .. htmlEscape(url_string) .. "\r\n")
mg.write(" mg-url: " .. htmlEscape(mg_string) .. "\r\n")
mg.write(" reference url: " .. htmlEscape(ref_string) .. "\r\n")
dec_url_string = mg.url_decode(url_string)
dec_mg_string = mg.url_decode(mg_string)
dec_ref_string = mg.url_decode(ref_string)
mg.write(" decoded mg-url: " .. htmlEscape(dec_url_string) .. "\r\n")
mg.write(" decoded mg-url: " .. htmlEscape(dec_mg_string) .. "\r\n")
mg.write(" decoded reference url: " .. htmlEscape(dec_ref_string) .. "\r\n")
dec_url_string = mg.url_decode(url_string, false)
dec_mg_string = mg.url_decode(mg_string, false)
dec_ref_string = mg.url_decode(ref_string, false)
mg.write(" decoded mg-url: " .. htmlEscape(dec_url_string) .. "\r\n")
mg.write(" decoded mg-url: " .. htmlEscape(dec_mg_string) .. "\r\n")
mg.write(" decoded reference url: " .. htmlEscape(dec_ref_string) .. "\r\n")
dec_url_string = mg.url_decode(url_string, true)
dec_mg_string = mg.url_decode(mg_string, true)
dec_ref_string = mg.url_decode(ref_string, true)
mg.write(" decoded mg-url: " .. htmlEscape(dec_url_string) .. "\r\n")
mg.write(" decoded mg-url: " .. htmlEscape(dec_mg_string) .. "\r\n")
mg.write(" decoded reference url: " .. htmlEscape(dec_ref_string) .. "\r\n")
mg.write("\r\n")
-- base64_encode
mg.write("BASE64 encode/decode test:\r\n")
raw_string = [[ !"#$%&'()*+,-./0123456789:;<=>?@]]
mg.write(" original string: " .. htmlEscape(raw_string) .. "\r\n")
mg_string = mg.base64_encode(raw_string)
ref_string = "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9A" -- from http://www.base64encode.org/
mg.write(" mg-base64: " .. htmlEscape(mg_string) .. "\r\n")
mg.write(" reference base64: " .. htmlEscape(ref_string) .. "\r\n")
dec_mg_string = mg.base64_decode(mg_string)
dec_ref_string = mg.base64_decode(ref_string)
mg.write(" decoded mg-base64: " .. htmlEscape(dec_mg_string) .. "\r\n")
mg.write(" decoded reference base64: " .. htmlEscape(dec_ref_string) .. "\r\n")
mg.write("\r\n")
raw_string = [[<?> -?-]]
mg.write(" original string: " .. htmlEscape(raw_string) .. "\r\n")
mg_string = mg.base64_encode(raw_string)
ref_string = "PD8+IC0/LQ==" -- from http://www.base64encode.org/
mg.write(" mg-base64: " .. htmlEscape(mg_string) .. "\r\n")
mg.write(" reference base64: " .. htmlEscape(ref_string) .. "\r\n")
dec_mg_string = mg.base64_decode(mg_string)
dec_ref_string = mg.base64_decode(ref_string)
mg.write(" decoded mg-base64: " .. htmlEscape(dec_mg_string) .. "\r\n")
mg.write(" decoded reference base64: " .. htmlEscape(dec_ref_string) .. "\r\n")
-- end of page
mg.write("</pre>\r\n</body>\r\n</html>\r\n")

BIN
test/test.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB