Compare commits

...
25 Commits
Author SHA1 Message Date
Sergey Lyubka 08afdfa8b6 better cleanup 2013-01-18 10:43:52 +00:00
Sergey Lyubka 05f7d65fc9 Added "Start browser" menu item 2013-01-18 10:38:58 +00:00
Sergey Lyubka a194e07545 2011 -> 2013 2013-01-18 09:45:54 +00:00
Sergey Lyubka dbb8a5bbf4 put_delete_passwords_file -> put_delete_auth_file 2013-01-18 09:45:36 +00:00
Sergey Lyubka d3005df019 Bug fix in Win32 mg_mkdir 2013-01-17 17:51:59 +00:00
Sergey Lyubka ef9b103a00 Started adding ssl redirection support 2013-01-17 17:50:18 +00:00
Sergey Lyubka f0a2924a7c Added config editor for Win32 2013-01-17 17:14:41 +00:00
Sergey Lyubka f109030cd0 Renamed: global_passwords_file -> global_auth_file, put_delete_passwords_file -> put_delete_auth_file. 2013-01-17 17:14:11 +00:00
Sergey Lyubka eebdd06532 Added comdlg32.lib to GUILIB 2013-01-17 17:13:15 +00:00
Sergey Lyubka ca2fc71a01 Changed systray icon 2013-01-17 11:26:50 +00:00
Sergey Lyubka e61e371c16 Changed user manual 2013-01-17 11:20:59 +00:00
Sergey Lyubka d5f07f5b9b Changed README 2013-01-17 11:07:53 +00:00
Sergey Lyubka 0a8d5dde51 Changed README 2013-01-17 10:59:42 +00:00
Sergey Lyubka 5646caa572 Using poll() instead of select() to prevent FD_SETSIZE overflows, for programs that open large number of descriptors 2013-01-16 23:45:03 +00:00
Sergey Lyubka c7e5573d08 Not building DLLs. Using separate target for cocoa mac app. 2013-01-16 23:43:06 +00:00
Sergey Lyubka b9a226d109 Adding command line options 2013-01-15 13:53:25 +00:00
Sergey Lyubka fcf8b86288 Adding command line options 2013-01-15 13:51:35 +00:00
Sergey Lyubka a8b08c6784 Fixing http://code.google.com/p/mongoose/issues/detail?id=392 2013-01-15 13:24:53 +00:00
Sergey Lyubka 1fb6f391a1 In mg_upload(), use fopen() with "wb" mode instead of "wbx", cause "x" is not understood by Mac and Windows. 2013-01-13 10:55:42 +00:00
Sergey Lyubka fcbc36ecde return 0; after PostQuitMessage() 2013-01-10 16:26:01 +00:00
Sergey Lyubka 70771f29ce Config edit implemented for mac 2013-01-10 16:24:42 +00:00
Sergey Lyubka 121391504f mongoose.app -> Mongoose.app 2013-01-10 16:24:12 +00:00
Sergey Lyubka 8455742440 Pass reply status code to MG_REQUEST_COMPLETE 2013-01-10 09:56:24 +00:00
Sergey Lyubka 5b786a5089 make release corrected 2013-01-09 21:21:04 +00:00
Sergey Lyubka a6e74e7707 3.5 -> 3.6 2013-01-09 20:55:21 +00:00
10 changed files with 631 additions and 160 deletions

No files matched your search

