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:
23
Makefile
23
Makefile
@@ -1,4 +1,4 @@
|
||||
#
|
||||
#
|
||||
# Copyright (c) 2013 No Face Press, LLC
|
||||
# License http://opensource.org/licenses/mit-license.php MIT License
|
||||
#
|
||||
@@ -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
|
||||
@@ -92,7 +94,7 @@ LIB_OBJECTS = $(filter-out $(MAIN_OBJECTS), $(BUILD_OBJECTS))
|
||||
|
||||
LIBS = -lpthread -lm
|
||||
|
||||
ifeq ($(TARGET_OS),LINUX)
|
||||
ifeq ($(TARGET_OS),LINUX)
|
||||
LIBS += -ldl
|
||||
endif
|
||||
|
||||
@@ -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,16 +191,20 @@ 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 $@
|
||||
@rm -f $@
|
||||
ar cq $@ $(LIB_OBJECTS)
|
||||
|
||||
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)
|
||||
|
||||
|
@@ -1,14 +1,24 @@
|
||||
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)
|
||||
- Fixed: select for Linux needs the nfds parameter set correctly (bel)
|
||||
- Fixed: select for Linux needs the nfds parameter set correctly (bel)
|
||||
- Add methods for returning the ports civetweb is listening on (keithel)
|
||||
- Fixes for Lua Server Pages, as described within the google groups thread. (bel)
|
||||
- Added support for plain Lua Scripts, and an example script. (bel)
|
||||
@@ -30,7 +40,7 @@ Changes
|
||||
|
||||
- Corrected bad mask flag/opcode passing to websocket callback (William Greathouse)
|
||||
- Moved CEVITWEB_VERSION define into civetweb.h
|
||||
- Added new simple zip deployment build for Windows.
|
||||
- Added new simple zip deployment build for Windows.
|
||||
- Removed windows install package build.
|
||||
- Fixes page violation in mod_lua.inl (apkbox)
|
||||
- Use C style comments to enable compiling most of civetweb with -ansi. (F-Secure Corporation)
|
||||
@@ -79,7 +89,7 @@ Changes
|
||||
- Conformed source files to UNIX line endings for consistency.
|
||||
- Unified the coding style to improve reability.
|
||||
|
||||
Release Notes v1.3
|
||||
Release Notes v1.3
|
||||
===
|
||||
### Objectives: *Buildroot Integration*
|
||||
|
||||
@@ -91,7 +101,7 @@ Changes
|
||||
- Updated documentation
|
||||
- Updated Buildroot config example
|
||||
|
||||
Release Notes v1.2
|
||||
Release Notes v1.2
|
||||
===
|
||||
### Objectives: *Installation Improvements, buildroot, cross compile support*
|
||||
The objective of this release is to make installation seamless.
|
||||
@@ -115,7 +125,7 @@ Known Issues
|
||||
|
||||
- The prebuilt Window's version requires [Visual C++ Redistributable for Visual Studio 2012](http://www.microsoft.com/en-us/download/details.aspx?id=30679)
|
||||
|
||||
Release Notes v1.1
|
||||
Release Notes v1.1
|
||||
===
|
||||
### Objectives: *Build, Documentation, License Improvements*
|
||||
The objective of this release is to establish a maintable code base, ensure MIT license rights and improve usability and documentation.
|
||||
@@ -144,7 +154,7 @@ Changes
|
||||
+ Removed yaSSL from the OSX build, not needed.
|
||||
- Added new Visual Studio projects for Windows builds.
|
||||
+ Removed Windows support from Makefiles
|
||||
+ Provided additional, examples with Lua, and another with yaSSL.
|
||||
+ Provided additional, examples with Lua, and another with yaSSL.
|
||||
- Changed Zombie Reaping policy to not ignore SIGCHLD.
|
||||
+ The previous method caused trouble in applciations that spawn children.
|
||||
|
||||
@@ -154,7 +164,7 @@ Known Issues
|
||||
- Build support for VS6 and some other has been deprecated.
|
||||
+ This does not impact embedded programs, just the stand-alone build.
|
||||
+ The old Makefile was renamed to Makefile.deprecated.
|
||||
+ This is partcially do to lack fo testing.
|
||||
+ This is partcially do to lack fo testing.
|
||||
+ Need to find out what is actually in demand.
|
||||
- Build changes may impact current users.
|
||||
+ As with any change of this type, changes may impact some users.
|
||||
|
@@ -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>
|
||||
|
@@ -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.
|
||||
@@ -402,11 +409,13 @@ mg (table):
|
||||
mg.document_root -- a string that holds the document root directory
|
||||
mg.auth_domain -- a string that holds the HTTP authentication domain
|
||||
mg.get_var(str, varname) -- extract variable from (query) string
|
||||
mg.get_cookie(str, cookie) -- extract cookie from a string
|
||||
mg.get_cookie(str, cookie) -- extract cookie from a string
|
||||
mg.get_mime_type(filename) -- get MIME type of a file
|
||||
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
|
||||
@@ -419,7 +428,7 @@ mg (table):
|
||||
.query_string -- query string if present, nil otherwise
|
||||
.script_name -- name of the Lua script
|
||||
.https -- true if accessed by https://, false otherwise
|
||||
.remote_user -- user name if authenticated, nil otherwise
|
||||
.remote_user -- user name if authenticated, nil otherwise
|
||||
|
||||
connect (function):
|
||||
-- Connect to the remote TCP server. This function is an implementation
|
||||
|
@@ -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))) {
|
||||
|
@@ -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,
|
||||
const char *domain,
|
||||
const char *user,
|
||||
const char *password);
|
||||
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,
|
||||
const char *data, size_t data_len);
|
||||
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 *,
|
||||
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
|
||||
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,8 +409,9 @@ 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,
|
||||
const char *var_name, char *dst, size_t dst_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.
|
||||
|
||||
@@ -388,8 +436,9 @@ 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,
|
||||
const char *var_name, char *dst, size_t dst_len, size_t occurrence);
|
||||
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.
|
||||
|
||||
@@ -404,8 +453,8 @@ 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,
|
||||
char *buf, size_t buf_len);
|
||||
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.
|
||||
@@ -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,
|
||||
char *error_buffer, size_t error_buffer_size,
|
||||
PRINTF_FORMAT_STRING(const char *request_fmt),
|
||||
...) PRINTF_ARGS(6, 7);
|
||||
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,
|
||||
int dst_len, int is_form_url_encoded);
|
||||
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,
|
||||
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
|
||||
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
|
||||
}
|
||||
|
@@ -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
|
@@ -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
|
||||
|
628
src/civetweb.c
628
src/civetweb.c
File diff suppressed because it is too large
Load Diff
308
src/main.c
308
src/main.c
@@ -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,10 +78,11 @@ 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 struct mg_context *ctx; /* Set by start_civetweb() */
|
||||
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)
|
||||
#define CONFIG_FILE "civetweb.conf"
|
||||
@@ -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"),
|
||||
|
204
src/mod_lua.inl
204
src/mod_lua.inl
@@ -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));
|
||||
|
||||
/*
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
lws_data = (struct lua_websock_data *) mg_malloc(sizeof(*lws_data));
|
||||
}
|
||||
|
||||
if (lws_data) {
|
||||
lws_data->conn = conn;
|
||||
|
||||
if (is_shared) {
|
||||
(void)pthread_mutex_lock(&conn->ctx->mutex);
|
||||
// TODO: add_to_websocket_list(lws_data);
|
||||
(void)pthread_mutex_unlock(&conn->ctx->mutex);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
lws_data->main = luaL_newstate();
|
||||
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);
|
||||
|
||||
lua_close(lws_data->main);
|
||||
free(lws_data);
|
||||
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);
|
||||
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
|
||||
|
@@ -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)
|
||||
?>
|
@@ -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
75
test/cors.html
Normal 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
7
test/cors.reply.html
Normal 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
73
test/cors.reply.lua
Normal 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
7
test/cors.reply.shtml
Normal 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>
|
7
test/lua_preload_file.lua
Normal file
7
test/lua_preload_file.lua
Normal 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"
|
@@ -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
BIN
test/test.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Reference in New Issue
Block a user