mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08afdfa8b6 | ||
|
|
05f7d65fc9 | ||
|
|
a194e07545 | ||
|
|
dbb8a5bbf4 | ||
|
|
d3005df019 | ||
|
|
ef9b103a00 | ||
|
|
f0a2924a7c | ||
|
|
f109030cd0 | ||
|
|
eebdd06532 | ||
|
|
ca2fc71a01 | ||
|
|
e61e371c16 | ||
|
|
d5f07f5b9b | ||
|
|
0a8d5dde51 | ||
|
|
5646caa572 | ||
|
|
c7e5573d08 | ||
|
|
b9a226d109 | ||
|
|
fcf8b86288 | ||
|
|
a8b08c6784 | ||
|
|
1fb6f391a1 | ||
|
|
fcbc36ecde | ||
|
|
70771f29ce | ||
|
|
121391504f | ||
|
|
8455742440 | ||
|
|
5b786a5089 | ||
|
|
a6e74e7707 |
No files matched your search
@@ -36,20 +36,19 @@ LIB = lib$(PROG).so$(MONGOOSE_LIB_SUFFIX)
|
||||
# "-Wl,--as-needed" turned on by default in cc command.
|
||||
# Also, this is turned in many other distros in static linkage builds.
|
||||
linux:
|
||||
$(CC) mongoose.c -shared -fPIC -fpic -o $(LIB) -Wl,-soname,$(LIB) -ldl -pthread $(CFLAGS)
|
||||
$(CC) mongoose.c main.c -o $(PROG) -ldl -pthread $(CFLAGS)
|
||||
|
||||
bsd:
|
||||
$(CC) mongoose.c -shared -pthread -fpic -fPIC -o $(LIB) $(CFLAGS)
|
||||
$(CC) mongoose.c main.c -pthread -o $(PROG) $(CFLAGS)
|
||||
|
||||
mac:
|
||||
$(CC) mongoose.c -pthread -o $(LIB) -flat_namespace -bundle -undefined suppress $(CFLAGS)
|
||||
$(CC) mongoose.c main.c -DUSE_COCOA -pthread $(CFLAGS) -framework Cocoa -ObjC -arch i386 -arch x86_64 -o $(PROG)
|
||||
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`; DIR=dmg/$(PROG).app && rm -rf $$DIR && mkdir -p $$DIR/Contents/{MacOS,Resources} && install -m 644 build/mongoose_*.png $$DIR/Contents/Resources/ && install -m 644 build/Info.plist $$DIR/Contents/ && install -m 755 $(PROG) $$DIR/Contents/MacOS/ && ln -fs /Applications dmg/ ; hdiutil create $(PROG)_$$V.dmg -volname "Mongoose $$V" -srcfolder dmg -ov #; rm -rf dmg
|
||||
$(CC) mongoose.c main.c -pthread $(CFLAGS) -o $(PROG)
|
||||
|
||||
cocoa:
|
||||
$(CC) mongoose.c main.c -DUSE_COCOA -pthread $(CFLAGS) -framework Cocoa -ObjC -arch i386 -arch x86_64 -o Mongoose
|
||||
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`; DIR=dmg/Mongoose.app && rm -rf $$DIR && mkdir -p $$DIR/Contents/{MacOS,Resources} && install -m 644 build/mongoose_*.png $$DIR/Contents/Resources/ && install -m 644 build/Info.plist $$DIR/Contents/ && install -m 755 Mongoose $$DIR/Contents/MacOS/ && ln -fs /Applications dmg/ ; hdiutil create Mongoose_$$V.dmg -volname "Mongoose $$V" -srcfolder dmg -ov #; rm -rf dmg
|
||||
|
||||
solaris:
|
||||
$(CC) mongoose.c -pthread -lnsl -lsocket -fpic -fPIC -shared -o $(LIB) $(CFLAGS)
|
||||
$(CC) mongoose.c main.c -pthread -lnsl -lsocket -o $(PROG) $(CFLAGS)
|
||||
|
||||
|
||||
@@ -67,7 +66,7 @@ CYA = e:/cyassl-2.0.0rc2
|
||||
DBG = /DNDEBUG /O1
|
||||
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /Gz /W3 /DNO_SSL_DL \
|
||||
/I$(MSVC)/include /DUSE_LUA /I$(LUA)
|
||||
GUILIB= user32.lib shell32.lib
|
||||
GUILIB= user32.lib shell32.lib comdlg32.lib
|
||||
LINK = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
|
||||
/subsystem:windows ws2_32.lib advapi32.lib cyassl.lib lua.lib
|
||||
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl /I$(MSVC)/INCLUDE \
|
||||
@@ -114,7 +113,7 @@ windows: cyassl.lib lua.lib
|
||||
$(MSVC)/bin/rc build\res.rc
|
||||
$(CL) /Ibuild main.c mongoose.c /GA $(LINK) build\res.res \
|
||||
$(GUILIB) /out:$(PROG).exe
|
||||
$(CL) mongoose.c /GD $(LINK) /DLL /DEF:build\dll.def /out:$(PROG).dll
|
||||
# $(CL) mongoose.c /GD $(LINK) /DLL /DEF:build\dll.def /out:$(PROG).dll
|
||||
|
||||
# Build for Windows under MinGW
|
||||
#MINGWDBG= -DDEBUG -O0 -ggdb
|
||||
@@ -154,7 +153,9 @@ tests:
|
||||
perl test/test.pl $(TEST)
|
||||
|
||||
release: clean
|
||||
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`.tgz ; cd .. && tar -czf x mongoose/{LICENSE,Makefile,examples,test,build,mongoose.c,mongoose.h,mongoose.1} && mv x mongoose/$$F
|
||||
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`.tgz ; cd .. && tar -czf x mongoose/{LICENSE,Makefile,examples,test,build,*.[ch],*.md} && mv x mongoose/$$F
|
||||
|
||||
clean:
|
||||
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz $(PROG).exe *.dll *.lib build/res.o build/res.RES
|
||||
cd examples && $(MAKE) clean
|
||||
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz \
|
||||
$(PROG).exe *.dll *.lib build/res.o build/res.RES *.dSYM
|
||||
@@ -2,8 +2,9 @@ Project Mission
|
||||
---------------
|
||||
|
||||
Project mission is to provide simple, functional, embeddable web server to
|
||||
make it easy for application and device developers to implement web interface for their
|
||||
application and devices, and to offer a simple web development environment.
|
||||
make it easy for application and device developers to implement web interface
|
||||
for their application and devices, and to offer a simple web development
|
||||
environment.
|
||||
|
||||
Overview
|
||||
--------
|
||||
@@ -11,32 +12,45 @@ Overview
|
||||
To accomplish it's mission, Mongoose keeps balance on functionality and
|
||||
simplicity by carefully selected list of features:
|
||||
|
||||
- Liberal, commercial-friendly [MIT license](http://en.wikipedia.org/wiki/MIT_License)
|
||||
- Liberal, commercial-friendly
|
||||
[MIT license](http://en.wikipedia.org/wiki/MIT_License)
|
||||
- Works on Windows, Mac, UNIX, iPhone, Android, and many other platforms
|
||||
- Support for CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV
|
||||
- Lua server pages (PHP-like functionality using Lua), see [page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp)
|
||||
- Lua server pages (PHP-like functionality using Lua), see
|
||||
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp)
|
||||
- Resumed download, URL rewrite, IP-based ACL, Windows service
|
||||
- Excluding files from serving by URI pattern (file blacklist)
|
||||
- Download speed limit based on client subnet or URI pattern
|
||||
- Small footprint: executable size is 50 kB on Linux 2.6 i386 system
|
||||
- 130 kilobytes Windows executable with all of the above and no dependencies
|
||||
- Simple and clean embedding API ([mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)). The source is in single [mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file to make things easy.
|
||||
- Embedding examples: [hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c), [post.c](https://github.com/valenok/mongoose/blob/master/examples/post.c), [upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c), [websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c)
|
||||
- Extensive documentation in form of [User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
|
||||
- Simple and clean embedding API,
|
||||
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)).
|
||||
The source is in single
|
||||
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file
|
||||
to make things easy.
|
||||
- Embedding examples:
|
||||
[hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c),
|
||||
[post.c](https://github.com/valenok/mongoose/blob/master/examples/post.c),
|
||||
[upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c),
|
||||
[websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c)
|
||||
- Extensive documentation in form of
|
||||
[User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
|
||||
|
||||
Questions can be asked at
|
||||
[mongoose-users@google.com](http://groups.google.com/group/mongoose-users) mailing list.
|
||||
[mongoose-users@google.com](http://groups.google.com/group/mongoose-users)
|
||||
mailing list.
|
||||
|
||||
|
||||
Keep Sergey happy
|
||||
-----------------
|
||||
|
||||
Since 2004, Mongoose is being constantly improved by me, Sergey Lyubka, a software engineer
|
||||
from Galway, Ireland. My other software I give to the community for free is
|
||||
Since 2004, Mongoose is being constantly improved by me, Sergey Lyubka, a
|
||||
software engineer from Galway, Ireland. My other software I give to the
|
||||
community for free is
|
||||
[Super Light Regular Expression library](http://code.google.com/p/slre).
|
||||
|
||||
|
||||
If you feel grateful for the stuff I've done, you can buy me a book from my
|
||||
[Amazon wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority). Many thanks to all who
|
||||
already did so: T.Barmann, D.Hughes, J.C.Sloan, R.Romeo and 4 others.
|
||||
Appreciated guys, you keep my brains going!
|
||||
[Amazon wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority). Many thanks
|
||||
to all who already did so: T.Barmann, D.Hughes, J.C.Sloan, R.Romeo,
|
||||
L.E.Spencer, and 4 others. Appreciated guys, you keep my brains going!
|
||||
+133
-1
@@ -51,12 +51,144 @@ Usage Examples
|
||||
- How to share a Windows folder: copy mongoose executable to a folder and
|
||||
double-click the executable. The folder should be accessible via
|
||||
[http://localhost:8080](http://localhost:8080) in any browser.
|
||||
|
||||
- How to start mongoose at UNIX startup time in daemon mode, serving
|
||||
directory `/var/www`: put this line in the system startup script,
|
||||
directory `/var/www`: put this line in the system startup script:
|
||||
`/path/to/mongoose -listening_ports 80 -document_root /var/www &`
|
||||
|
||||
- How to serve user home directories using URL rewrite:
|
||||
`mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/`
|
||||
|
||||
Command Line Options
|
||||
--------------------
|
||||
```
|
||||
|
||||
-A htpasswd_file domain_name user_name password
|
||||
Add/edit user's password in the passwords file. Deleting users
|
||||
can be done with any text editor. Functionality is similar to
|
||||
Apache's htdigest utility.
|
||||
|
||||
-C cgi_pattern
|
||||
All files that fully match cgi_pattern are treated as CGI.
|
||||
Default pattern allows CGI files be anywhere. To restrict CGIs to
|
||||
certain directory, use e.g. "-C /cgi-bin/**.cgi". Default:
|
||||
"**.cgi$|**.pl$|**.php$"
|
||||
|
||||
-E cgi_environment
|
||||
Extra environment variables to be passed to the CGI script in
|
||||
addition to standard ones. The list must be comma-separated list
|
||||
of X=Y pairs, like this: "VARIABLE1=VALUE1,VARIABLE2=VALUE2".
|
||||
Default: ""
|
||||
|
||||
-G put_delete_passwords_file
|
||||
PUT and DELETE passwords file. This must be specified if PUT or
|
||||
DELETE methods are used. Default: ""
|
||||
|
||||
-I cgi_interpreter
|
||||
Use cgi_interpreter as a CGI interpreter for all CGI scripts
|
||||
regardless script extension. Mongoose decides which interpreter
|
||||
to use by looking at the first line of a CGI script. Default:
|
||||
"".
|
||||
|
||||
-P protect_uri
|
||||
Comma separated list of URI=PATH pairs, specifying that given
|
||||
URIs must be protected with respected password files. Default: ""
|
||||
|
||||
-R authentication_domain
|
||||
Authorization realm. Default: "mydomain.com"
|
||||
|
||||
-S ssi_pattern
|
||||
All files that fully match ssi_pattern are treated as SSI.
|
||||
Unknown SSI directives are silently ignored. Currently, two SSI
|
||||
directives are supported, "include" and "exec". Default:
|
||||
"**.shtml$|**.shtm$"
|
||||
|
||||
-T throttle
|
||||
Limit download speed for clients. throttle is a comma-separated
|
||||
list of key=value pairs, where key could be a '*' character
|
||||
(limit for all connections), a subnet in form x.x.x.x/mask (limit
|
||||
for a given subnet, for example 10.0.0.0/8), or an URI prefix
|
||||
pattern (limit for the set of URIs, for example /foo/**). The
|
||||
value is a floating-point number of bytes per second, optionally
|
||||
followed by a `k' or `m' character, meaning kilobytes and
|
||||
megabytes respectively. A limit of 0 means unlimited rate. The
|
||||
last matching rule wins. For example, "*=1k,10.0.0.0/8" means
|
||||
limit everybody to 1 kilobyte per second, but give people from
|
||||
10/8 subnet unlimited speed. Default: ""
|
||||
|
||||
-a access_log_file
|
||||
Access log file. Default: "", no logging is done.
|
||||
|
||||
-d enable_directory_listing
|
||||
Enable/disable directory listing. Default: "yes"
|
||||
|
||||
-e error_log_file
|
||||
Error log file. Default: "", no errors are logged.
|
||||
|
||||
-g global_passwords_file
|
||||
Location of a global passwords file. If set, per-directory
|
||||
.htpasswd files are ignored, and all requests must be authorised
|
||||
against that file. Default: ""
|
||||
|
||||
-i index_files
|
||||
Comma-separated list of files to be treated as directory index
|
||||
files. Default: "index.html,index.htm,index.cgi"
|
||||
|
||||
-l access_control_list
|
||||
Specify access control list (ACL). ACL is a comma separated list
|
||||
of IP subnets, each subnet is prepended by '-' or '+' sign. Plus
|
||||
means allow, minus means deny. If subnet mask is omitted, like
|
||||
"-1.2.3.4", then it means single IP address. Mask may vary from 0
|
||||
to 32 inclusive. On each request, full list is traversed, and
|
||||
last match wins. Default setting is to allow all. For example, to
|
||||
allow only 192.168/16 subnet to connect, run "mongoose
|
||||
-0.0.0.0/0,+192.168/16". Default: ""
|
||||
|
||||
-m extra_mime_types
|
||||
Extra mime types to recognize, in form "extension1=type1,exten-
|
||||
sion2=type2,...". Extension must include dot. Example: "mongoose
|
||||
-m .cpp=plain/text,.java=plain/text". Default: ""
|
||||
|
||||
-p listening_ports
|
||||
Comma-separated list of ports to listen on. If the port is SSL, a
|
||||
letter 's' must be appeneded, for example, "-p 80,443s" will open
|
||||
port 80 and port 443, and connections on port 443 will be SSL-ed.
|
||||
It is possible to specify an IP address to bind to. In this case,
|
||||
an IP address and a colon must be prepended to the port number.
|
||||
For example, to bind to a loopback interface on port 80 and to
|
||||
all interfaces on HTTPS port 443, use "mongoose -p
|
||||
127.0.0.1:80,443s". Default: "8080"
|
||||
|
||||
-r document_root
|
||||
Location of the WWW root directory. Default: "."
|
||||
|
||||
-s ssl_certificate
|
||||
Location of SSL certificate file. Default: ""
|
||||
|
||||
-t num_threads
|
||||
Number of worker threads to start. Default: "10"
|
||||
|
||||
-u run_as_user
|
||||
Switch to given user's credentials after startup. Default: ""
|
||||
|
||||
-w url_rewrite_patterns
|
||||
Comma-separated list of URL rewrites in the form of "pattern=sub-
|
||||
stitution,..." If the "pattern" matches some prefix of the
|
||||
requested URL, then matched prefix gets substituted with "substi-
|
||||
tution". For example, "-w /con-
|
||||
fig=/etc,**.doc|**.rtf=/path/to/cgi-bin/handle_doc.cgi" will
|
||||
serve all URLs that start with "/config" from the "/etc" direc-
|
||||
tory, and call handle_doc.cgi script for .doc and .rtf file
|
||||
requests. If some pattern matches, no further matching/substitu-
|
||||
tion is performed (first matching pattern wins). Use full paths
|
||||
in substitutions. Default: ""
|
||||
|
||||
-x hide_files_patterns
|
||||
A prefix pattern for the files to hide. Files that match the pat-
|
||||
tern will not show up in directory listing and return 404 Not
|
||||
Found if requested. Default: ""
|
||||
|
||||
```
|
||||
|
||||
Common Problems
|
||||
---------------
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
200 ICON DISCARDABLE "systray.ico"
|
||||
100 ICON DISCARDABLE "systray.ico"
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 17 KiB |
+1
-1
@@ -25,4 +25,4 @@ windows:
|
||||
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf hello hello.exe upload upload.exe post post.exe websocket websocket.exe chat chat.exe *.dSYM *.obj
|
||||
rm -rf hello upload post websocket chat *.exe *.dSYM *.obj
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2004-2011 Sergey Lyubka
|
||||
// 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
|
||||
@@ -40,6 +40,7 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <winsvc.h>
|
||||
#include <shlobj.h>
|
||||
#define PATH_MAX MAX_PATH
|
||||
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
||||
#define DIRSEP '\\'
|
||||
@@ -110,6 +111,53 @@ static void show_usage_and_exit(void) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(USE_COCOA)
|
||||
static const char *config_file_top_comment =
|
||||
"# Mongoose web server configuration file.\n"
|
||||
"# For detailed description of every option, visit\n"
|
||||
"# https://github.com/valenok/mongoose/blob/master/UserManual.md\n"
|
||||
"# Lines starting with '#' and empty lines are ignored.\n"
|
||||
"# To make a change, remove leading '#', modify option's value,\n"
|
||||
"# save this file and then restart Mongoose.\n\n";
|
||||
|
||||
static const char *get_url_to_first_open_port(const struct mg_context *ctx) {
|
||||
static char url[100];
|
||||
const char *open_ports = mg_get_option(ctx, "listening_ports");
|
||||
int a, b, c, d, port, n;
|
||||
|
||||
if (sscanf(open_ports, "%d.%d.%d.%d:%d%n", &a, &b, &c, &d, &port, &n) == 5) {
|
||||
snprintf(url, sizeof(url), "%s://%d.%d.%d.%d:%d",
|
||||
open_ports[n] == 's' ? "https" : "http", a, b, c, d, port);
|
||||
} else if (sscanf(open_ports, "%d%n", &port, &n) == 1) {
|
||||
snprintf(url, sizeof(url), "%s://localhost:%d",
|
||||
open_ports[n] == 's' ? "https" : "http", port);
|
||||
} else {
|
||||
snprintf(url, sizeof(url), "%s", "http://localhost:8080");
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
static void create_config_file(const char *path) {
|
||||
const char **names, *value;
|
||||
FILE *fp;
|
||||
int i;
|
||||
|
||||
// Create config file if it is not present yet
|
||||
if ((fp = fopen(path, "r")) != NULL) {
|
||||
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] != NULL; i += 3) {
|
||||
value = mg_get_option(ctx, names[i]);
|
||||
fprintf(fp, "# %s %s\n", names[i + 1], *value ? value : "<value>");
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void verify_document_root(const char *root) {
|
||||
const char *p, *path;
|
||||
char buf[PATH_MAX];
|
||||
@@ -188,18 +236,20 @@ static void process_command_line_arguments(char *argv[], char **options) {
|
||||
|
||||
// Loop over the lines in config file
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
|
||||
line_no++;
|
||||
|
||||
// Ignore empty lines and comments
|
||||
for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
|
||||
if (line[i] == '#' || line[i] == '\0')
|
||||
for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
|
||||
if (line[i] == '#' || line[i] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
||||
die("%s: line %d is invalid", config_file, (int) line_no);
|
||||
printf("%s: line %d is invalid, ignoring it:\n %s",
|
||||
config_file, (int) line_no, line);
|
||||
} else {
|
||||
set_option(options, opt, val);
|
||||
}
|
||||
set_option(options, opt, val);
|
||||
}
|
||||
|
||||
(void) fclose(fp);
|
||||
@@ -266,9 +316,25 @@ static void start_mongoose(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
enum {
|
||||
ID_ICON = 100, ID_QUIT, ID_SETTINGS, ID_SEPARATOR, ID_INSTALL_SERVICE,
|
||||
ID_REMOVE_SERVICE, ID_STATIC, ID_GROUP, ID_SAVE, ID_RESET_DEFAULTS,
|
||||
ID_STATUS, ID_CONNECT,
|
||||
|
||||
// All dynamically created text boxes for options have IDs starting from
|
||||
// ID_CONTROLS, incremented by one.
|
||||
ID_CONTROLS = 200,
|
||||
|
||||
// Text boxes for files have "..." buttons to open file browser. These
|
||||
// buttons have IDs that are ID_FILE_BUTTONS_DELTA higher than associated
|
||||
// text box ID.
|
||||
ID_FILE_BUTTONS_DELTA = 1000
|
||||
};
|
||||
static HICON hIcon;
|
||||
static SERVICE_STATUS ss;
|
||||
static SERVICE_STATUS_HANDLE hStatus;
|
||||
static const char *service_magic_argument = "--";
|
||||
static NOTIFYICONDATA TrayIcon;
|
||||
|
||||
static void WINAPI ControlHandler(DWORD code) {
|
||||
if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
|
||||
@@ -296,41 +362,6 @@ static void WINAPI ServiceMain(void) {
|
||||
SetServiceStatus(hStatus, &ss);
|
||||
}
|
||||
|
||||
#define ID_TRAYICON 100
|
||||
#define ID_QUIT 101
|
||||
#define ID_EDIT_CONFIG 102
|
||||
#define ID_SEPARATOR 103
|
||||
#define ID_INSTALL_SERVICE 104
|
||||
#define ID_REMOVE_SERVICE 105
|
||||
#define ID_ICON 200
|
||||
static NOTIFYICONDATA TrayIcon;
|
||||
|
||||
static void edit_config_file(void) {
|
||||
const char **names, *value;
|
||||
FILE *fp;
|
||||
int i;
|
||||
char cmd[200];
|
||||
|
||||
// Create config file if it is not present yet
|
||||
if ((fp = fopen(config_file, "r")) != NULL) {
|
||||
fclose(fp);
|
||||
} else if ((fp = fopen(config_file, "a+")) != NULL) {
|
||||
fprintf(fp,
|
||||
"# Mongoose web server configuration file.\n"
|
||||
"# Lines starting with '#' and empty lines are ignored.\n"
|
||||
"# For detailed description of every option, visit\n"
|
||||
"# http://code.google.com/p/mongoose/wiki/MongooseManual\n\n");
|
||||
names = mg_get_valid_option_names();
|
||||
for (i = 0; names[i] != NULL; i += 3) {
|
||||
value = mg_get_option(ctx, names[i]);
|
||||
fprintf(fp, "# %s %s\n", names[i + 1], *value ? value : "<value>");
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "notepad.exe %s", config_file);
|
||||
WinExec(cmd, SW_SHOW);
|
||||
}
|
||||
|
||||
static void show_error(void) {
|
||||
char buf[256];
|
||||
@@ -341,6 +372,272 @@ static void show_error(void) {
|
||||
MessageBox(NULL, buf, "Error", MB_OK);
|
||||
}
|
||||
|
||||
static void *align(void *ptr, DWORD alig) {
|
||||
ULONG ul = (ULONG) ptr;
|
||||
ul += alig;
|
||||
ul &= ~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;
|
||||
int i, id;
|
||||
|
||||
fprintf(fp, "%s", config_file_top_comment);
|
||||
options = mg_get_valid_option_names();
|
||||
for (i = 0; options[i] != NULL; i += 3) {
|
||||
name = options[i + 1];
|
||||
id = ID_CONTROLS + i / 3;
|
||||
if (is_boolean_option(name)) {
|
||||
snprintf(value, sizeof(value), "%s",
|
||||
IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
|
||||
} else {
|
||||
GetDlgItemText(hDlg, id, value, sizeof(value));
|
||||
}
|
||||
default_value = options[i + 2] == NULL ? "" : options[i + 2];
|
||||
// If value is the same as default, skip it
|
||||
if (strcmp(value, default_value) != 0) {
|
||||
fprintf(fp, "%s %s\n", name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
|
||||
FILE *fp;
|
||||
int i;
|
||||
const char *name, *value, **options = mg_get_valid_option_names();
|
||||
|
||||
switch (msg) {
|
||||
case WM_CLOSE:
|
||||
DestroyWindow(hDlg);
|
||||
break;
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam)) {
|
||||
case ID_SAVE:
|
||||
EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE);
|
||||
if ((fp = fopen(config_file, "w+")) != NULL) {
|
||||
save_config(hDlg, fp);
|
||||
fclose(fp);
|
||||
mg_stop(ctx);
|
||||
start_mongoose(__argc, __argv);
|
||||
}
|
||||
EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
|
||||
break;
|
||||
case ID_RESET_DEFAULTS:
|
||||
for (i = 0; options[i] != NULL; i += 3) {
|
||||
name = options[i + 1];
|
||||
value = options[i + 2] == NULL ? "" : options[i + 2];
|
||||
if (is_boolean_option(name)) {
|
||||
CheckDlgButton(hDlg, ID_CONTROLS + i / 3, !strcmp(value, "yes") ?
|
||||
BST_CHECKED : BST_UNCHECKED);
|
||||
} else {
|
||||
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; options[i] != NULL; i += 3) {
|
||||
name = options[i + 1];
|
||||
if ((is_filename_option(name) || is_directory_option(name)) &&
|
||||
LOWORD(wParam) == ID_CONTROLS + i / 3 + ID_FILE_BUTTONS_DELTA) {
|
||||
OPENFILENAME of;
|
||||
BROWSEINFO bi;
|
||||
char path[PATH_MAX] = "";
|
||||
|
||||
memset(&of, 0, sizeof(of));
|
||||
of.lStructSize = sizeof(of);
|
||||
of.hwndOwner = (HWND) hDlg;
|
||||
of.lpstrFile = path;
|
||||
of.nMaxFile = sizeof(path);
|
||||
of.lpstrInitialDir = mg_get_option(ctx, "document_root");
|
||||
of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR;
|
||||
|
||||
memset(&bi, 0, sizeof(bi));
|
||||
bi.hwndOwner = (HWND) hDlg;
|
||||
bi.lpszTitle = "Choose WWW root directory:";
|
||||
bi.ulFlags = BIF_RETURNONLYFSDIRS;
|
||||
|
||||
if (is_directory_option(name)) {
|
||||
SHGetPathFromIDList(SHBrowseForFolder(&bi), path);
|
||||
} else {
|
||||
GetOpenFileName(&of);
|
||||
}
|
||||
|
||||
if (path[0] != '\0') {
|
||||
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WM_INITDIALOG:
|
||||
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_SMALL, (LPARAM) hIcon);
|
||||
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_BIG, (LPARAM) hIcon);
|
||||
SetWindowText(hDlg, "Mongoose settings");
|
||||
SetFocus(GetDlgItem(hDlg, ID_SAVE));
|
||||
for (i = 0; options[i] != NULL; i += 3) {
|
||||
name = options[i + 1];
|
||||
value = mg_get_option(ctx, name);
|
||||
if (is_boolean_option(name)) {
|
||||
CheckDlgButton(hDlg, ID_CONTROLS + i / 3, !strcmp(value, "yes") ?
|
||||
BST_CHECKED : BST_UNCHECKED);
|
||||
} else {
|
||||
SetDlgItemText(hDlg, ID_CONTROLS + i / 3, value == NULL ? "" : value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type,
|
||||
DWORD id, DWORD style, WORD x, WORD y,
|
||||
WORD cx, WORD cy, const char *caption) {
|
||||
DLGITEMTEMPLATE *tp;
|
||||
LPWORD p;
|
||||
|
||||
dia->cdit++;
|
||||
|
||||
*mem = align(*mem, 3);
|
||||
tp = (DLGITEMTEMPLATE *) *mem;
|
||||
|
||||
tp->id = (WORD)id;
|
||||
tp->style = style;
|
||||
tp->dwExtendedStyle = 0;
|
||||
tp->x = x;
|
||||
tp->y = y;
|
||||
tp->cx = cx;
|
||||
tp->cy = cy;
|
||||
|
||||
p = align(*mem + sizeof(*tp), 1);
|
||||
*p++ = 0xffff;
|
||||
*p++ = type;
|
||||
|
||||
while (*caption != '\0') {
|
||||
*p++ = (WCHAR) *caption++;
|
||||
}
|
||||
*p++ = 0;
|
||||
p = align(p, 1);
|
||||
|
||||
*p++ = 0;
|
||||
*mem = (unsigned char *) p;
|
||||
}
|
||||
|
||||
static void show_settings_dialog() {
|
||||
#define HEIGHT 15
|
||||
#define WIDTH 400
|
||||
#define LABEL_WIDTH 80
|
||||
|
||||
unsigned char mem[4096], *p;
|
||||
const char **option_names, *long_option_name;
|
||||
DWORD style;
|
||||
DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
|
||||
WORD i, cl, x, y, width, nelems = 0;
|
||||
static int guard;
|
||||
|
||||
static struct {
|
||||
DLGTEMPLATE template; // 18 bytes
|
||||
WORD menu, class;
|
||||
wchar_t caption[1];
|
||||
WORD fontsiz;
|
||||
wchar_t fontface[7];
|
||||
} dialog_header = {{WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE |
|
||||
DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW, 0, 200, 200, WIDTH, 0},
|
||||
0, 0, L"", 8, L"Tahoma"};
|
||||
|
||||
if (guard == 0) {
|
||||
guard++;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
(void) memset(mem, 0, sizeof(mem));
|
||||
(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] != NULL; i += 3) {
|
||||
long_option_name = option_names[i + 1];
|
||||
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)) {
|
||||
style |= ES_NUMBER;
|
||||
cl = 0x81;
|
||||
style |= WS_BORDER | ES_AUTOHSCROLL;
|
||||
} else if (is_boolean_option(long_option_name)) {
|
||||
cl = 0x80;
|
||||
style |= BS_AUTOCHECKBOX;
|
||||
} else if (is_filename_option(long_option_name) ||
|
||||
is_directory_option(long_option_name)) {
|
||||
style |= WS_BORDER | ES_AUTOHSCROLL;
|
||||
width -= 20;
|
||||
cl = 0x81;
|
||||
add_control(&p, dia, 0x80,
|
||||
ID_CONTROLS + (i / 3) + ID_FILE_BUTTONS_DELTA,
|
||||
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
|
||||
(WORD) (x + width + LABEL_WIDTH + 5),
|
||||
y, 15, 12, "...");
|
||||
} else {
|
||||
cl = 0x81;
|
||||
style |= WS_BORDER | ES_AUTOHSCROLL;
|
||||
}
|
||||
add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
|
||||
x, y, LABEL_WIDTH, HEIGHT, long_option_name);
|
||||
add_control(&p, dia, cl, ID_CONTROLS + (i / 3), style,
|
||||
(WORD) (x + LABEL_WIDTH), y, width, 12, "");
|
||||
nelems++;
|
||||
}
|
||||
|
||||
y = (WORD) (((nelems + 1) / 2 + 1) * HEIGHT + 5);
|
||||
add_control(&p, dia, 0x80, ID_GROUP, WS_CHILD | WS_VISIBLE |
|
||||
BS_GROUPBOX, 5, 5, WIDTH - 10, y, " Settings ");
|
||||
y += 10;
|
||||
add_control(&p, dia, 0x80, ID_SAVE,
|
||||
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
|
||||
WIDTH - 70, y, 65, 12, "Save Settings");
|
||||
add_control(&p, dia, 0x80, ID_RESET_DEFAULTS,
|
||||
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
|
||||
WIDTH - 140, y, 65, 12, "Reset to defaults");
|
||||
add_control(&p, dia, 0x82, ID_STATIC,
|
||||
WS_CHILD | WS_VISIBLE | WS_DISABLED,
|
||||
5, y, 180, 12, server_name);
|
||||
|
||||
dia->cy = ((nelems + 1) / 2 + 1) * HEIGHT + 30;
|
||||
DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) NULL);
|
||||
guard--;
|
||||
}
|
||||
|
||||
static int manage_service(int action) {
|
||||
static const char *service_name = "Mongoose";
|
||||
SC_HANDLE hSCM = NULL, hService = NULL;
|
||||
@@ -409,14 +706,19 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
||||
mg_stop(ctx);
|
||||
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
case ID_EDIT_CONFIG:
|
||||
edit_config_file();
|
||||
return 0;
|
||||
case ID_SETTINGS:
|
||||
show_settings_dialog();
|
||||
break;
|
||||
case ID_INSTALL_SERVICE:
|
||||
case ID_REMOVE_SERVICE:
|
||||
manage_service(LOWORD(wParam));
|
||||
break;
|
||||
case ID_CONNECT:
|
||||
printf("[%s]\n", get_url_to_first_open_port(ctx));
|
||||
ShellExecute(NULL, "open", get_url_to_first_open_port(ctx),
|
||||
NULL, NULL, SW_SHOW);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WM_USER:
|
||||
@@ -436,7 +738,9 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
||||
AppendMenu(hMenu, MF_STRING | (!service_installed ? MF_GRAYED : 0),
|
||||
ID_REMOVE_SERVICE, "Deinstall service");
|
||||
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
|
||||
AppendMenu(hMenu, MF_STRING, ID_EDIT_CONFIG, "Edit config file");
|
||||
AppendMenu(hMenu, MF_STRING, ID_CONNECT, "Start browser");
|
||||
AppendMenu(hMenu, MF_STRING, ID_SETTINGS, "Edit Settings");
|
||||
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
|
||||
AppendMenu(hMenu, MF_STRING, ID_QUIT, "Exit");
|
||||
GetCursorPos(&pt);
|
||||
SetForegroundWindow(hWnd);
|
||||
@@ -473,10 +777,11 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
|
||||
ShowWindow(hWnd, SW_HIDE);
|
||||
|
||||
TrayIcon.cbSize = sizeof(TrayIcon);
|
||||
TrayIcon.uID = ID_TRAYICON;
|
||||
TrayIcon.uID = ID_ICON;
|
||||
TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
TrayIcon.hIcon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON),
|
||||
IMAGE_ICON, 16, 16, 0);
|
||||
TrayIcon.hIcon = hIcon = LoadImage(GetModuleHandle(NULL),
|
||||
MAKEINTRESOURCE(ID_ICON),
|
||||
IMAGE_ICON, 16, 16, 0);
|
||||
TrayIcon.hWnd = hWnd;
|
||||
snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", server_name);
|
||||
TrayIcon.uCallbackMessage = WM_USER;
|
||||
@@ -499,14 +804,16 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
|
||||
@end
|
||||
|
||||
@implementation Mongoose
|
||||
- (void) openBrowser {
|
||||
- (void) openBrowser {
|
||||
[[NSWorkspace sharedWorkspace]
|
||||
openURL:[NSURL URLWithString:
|
||||
[NSString stringWithUTF8String:"http://www.yahoo.com"]]];
|
||||
}
|
||||
- (void) editConfig {
|
||||
- (void) editConfig {
|
||||
create_config_file(config_file);
|
||||
[[NSWorkspace sharedWorkspace]
|
||||
openFile:@"mongoose.conf" withApplication:@"TextEdit"];
|
||||
openFile:[NSString stringWithUTF8String:config_file]
|
||||
withApplication:@"TextEdit"];
|
||||
}
|
||||
- (void)shutDown{
|
||||
[NSApp terminate:nil];
|
||||
|
||||
+101
-85
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2004-2012 Sergey Lyubka
|
||||
// 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
|
||||
@@ -30,8 +30,10 @@
|
||||
#endif
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma warning (disable : 4127) // conditional expression is constant: introduced by FD_SET(..)
|
||||
#pragma warning (disable : 4204) // non-constant aggregate initializer: issued due to missing C99 support
|
||||
// conditional expression is constant: introduced by FD_SET(..)
|
||||
#pragma warning (disable : 4127)
|
||||
// non-constant aggregate initializer: issued due to missing C99 support
|
||||
#pragma warning (disable : 4204)
|
||||
#endif
|
||||
|
||||
// Disable WIN32_LEAN_AND_MEAN.
|
||||
@@ -149,9 +151,7 @@ typedef DWORD pthread_t;
|
||||
|
||||
static int pthread_mutex_lock(pthread_mutex_t *);
|
||||
static int pthread_mutex_unlock(pthread_mutex_t *);
|
||||
|
||||
static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len);
|
||||
|
||||
struct file;
|
||||
static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p);
|
||||
|
||||
@@ -176,13 +176,23 @@ typedef struct DIR {
|
||||
struct dirent result;
|
||||
} DIR;
|
||||
|
||||
#ifndef HAS_POLL
|
||||
struct pollfd {
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
#define POLLIN 1
|
||||
#endif
|
||||
|
||||
|
||||
// Mark required libraries
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
|
||||
#else // UNIX specific
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/poll.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/time.h>
|
||||
@@ -231,7 +241,7 @@ typedef int SOCKET;
|
||||
#include <lauxlib.h>
|
||||
#endif
|
||||
|
||||
#define MONGOOSE_VERSION "3.5"
|
||||
#define MONGOOSE_VERSION "3.6"
|
||||
#define PASSWORDS_FILE_NAME ".htpasswd"
|
||||
#define CGI_ENVIRONMENT_SIZE 4096
|
||||
#define MAX_CGI_ENVIR_VARS 64
|
||||
@@ -435,11 +445,11 @@ struct file {
|
||||
// Describes listening socket, or socket which was accept()-ed by the master
|
||||
// thread and queued for future handling by the worker thread.
|
||||
struct socket {
|
||||
struct socket *next; // Linkage
|
||||
SOCKET sock; // Listening socket
|
||||
union usa lsa; // Local socket address
|
||||
union usa rsa; // Remote socket address
|
||||
int is_ssl; // Is socket SSL-ed
|
||||
unsigned is_ssl:1; // Is port SSL-ed
|
||||
unsigned ssl_redir:1; // Is port supposed to redirect everything to SSL port
|
||||
};
|
||||
|
||||
// NOTE(lsm): this enum shoulds be in sync with the config_options below.
|
||||
@@ -456,7 +466,7 @@ enum {
|
||||
static const char *config_options[] = {
|
||||
"C", "cgi_pattern", "**.cgi$|**.pl$|**.php$",
|
||||
"E", "cgi_environment", NULL,
|
||||
"G", "put_delete_passwords_file", NULL,
|
||||
"G", "put_delete_auth_file", NULL,
|
||||
"I", "cgi_interpreter", NULL,
|
||||
"P", "protect_uri", NULL,
|
||||
"R", "authentication_domain", "mydomain.com",
|
||||
@@ -465,7 +475,7 @@ static const char *config_options[] = {
|
||||
"a", "access_log_file", NULL,
|
||||
"d", "enable_directory_listing", "yes",
|
||||
"e", "error_log_file", NULL,
|
||||
"g", "global_passwords_file", NULL,
|
||||
"g", "global_auth_file", NULL,
|
||||
"i", "index_files", "index.html,index.htm,index.cgi,index.shtml,index.php",
|
||||
"k", "enable_keep_alive", "no",
|
||||
"l", "access_control_list", NULL,
|
||||
@@ -490,6 +500,7 @@ struct mg_context {
|
||||
void *user_data; // User-defined data
|
||||
|
||||
struct socket *listening_sockets;
|
||||
int num_listening_sockets;
|
||||
|
||||
volatile int num_threads; // Number of threads
|
||||
pthread_mutex_t mutex; // Protects (max|num)_threads
|
||||
@@ -1158,7 +1169,7 @@ static int mg_mkdir(const char *path, int mode) {
|
||||
mg_strlcpy(buf, path, sizeof(buf));
|
||||
change_slashes_to_backslashes(buf);
|
||||
|
||||
(void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
|
||||
(void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, ARRAY_SIZE(wbuf));
|
||||
|
||||
return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
|
||||
}
|
||||
@@ -1231,6 +1242,33 @@ static struct dirent *readdir(DIR *dir) {
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef HAVE_POLL
|
||||
static int poll(struct pollfd *pfd, int n, int milliseconds) {
|
||||
struct timeval tv;
|
||||
fd_set set;
|
||||
int i, result;
|
||||
|
||||
tv.tv_sec = milliseconds / 1000;
|
||||
tv.tv_usec = (milliseconds % 1000) * 1000;
|
||||
FD_ZERO(&set);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
FD_SET((SOCKET) pfd[i].fd, &set);
|
||||
pfd[i].revents = 0;
|
||||
}
|
||||
|
||||
if ((result = select(0, &set, NULL, NULL, &tv)) > 0) {
|
||||
for (i = 0; i < n; i++) {
|
||||
if (FD_ISSET(pfd[i].fd, &set)) {
|
||||
pfd[i].revents = POLLIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif // HAVE_POLL
|
||||
|
||||
#define set_close_on_exec(x) // No FD_CLOEXEC on Windows
|
||||
|
||||
int mg_start_thread(mg_thread_func_t f, void *p) {
|
||||
@@ -1470,17 +1508,14 @@ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
|
||||
// reading, must give up and close the connection and exit serving thread.
|
||||
static int wait_until_socket_is_readable(struct mg_connection *conn) {
|
||||
int result;
|
||||
struct timeval tv;
|
||||
fd_set set;
|
||||
struct pollfd pfd;
|
||||
|
||||
do {
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 300 * 1000;
|
||||
FD_ZERO(&set);
|
||||
FD_SET(conn->client.sock, &set);
|
||||
result = select(conn->client.sock + 1, &set, NULL, NULL, &tv);
|
||||
if(result == 0 && conn->ssl != NULL) {
|
||||
result = SSL_pending(conn->ssl);
|
||||
pfd.fd = conn->client.sock;
|
||||
pfd.events = POLLIN;
|
||||
result = poll(&pfd, 1, 200);
|
||||
if (result == 0 && conn->ssl != NULL) {
|
||||
result = SSL_pending(conn->ssl);
|
||||
}
|
||||
} while ((result == 0 || (result < 0 && ERRNO == EINTR)) &&
|
||||
conn->ctx->stop_flag == 0);
|
||||
@@ -1609,7 +1644,8 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
|
||||
// vsnprintf() error, give up
|
||||
len = -1;
|
||||
cry(conn, "%s(%s, ...): vsnprintf() error", __func__, fmt);
|
||||
} else if (len > (int) sizeof(mem) && (buf = (char *) malloc(len + 1)) != NULL) {
|
||||
} else if (len > (int) sizeof(mem) &&
|
||||
(buf = (char *) malloc(len + 1)) != NULL) {
|
||||
// Local buffer is not large enough, allocate big buffer on heap
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, len + 1, fmt, ap);
|
||||
@@ -4141,9 +4177,9 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
|
||||
if ((s = strrchr(fname, '/')) == NULL) {
|
||||
s = fname;
|
||||
}
|
||||
// Open file in binary mode with exclusive lock set
|
||||
// Open file in binary mode. TODO: set an exclusive lock.
|
||||
snprintf(path, sizeof(path), "%s/%s", destination_dir, s);
|
||||
if ((fp = fopen(path, "wbx")) == NULL) {
|
||||
if ((fp = fopen(path, "wb")) == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4272,12 +4308,11 @@ static void handle_request(struct mg_connection *conn) {
|
||||
}
|
||||
|
||||
static void close_all_listening_sockets(struct mg_context *ctx) {
|
||||
struct socket *sp, *tmp;
|
||||
for (sp = ctx->listening_sockets; sp != NULL; sp = tmp) {
|
||||
tmp = sp->next;
|
||||
(void) closesocket(sp->sock);
|
||||
free(sp);
|
||||
int i;
|
||||
for (i = 0; i < ctx->num_listening_sockets; i++) {
|
||||
closesocket(ctx->listening_sockets[i].sock);
|
||||
}
|
||||
free(ctx->listening_sockets);
|
||||
}
|
||||
|
||||
// Valid listening port specification is: [ip_address:]port[s]
|
||||
@@ -4297,11 +4332,13 @@ static int parse_port_string(const struct vec *vec, struct socket *so) {
|
||||
} else if (sscanf(vec->ptr, "%d%n", &port, &len) != 1 ||
|
||||
len <= 0 ||
|
||||
len > (int) vec->len ||
|
||||
(vec->ptr[len] && vec->ptr[len] != 's' && vec->ptr[len] != ',')) {
|
||||
(vec->ptr[len] && vec->ptr[len] != 's' &&
|
||||
vec->ptr[len] != 'r' && vec->ptr[len] != ',')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
so->is_ssl = vec->ptr[len] == 's';
|
||||
so->ssl_redir = vec->ptr[len] == 'r';
|
||||
#if defined(USE_IPV6)
|
||||
so->lsa.sin6.sin6_family = AF_INET6;
|
||||
so->lsa.sin6.sin6_port = htons((uint16_t) port);
|
||||
@@ -4316,9 +4353,8 @@ static int parse_port_string(const struct vec *vec, struct socket *so) {
|
||||
static int set_ports_option(struct mg_context *ctx) {
|
||||
const char *list = ctx->config[LISTENING_PORTS];
|
||||
int on = 1, success = 1;
|
||||
SOCKET sock;
|
||||
struct vec vec;
|
||||
struct socket so, *listener;
|
||||
struct socket so;
|
||||
|
||||
while (success && (list = next_option(list, &vec, NULL)) != NULL) {
|
||||
if (!parse_port_string(&vec, &so)) {
|
||||
@@ -4329,11 +4365,11 @@ static int set_ports_option(struct mg_context *ctx) {
|
||||
(ctx->ssl_ctx == NULL || ctx->config[SSL_CERTIFICATE] == NULL)) {
|
||||
cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
|
||||
success = 0;
|
||||
} else if ((sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
|
||||
} else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
|
||||
INVALID_SOCKET ||
|
||||
// On Windows, SO_REUSEADDR is recommended only for
|
||||
// broadcast UDP sockets
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &on,
|
||||
setsockopt(so.sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &on,
|
||||
sizeof(on)) != 0 ||
|
||||
// Set TCP keep-alive. This is needed because if HTTP-level
|
||||
// keep-alive is enabled, and client resets the connection,
|
||||
@@ -4342,27 +4378,22 @@ static int set_ports_option(struct mg_context *ctx) {
|
||||
// handshake will figure out that the client is down and
|
||||
// will close the server end.
|
||||
// Thanks to Igor Klopov who suggested the patch.
|
||||
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
|
||||
setsockopt(so.sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
|
||||
sizeof(on)) != 0 ||
|
||||
bind(sock, &so.lsa.sa, sizeof(so.lsa)) != 0 ||
|
||||
listen(sock, SOMAXCONN) != 0) {
|
||||
closesocket(sock);
|
||||
bind(so.sock, &so.lsa.sa, sizeof(so.lsa)) != 0 ||
|
||||
listen(so.sock, SOMAXCONN) != 0) {
|
||||
closesocket(so.sock);
|
||||
cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
|
||||
(int) vec.len, vec.ptr, strerror(ERRNO));
|
||||
success = 0;
|
||||
} else if ((listener = (struct socket *)
|
||||
calloc(1, sizeof(*listener))) == NULL) {
|
||||
// NOTE(lsm): order is important: call cry before closesocket(),
|
||||
// cause closesocket() alters the errno.
|
||||
cry(fc(ctx), "%s: %s", __func__, strerror(ERRNO));
|
||||
closesocket(sock);
|
||||
success = 0;
|
||||
} else {
|
||||
*listener = so;
|
||||
listener->sock = sock;
|
||||
set_close_on_exec(listener->sock);
|
||||
listener->next = ctx->listening_sockets;
|
||||
ctx->listening_sockets = listener;
|
||||
set_close_on_exec(so.sock);
|
||||
// TODO: handle realloc failure
|
||||
ctx->listening_sockets = realloc(ctx->listening_sockets,
|
||||
(ctx->num_listening_sockets + 1) *
|
||||
sizeof(ctx->listening_sockets[0]));
|
||||
ctx->listening_sockets[ctx->num_listening_sockets] = so;
|
||||
ctx->num_listening_sockets++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4443,13 +4474,6 @@ static int check_acl(struct mg_context *ctx, uint32_t remote_ip) {
|
||||
return allowed == '+';
|
||||
}
|
||||
|
||||
static void add_to_set(SOCKET fd, fd_set *set, int *max_fd) {
|
||||
FD_SET(fd, set);
|
||||
if (fd > (SOCKET) *max_fd) {
|
||||
*max_fd = (int) fd;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
static int set_uid_option(struct mg_context *ctx) {
|
||||
struct passwd *pw;
|
||||
@@ -4826,6 +4850,7 @@ static void process_new_connection(struct mg_connection *conn) {
|
||||
}
|
||||
conn->birth_time = time(NULL);
|
||||
handle_request(conn);
|
||||
conn->request_info.ev_data = (void *) conn->status_code;
|
||||
call_user(conn, MG_REQUEST_COMPLETE);
|
||||
log_access(conn);
|
||||
}
|
||||
@@ -4884,7 +4909,8 @@ static int consume_socket(struct mg_context *ctx, struct socket *sp) {
|
||||
return !ctx->stop_flag;
|
||||
}
|
||||
|
||||
static void worker_thread(struct mg_context *ctx) {
|
||||
static void *worker_thread(void *thread_func_param) {
|
||||
struct mg_context *ctx = thread_func_param;
|
||||
struct mg_connection *conn;
|
||||
|
||||
conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
|
||||
@@ -4929,6 +4955,7 @@ static void worker_thread(struct mg_context *ctx) {
|
||||
(void) pthread_mutex_unlock(&ctx->mutex);
|
||||
|
||||
DEBUG_TRACE(("exiting"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Master thread adds accepted socket to a queue
|
||||
@@ -4977,11 +5004,10 @@ static void accept_new_connection(const struct socket *listener,
|
||||
}
|
||||
}
|
||||
|
||||
static void master_thread(struct mg_context *ctx) {
|
||||
fd_set read_set;
|
||||
struct timeval tv;
|
||||
struct socket *sp;
|
||||
int max_fd;
|
||||
static void *master_thread(void *thread_func_param) {
|
||||
struct mg_context *ctx = thread_func_param;
|
||||
struct pollfd *pfd;
|
||||
int i;
|
||||
|
||||
// Increase priority of the master thread
|
||||
#if defined(_WIN32)
|
||||
@@ -4994,33 +5020,22 @@ static void master_thread(struct mg_context *ctx) {
|
||||
pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
|
||||
#endif
|
||||
|
||||
pfd = calloc(ctx->num_listening_sockets, sizeof(pfd[0]));
|
||||
while (ctx->stop_flag == 0) {
|
||||
FD_ZERO(&read_set);
|
||||
max_fd = -1;
|
||||
|
||||
// Add listening sockets to the read set
|
||||
for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
|
||||
add_to_set(sp->sock, &read_set, &max_fd);
|
||||
for (i = 0; i < ctx->num_listening_sockets; i++) {
|
||||
pfd[i].fd = ctx->listening_sockets[i].sock;
|
||||
pfd[i].events = POLLIN;
|
||||
}
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 200 * 1000;
|
||||
|
||||
if (select(max_fd + 1, &read_set, NULL, NULL, &tv) < 0) {
|
||||
#ifdef _WIN32
|
||||
// On windows, if read_set and write_set are empty,
|
||||
// select() returns "Invalid parameter" error
|
||||
// (at least on my Windows XP Pro). So in this case, we sleep here.
|
||||
mg_sleep(1000);
|
||||
#endif // _WIN32
|
||||
} else {
|
||||
for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
|
||||
if (ctx->stop_flag == 0 && FD_ISSET(sp->sock, &read_set)) {
|
||||
accept_new_connection(sp, ctx);
|
||||
if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
|
||||
for (i = 0; i < ctx->num_listening_sockets; i++) {
|
||||
if (ctx->stop_flag == 0 && pfd[i].revents == POLLIN) {
|
||||
accept_new_connection(&ctx->listening_sockets[i], ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(pfd);
|
||||
DEBUG_TRACE(("stopping workers"));
|
||||
|
||||
// Stop signal received: somebody called mg_stop. Quit.
|
||||
@@ -5051,6 +5066,7 @@ static void master_thread(struct mg_context *ctx) {
|
||||
// WARNING: This must be the very last thing this
|
||||
// thread does, as ctx becomes invalid after this line.
|
||||
ctx->stop_flag = 2;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void free_context(struct mg_context *ctx) {
|
||||
@@ -5172,11 +5188,11 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
|
||||
(void) pthread_cond_init(&ctx->sq_full, NULL);
|
||||
|
||||
// Start master (listening) thread
|
||||
mg_start_thread((mg_thread_func_t) master_thread, ctx);
|
||||
mg_start_thread(master_thread, ctx);
|
||||
|
||||
// Start worker threads
|
||||
for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
|
||||
if (mg_start_thread((mg_thread_func_t) worker_thread, ctx) != 0) {
|
||||
if (mg_start_thread(worker_thread, ctx) != 0) {
|
||||
cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
|
||||
} else {
|
||||
ctx->num_threads++;
|
||||
|
||||
+3
-2
@@ -47,7 +47,7 @@ struct mg_request_info {
|
||||
const char *name; // HTTP header name
|
||||
const char *value; // HTTP header value
|
||||
} http_headers[64]; // Maximum 64 headers
|
||||
void *user_data; // User data pointer passed to the mg_start()
|
||||
void *user_data; // User data pointer passed to mg_start()
|
||||
void *ev_data; // Event-specific data pointer
|
||||
};
|
||||
|
||||
@@ -61,7 +61,8 @@ enum mg_event {
|
||||
|
||||
// Mongoose has finished handling the request.
|
||||
// Callback return value is ignored.
|
||||
// ev_data contains NULL.
|
||||
// ev_data contains integer HTTP status code:
|
||||
// int http_reply_status_code = (long) request_info->ev_data;
|
||||
MG_REQUEST_COMPLETE,
|
||||
|
||||
// HTTP error must be returned to the client.
|
||||
|
||||
+1
-1
@@ -171,7 +171,7 @@ my $cmd = "$exe ".
|
||||
"-error_log_file debug.log ".
|
||||
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
|
||||
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
|
||||
'-put_delete_passwords_file test/passfile ' .
|
||||
'-put_delete_auth_file test/passfile ' .
|
||||
'-access_control_list -0.0.0.0/0,+127.0.0.1 ' .
|
||||
"-document_root $root ".
|
||||
"-hide_files_patterns **exploit.pl ".
|
||||
|
||||
Reference in new issue
Block a user