+11 -10
View File
@@ -36,20 +36,19 @@ LIB = lib$(PROG).so$(MONGOOSE_LIB_SUFFIX)
# "-Wl,--as-needed" turned on by default in cc command. # "-Wl,--as-needed" turned on by default in cc command.
# Also, this is turned in many other distros in static linkage builds. # Also, this is turned in many other distros in static linkage builds.
linux: 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) $(CC) mongoose.c main.c -o $(PROG) -ldl -pthread $(CFLAGS)
bsd: bsd:
$(CC) mongoose.c -shared -pthread -fpic -fPIC -o $(LIB) $(CFLAGS)
$(CC) mongoose.c main.c -pthread -o $(PROG) $(CFLAGS) $(CC) mongoose.c main.c -pthread -o $(PROG) $(CFLAGS)
mac: mac:
$(CC) mongoose.c -pthread -o $(LIB) -flat_namespace -bundle -undefined suppress $(CFLAGS) $(CC) mongoose.c main.c -pthread $(CFLAGS) -o $(PROG)
$(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 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: solaris:
$(CC) mongoose.c -pthread -lnsl -lsocket -fpic -fPIC -shared -o $(LIB) $(CFLAGS)
$(CC) mongoose.c main.c -pthread -lnsl -lsocket -o $(PROG) $(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 DBG = /DNDEBUG /O1
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /Gz /W3 /DNO_SSL_DL \ CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /Gz /W3 /DNO_SSL_DL \
/I$(MSVC)/include /DUSE_LUA /I$(LUA) /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 \ LINK = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
/subsystem:windows ws2_32.lib advapi32.lib cyassl.lib lua.lib /subsystem:windows ws2_32.lib advapi32.lib cyassl.lib lua.lib
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl /I$(MSVC)/INCLUDE \ 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 $(MSVC)/bin/rc build\res.rc
$(CL) /Ibuild main.c mongoose.c /GA $(LINK) build\res.res \ $(CL) /Ibuild main.c mongoose.c /GA $(LINK) build\res.res \
$(GUILIB) /out:$(PROG).exe $(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 # Build for Windows under MinGW
#MINGWDBG= -DDEBUG -O0 -ggdb #MINGWDBG= -DDEBUG -O0 -ggdb
@@ -154,7 +153,9 @@ tests:
perl test/test.pl $(TEST) perl test/test.pl $(TEST)
release: clean 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: 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
+27 -13
View File
@@ -2,8 +2,9 @@ Project Mission
--------------- ---------------
Project mission is to provide simple, functional, embeddable web server to 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 make it easy for application and device developers to implement web interface
application and devices, and to offer a simple web development environment. for their application and devices, and to offer a simple web development
environment.
Overview Overview
-------- --------
@@ -11,32 +12,45 @@ Overview
To accomplish it's mission, Mongoose keeps balance on functionality and To accomplish it's mission, Mongoose keeps balance on functionality and
simplicity by carefully selected list of features: 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 - Works on Windows, Mac, UNIX, iPhone, Android, and many other platforms
- Support for CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV - 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 - Resumed download, URL rewrite, IP-based ACL, Windows service
- Excluding files from serving by URI pattern (file blacklist) - Excluding files from serving by URI pattern (file blacklist)
- Download speed limit based on client subnet or URI pattern - Download speed limit based on client subnet or URI pattern
- Small footprint: executable size is 50 kB on Linux 2.6 i386 system - 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 - 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. - Simple and clean embedding API,
- 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) [mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)).
- Extensive documentation in form of [User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md) 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 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 Keep Sergey happy
----------------- -----------------
Since 2004, Mongoose is being constantly improved by me, Sergey Lyubka, a software engineer Since 2004, Mongoose is being constantly improved by me, Sergey Lyubka, a
from Galway, Ireland. My other software I give to the community for free is 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). [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 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 [Amazon wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority). Many thanks
already did so: T.Barmann, D.Hughes, J.C.Sloan, R.Romeo and 4 others. to all who already did so: T.Barmann, D.Hughes, J.C.Sloan, R.Romeo,
Appreciated guys, you keep my brains going! L.E.Spencer, and 4 others. Appreciated guys, you keep my brains going!
+133 -1
View File
@@ -51,12 +51,144 @@ Usage Examples
- How to share a Windows folder: copy mongoose executable to a folder and - How to share a Windows folder: copy mongoose executable to a folder and
double-click the executable. The folder should be accessible via double-click the executable. The folder should be accessible via
[http://localhost:8080](http://localhost:8080) in any browser. [http://localhost:8080](http://localhost:8080) in any browser.
- How to start mongoose at UNIX startup time in daemon mode, serving - 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 &` `/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 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 Common Problems
--------------- ---------------
+1 -1
View File
@@ -1 +1 @@
200 ICON DISCARDABLE "systray.ico" 100 ICON DISCARDABLE "systray.ico"
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 17 KiB

+1 -1
View File
@@ -25,4 +25,4 @@ windows:
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS) $(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS)
clean: 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
+354 -47
View File
@@ -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 // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -40,6 +40,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#include <winsvc.h> #include <winsvc.h>
#include <shlobj.h>
#define PATH_MAX MAX_PATH #define PATH_MAX MAX_PATH
#define S_ISDIR(x) ((x) & _S_IFDIR) #define S_ISDIR(x) ((x) & _S_IFDIR)
#define DIRSEP '\\' #define DIRSEP '\\'
@@ -110,6 +111,53 @@ static void show_usage_and_exit(void) {
exit(EXIT_FAILURE); 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) { static void verify_document_root(const char *root) {
const char *p, *path; const char *p, *path;
char buf[PATH_MAX]; char buf[PATH_MAX];
@@ -188,19 +236,21 @@ static void process_command_line_arguments(char *argv[], char **options) {
// Loop over the lines in config file // Loop over the lines in config file
while (fgets(line, sizeof(line), fp) != NULL) { while (fgets(line, sizeof(line), fp) != NULL) {
line_no++; line_no++;
// Ignore empty lines and comments // Ignore empty lines and comments
for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++; for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
if (line[i] == '#' || line[i] == '\0') if (line[i] == '#' || line[i] == '\0') {
continue; continue;
}
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) { 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); (void) fclose(fp);
} }
@@ -266,9 +316,25 @@ static void start_mongoose(int argc, char *argv[]) {
} }
#ifdef _WIN32 #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 ss;
static SERVICE_STATUS_HANDLE hStatus; static SERVICE_STATUS_HANDLE hStatus;
static const char *service_magic_argument = "--"; static const char *service_magic_argument = "--";
static NOTIFYICONDATA TrayIcon;
static void WINAPI ControlHandler(DWORD code) { static void WINAPI ControlHandler(DWORD code) {
if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) { if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
@@ -296,41 +362,6 @@ static void WINAPI ServiceMain(void) {
SetServiceStatus(hStatus, &ss); 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) { static void show_error(void) {
char buf[256]; char buf[256];
@@ -341,6 +372,272 @@ static void show_error(void) {
MessageBox(NULL, buf, "Error", MB_OK); 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 int manage_service(int action) {
static const char *service_name = "Mongoose"; static const char *service_name = "Mongoose";
SC_HANDLE hSCM = NULL, hService = NULL; SC_HANDLE hSCM = NULL, hService = NULL;
@@ -409,14 +706,19 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
mg_stop(ctx); mg_stop(ctx);
Shell_NotifyIcon(NIM_DELETE, &TrayIcon); Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
PostQuitMessage(0); PostQuitMessage(0);
break; return 0;
case ID_EDIT_CONFIG: case ID_SETTINGS:
edit_config_file(); show_settings_dialog();
break; break;
case ID_INSTALL_SERVICE: case ID_INSTALL_SERVICE:
case ID_REMOVE_SERVICE: case ID_REMOVE_SERVICE:
manage_service(LOWORD(wParam)); manage_service(LOWORD(wParam));
break; 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; break;
case WM_USER: 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), AppendMenu(hMenu, MF_STRING | (!service_installed ? MF_GRAYED : 0),
ID_REMOVE_SERVICE, "Deinstall service"); ID_REMOVE_SERVICE, "Deinstall service");
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, ""); 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"); AppendMenu(hMenu, MF_STRING, ID_QUIT, "Exit");
GetCursorPos(&pt); GetCursorPos(&pt);
SetForegroundWindow(hWnd); SetForegroundWindow(hWnd);
@@ -473,9 +777,10 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
ShowWindow(hWnd, SW_HIDE); ShowWindow(hWnd, SW_HIDE);
TrayIcon.cbSize = sizeof(TrayIcon); TrayIcon.cbSize = sizeof(TrayIcon);
TrayIcon.uID = ID_TRAYICON; TrayIcon.uID = ID_ICON;
TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
TrayIcon.hIcon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON), TrayIcon.hIcon = hIcon = LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(ID_ICON),
IMAGE_ICON, 16, 16, 0); IMAGE_ICON, 16, 16, 0);
TrayIcon.hWnd = hWnd; TrayIcon.hWnd = hWnd;
snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", server_name); snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", server_name);
@@ -505,8 +810,10 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
[NSString stringWithUTF8String:"http://www.yahoo.com"]]]; [NSString stringWithUTF8String:"http://www.yahoo.com"]]];
} }
- (void) editConfig { - (void) editConfig {
create_config_file(config_file);
[[NSWorkspace sharedWorkspace] [[NSWorkspace sharedWorkspace]
openFile:@"mongoose.conf" withApplication:@"TextEdit"]; openFile:[NSString stringWithUTF8String:config_file]
withApplication:@"TextEdit"];
} }
- (void)shutDown{ - (void)shutDown{
[NSApp terminate:nil]; [NSApp terminate:nil];
+100 -84
View File
@@ -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 // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -30,8 +30,10 @@
#endif #endif
#if defined (_MSC_VER) #if defined (_MSC_VER)
#pragma warning (disable : 4127) // conditional expression is constant: introduced by FD_SET(..) // conditional expression is constant: introduced by FD_SET(..)
#pragma warning (disable : 4204) // non-constant aggregate initializer: issued due to missing C99 support #pragma warning (disable : 4127)
// non-constant aggregate initializer: issued due to missing C99 support
#pragma warning (disable : 4204)
#endif #endif
// Disable WIN32_LEAN_AND_MEAN. // 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_lock(pthread_mutex_t *);
static int pthread_mutex_unlock(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); static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len);
struct file; struct file;
static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p); static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p);
@@ -176,13 +176,23 @@ typedef struct DIR {
struct dirent result; struct dirent result;
} DIR; } DIR;
#ifndef HAS_POLL
struct pollfd {
int fd;
short events;
short revents;
};
#define POLLIN 1
#endif
// Mark required libraries // Mark required libraries
#pragma comment(lib, "Ws2_32.lib") #pragma comment(lib, "Ws2_32.lib")
#else // UNIX specific #else // UNIX specific
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/select.h> #include <sys/poll.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/time.h> #include <sys/time.h>
@@ -231,7 +241,7 @@ typedef int SOCKET;
#include <lauxlib.h> #include <lauxlib.h>
#endif #endif
#define MONGOOSE_VERSION "3.5" #define MONGOOSE_VERSION "3.6"
#define PASSWORDS_FILE_NAME ".htpasswd" #define PASSWORDS_FILE_NAME ".htpasswd"
#define CGI_ENVIRONMENT_SIZE 4096 #define CGI_ENVIRONMENT_SIZE 4096
#define MAX_CGI_ENVIR_VARS 64 #define MAX_CGI_ENVIR_VARS 64
@@ -435,11 +445,11 @@ struct file {
// Describes listening socket, or socket which was accept()-ed by the master // Describes listening socket, or socket which was accept()-ed by the master
// thread and queued for future handling by the worker thread. // thread and queued for future handling by the worker thread.
struct socket { struct socket {
struct socket *next; // Linkage
SOCKET sock; // Listening socket SOCKET sock; // Listening socket
union usa lsa; // Local socket address union usa lsa; // Local socket address
union usa rsa; // Remote 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. // NOTE(lsm): this enum shoulds be in sync with the config_options below.
@@ -456,7 +466,7 @@ enum {
static const char *config_options[] = { static const char *config_options[] = {
"C", "cgi_pattern", "**.cgi$|**.pl$|**.php$", "C", "cgi_pattern", "**.cgi$|**.pl$|**.php$",
"E", "cgi_environment", NULL, "E", "cgi_environment", NULL,
"G", "put_delete_passwords_file", NULL, "G", "put_delete_auth_file", NULL,
"I", "cgi_interpreter", NULL, "I", "cgi_interpreter", NULL,
"P", "protect_uri", NULL, "P", "protect_uri", NULL,
"R", "authentication_domain", "mydomain.com", "R", "authentication_domain", "mydomain.com",
@@ -465,7 +475,7 @@ static const char *config_options[] = {
"a", "access_log_file", NULL, "a", "access_log_file", NULL,
"d", "enable_directory_listing", "yes", "d", "enable_directory_listing", "yes",
"e", "error_log_file", NULL, "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", "i", "index_files", "index.html,index.htm,index.cgi,index.shtml,index.php",
"k", "enable_keep_alive", "no", "k", "enable_keep_alive", "no",
"l", "access_control_list", NULL, "l", "access_control_list", NULL,
@@ -490,6 +500,7 @@ struct mg_context {
void *user_data; // User-defined data void *user_data; // User-defined data
struct socket *listening_sockets; struct socket *listening_sockets;
int num_listening_sockets;
volatile int num_threads; // Number of threads volatile int num_threads; // Number of threads
pthread_mutex_t mutex; // Protects (max|num)_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)); mg_strlcpy(buf, path, sizeof(buf));
change_slashes_to_backslashes(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; return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
} }
@@ -1231,6 +1242,33 @@ static struct dirent *readdir(DIR *dir) {
return result; 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 #define set_close_on_exec(x) // No FD_CLOEXEC on Windows
int mg_start_thread(mg_thread_func_t f, void *p) { int mg_start_thread(mg_thread_func_t f, void *p) {
@@ -1470,16 +1508,13 @@ 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. // reading, must give up and close the connection and exit serving thread.
static int wait_until_socket_is_readable(struct mg_connection *conn) { static int wait_until_socket_is_readable(struct mg_connection *conn) {
int result; int result;
struct timeval tv; struct pollfd pfd;
fd_set set;
do { do {
tv.tv_sec = 0; pfd.fd = conn->client.sock;
tv.tv_usec = 300 * 1000; pfd.events = POLLIN;
FD_ZERO(&set); result = poll(&pfd, 1, 200);
FD_SET(conn->client.sock, &set); if (result == 0 && conn->ssl != NULL) {
result = select(conn->client.sock + 1, &set, NULL, NULL, &tv);
if(result == 0 && conn->ssl != NULL) {
result = SSL_pending(conn->ssl); result = SSL_pending(conn->ssl);
} }
} while ((result == 0 || (result < 0 && ERRNO == EINTR)) && } while ((result == 0 || (result < 0 && ERRNO == EINTR)) &&
@@ -1609,7 +1644,8 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
// vsnprintf() error, give up // vsnprintf() error, give up
len = -1; len = -1;
cry(conn, "%s(%s, ...): vsnprintf() error", __func__, fmt); 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 // Local buffer is not large enough, allocate big buffer on heap
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf(buf, len + 1, fmt, ap); 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) { if ((s = strrchr(fname, '/')) == NULL) {
s = fname; 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); snprintf(path, sizeof(path), "%s/%s", destination_dir, s);
if ((fp = fopen(path, "wbx")) == NULL) { if ((fp = fopen(path, "wb")) == NULL) {
break; break;
} }
@@ -4272,12 +4308,11 @@ static void handle_request(struct mg_connection *conn) {
} }
static void close_all_listening_sockets(struct mg_context *ctx) { static void close_all_listening_sockets(struct mg_context *ctx) {
struct socket *sp, *tmp; int i;
for (sp = ctx->listening_sockets; sp != NULL; sp = tmp) { for (i = 0; i < ctx->num_listening_sockets; i++) {
tmp = sp->next; closesocket(ctx->listening_sockets[i].sock);
(void) closesocket(sp->sock);
free(sp);
} }
free(ctx->listening_sockets);
} }
// Valid listening port specification is: [ip_address:]port[s] // 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 || } else if (sscanf(vec->ptr, "%d%n", &port, &len) != 1 ||
len <= 0 || len <= 0 ||
len > (int) vec->len || 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; return 0;
} }
so->is_ssl = vec->ptr[len] == 's'; so->is_ssl = vec->ptr[len] == 's';
so->ssl_redir = vec->ptr[len] == 'r';
#if defined(USE_IPV6) #if defined(USE_IPV6)
so->lsa.sin6.sin6_family = AF_INET6; so->lsa.sin6.sin6_family = AF_INET6;
so->lsa.sin6.sin6_port = htons((uint16_t) port); 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) { static int set_ports_option(struct mg_context *ctx) {
const char *list = ctx->config[LISTENING_PORTS]; const char *list = ctx->config[LISTENING_PORTS];
int on = 1, success = 1; int on = 1, success = 1;
SOCKET sock;
struct vec vec; struct vec vec;
struct socket so, *listener; struct socket so;
while (success && (list = next_option(list, &vec, NULL)) != NULL) { while (success && (list = next_option(list, &vec, NULL)) != NULL) {
if (!parse_port_string(&vec, &so)) { 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)) { (ctx->ssl_ctx == NULL || ctx->config[SSL_CERTIFICATE] == NULL)) {
cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?"); cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
success = 0; 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 || INVALID_SOCKET ||
// On Windows, SO_REUSEADDR is recommended only for // On Windows, SO_REUSEADDR is recommended only for
// broadcast UDP sockets // 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 || sizeof(on)) != 0 ||
// Set TCP keep-alive. This is needed because if HTTP-level // Set TCP keep-alive. This is needed because if HTTP-level
// keep-alive is enabled, and client resets the connection, // 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 // handshake will figure out that the client is down and
// will close the server end. // will close the server end.
// Thanks to Igor Klopov who suggested the patch. // 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 || sizeof(on)) != 0 ||
bind(sock, &so.lsa.sa, sizeof(so.lsa)) != 0 || bind(so.sock, &so.lsa.sa, sizeof(so.lsa)) != 0 ||
listen(sock, SOMAXCONN) != 0) { listen(so.sock, SOMAXCONN) != 0) {
closesocket(sock); closesocket(so.sock);
cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__, cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
(int) vec.len, vec.ptr, strerror(ERRNO)); (int) vec.len, vec.ptr, strerror(ERRNO));
success = 0; 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 { } else {
*listener = so; set_close_on_exec(so.sock);
listener->sock = sock; // TODO: handle realloc failure
set_close_on_exec(listener->sock); ctx->listening_sockets = realloc(ctx->listening_sockets,
listener->next = ctx->listening_sockets; (ctx->num_listening_sockets + 1) *
ctx->listening_sockets = listener; 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 == '+'; 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) #if !defined(_WIN32)
static int set_uid_option(struct mg_context *ctx) { static int set_uid_option(struct mg_context *ctx) {
struct passwd *pw; struct passwd *pw;
@@ -4826,6 +4850,7 @@ static void process_new_connection(struct mg_connection *conn) {
} }
conn->birth_time = time(NULL); conn->birth_time = time(NULL);
handle_request(conn); handle_request(conn);
conn->request_info.ev_data = (void *) conn->status_code;
call_user(conn, MG_REQUEST_COMPLETE); call_user(conn, MG_REQUEST_COMPLETE);
log_access(conn); log_access(conn);
} }
@@ -4884,7 +4909,8 @@ static int consume_socket(struct mg_context *ctx, struct socket *sp) {
return !ctx->stop_flag; 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; struct mg_connection *conn;
conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE); 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); (void) pthread_mutex_unlock(&ctx->mutex);
DEBUG_TRACE(("exiting")); DEBUG_TRACE(("exiting"));
return NULL;
} }
// Master thread adds accepted socket to a queue // 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) { static void *master_thread(void *thread_func_param) {
fd_set read_set; struct mg_context *ctx = thread_func_param;
struct timeval tv; struct pollfd *pfd;
struct socket *sp; int i;
int max_fd;
// Increase priority of the master thread // Increase priority of the master thread
#if defined(_WIN32) #if defined(_WIN32)
@@ -4994,33 +5020,22 @@ static void master_thread(struct mg_context *ctx) {
pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param); pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
#endif #endif
pfd = calloc(ctx->num_listening_sockets, sizeof(pfd[0]));
while (ctx->stop_flag == 0) { while (ctx->stop_flag == 0) {
FD_ZERO(&read_set); for (i = 0; i < ctx->num_listening_sockets; i++) {
max_fd = -1; pfd[i].fd = ctx->listening_sockets[i].sock;
pfd[i].events = POLLIN;
// 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);
} }
tv.tv_sec = 0; if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
tv.tv_usec = 200 * 1000; for (i = 0; i < ctx->num_listening_sockets; i++) {
if (ctx->stop_flag == 0 && pfd[i].revents == POLLIN) {
if (select(max_fd + 1, &read_set, NULL, NULL, &tv) < 0) { accept_new_connection(&ctx->listening_sockets[i], ctx);
#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);
} }
} }
} }
} }
free(pfd);
DEBUG_TRACE(("stopping workers")); DEBUG_TRACE(("stopping workers"));
// Stop signal received: somebody called mg_stop. Quit. // 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 // WARNING: This must be the very last thing this
// thread does, as ctx becomes invalid after this line. // thread does, as ctx becomes invalid after this line.
ctx->stop_flag = 2; ctx->stop_flag = 2;
return NULL;
} }
static void free_context(struct mg_context *ctx) { 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); (void) pthread_cond_init(&ctx->sq_full, NULL);
// Start master (listening) thread // Start master (listening) thread
mg_start_thread((mg_thread_func_t) master_thread, ctx); mg_start_thread(master_thread, ctx);
// Start worker threads // Start worker threads
for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) { 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); cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
} else { } else {
ctx->num_threads++; ctx->num_threads++;
+3 -2
View File
@@ -47,7 +47,7 @@ struct mg_request_info {
const char *name; // HTTP header name const char *name; // HTTP header name
const char *value; // HTTP header value const char *value; // HTTP header value
} http_headers[64]; // Maximum 64 headers } 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 void *ev_data; // Event-specific data pointer
}; };
@@ -61,7 +61,8 @@ enum mg_event {
// Mongoose has finished handling the request. // Mongoose has finished handling the request.
// Callback return value is ignored. // 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, MG_REQUEST_COMPLETE,
// HTTP error must be returned to the client. // HTTP error must be returned to the client.
+1 -1
View File
@@ -171,7 +171,7 @@ my $cmd = "$exe ".
"-error_log_file debug.log ". "-error_log_file debug.log ".
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " . "-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " . "-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 ' . '-access_control_list -0.0.0.0/0,+127.0.0.1 ' .
"-document_root $root ". "-document_root $root ".
"-hide_files_patterns **exploit.pl ". "-hide_files_patterns **exploit.pl ".