Previous 199869 Revisions Next

r34317 Saturday 10th January, 2015 at 12:30:30 UTC by Miodrag Milanović
Added integral source of mongoose (nw)
[3rdparty/mongoose]LICENSE* README.md* mongoose.c* mongoose.h*
[3rdparty/mongoose/docs]API.md* AndroidBuild.md* BasicWebsite.md* Embed.md* FAQ.md* FileSharing.md* Internals.md* LuaSqlite.md* Options.md* PhpWebsite.md* ReleaseNotes.md* SSL.md* Usage.md*
[3rdparty/mongoose/examples]Makefile*
[3rdparty/mongoose/examples/big_upload]Makefile* big_upload.c*
[3rdparty/mongoose/examples/cookie_authentication]Makefile* cookie_auth.c* index.html* login.html*
[3rdparty/mongoose/examples/csharp]example.cs* mongoose.cs*
[3rdparty/mongoose/examples/digest_authentication]Makefile* digest_auth.c*
[3rdparty/mongoose/examples/file_upload]Makefile* file_upload.c*
[3rdparty/mongoose/examples/form_submit]Makefile* form_submit.c*
[3rdparty/mongoose/examples/hello_world]Makefile* hello_world.c*
[3rdparty/mongoose/examples/http_client]Makefile* http_client.c*
[3rdparty/mongoose/examples/mjpg_streamer]Makefile* mjpg_streamer.c*
[3rdparty/mongoose/examples/multi_threaded_server]Makefile* multi_threaded_server.c*
[3rdparty/mongoose/examples/proxy_server]Makefile* proxy_server.c* ssl_cert.pem*
[3rdparty/mongoose/examples/proxy_server/proxy_web_root]index.html*
[3rdparty/mongoose/examples/proxy_server/proxy_web_root/app1]index.html*
[3rdparty/mongoose/examples/proxy_server/proxy_web_root/app2]index.html*
[3rdparty/mongoose/examples/restful_api]Makefile* index.html* restful_api.c*
[3rdparty/mongoose/examples/send_file]Makefile* send_file.c*
[3rdparty/mongoose/examples/web_server]Makefile* web_server.c*
[3rdparty/mongoose/examples/websocket_chat]Makefile* index.html* websocket_chat.c*
[3rdparty/mongoose/examples/websocket_echo_server]Makefile* index.html* websocket_echo_server.c*
[3rdparty/mongoose/examples/websocket_ssl_proxy]Makefile* net_skeleton.h* ssl_wrapper.c* ssl_wrapper.h* ws_ssl.c* ws_ssl.html*
[3rdparty/mongoose/examples/websocket_ssl_proxy/certs]ws1_ca.pem* ws1_client.pem* ws1_server.pem* ws2_ca.pem* ws2_client.pem* ws2_server.pem*
[3rdparty/mongoose/jni]Android.mk*
[3rdparty/mongoose/scripts]embed_binary_files.pl*
[3rdparty/mongoose/test]Makefile* unit_test.c*
[src/lib]lib.mak
[src/lib/web]mongoose.c mongoose.h

trunk/3rdparty/mongoose/LICENSE
r0r242829
1Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
2Copyright (c) 2013 Cesanta Software Limited
3All rights reserved
4
5This code is dual-licensed: you can redistribute it and/or modify
6it under the terms of the GNU General Public License version 2 as
7published by the Free Software Foundation. For the terms of this
8license, see <http://www.gnu.org/licenses>.
9
10You are free to use this code under the terms of the GNU General
11Public License, but WITHOUT ANY WARRANTY; without even the implied
12warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13See the GNU General Public License for more details.
14
15Alternatively, you can license this code under a commercial
16license, as set out in <http://cesanta.com/>.
trunk/3rdparty/mongoose/README.md
r0r242829
1# <img src="http://cesanta.com/images/mongoose_logo.png" width="64" height="64"> Mongoose Web Server
2
3Mongoose is the most easy to use web server on the planet. A web server of choice for Web developers (PHP, Ruby, Python, etc) and Web designers.
4
5Mongoose is built on top of Libmongoose embedded library, which can turn
6anything into a web server in 5 minutes worth of effort and few lines of code.
7Libmongoose is used to serve Web GUI on embedded devices, implement RESTful
8services, RPC frameworks (e.g. JSON-RPC), handle telemetry data exchange, and
9perform many other tasks in various different industries including aerospace,
10manufacturing, finance, research, automotive, gaming, IT.
11
12
13   * [Mailing list](http://groups.google.com/group/mongoose-users)
14   * [Downloads](http://cesanta.com/products.shtml)
15   * [Documentation](http://cesanta.com/docs.shtml)
16
17Check out Fossa - our [embedded multi-protocol library](https://github.com/cesanta/fossa) with TCP,UDP,HTTP,Websocket,MQTT,DNS support, designed for Internet Of Things!
18
19# Features
20
21- Works on Windows, Mac, UNIX/Linux, iPhone, Android eCos, QNX
22and many other platforms
23- CGI, SSI, SSL, Digest auth, Websocket, WEbDAV, Resumed download,
24  URL rewrite, file blacklist
25- Custom error pages, Virtual hosts, IP-based ACL, Windows service,
26  HTTP/HTTPS client
27- Simple and clean
28  [embedding API](https://github.com/cesanta/mongoose/blob/master/mongoose.h).
29  The source is in single
30  [mongoose.c](https://github.com/cesanta/mongoose/blob/master/mongoose.c) file
31  to make embedding easy
32- Extremely lightweight, has a core of under 40kB and tiny runtime footprint
33- Asynchronous, non-blocking core supporting single- or multi-threaded usage
34- On the market since 2004 with over 1 million cumulative downloads
35- Stable, mature and tested, has several man-years invested
36  in continuous improvement and refinement
37
38# Screenshots
39
40Download, double-click to start, run browser -- that's all!
41
42![shot1](http://cesanta.com/images/tut_sharing/tut1.png)
43![shot2](http://cesanta.com/images/tut_sharing/tut2.png)
44
45![shot3](http://cesanta.com/images/tut_sharing/tut3.png)
46![shot4](http://cesanta.com/images/tut_sharing/tut4.png)
47
48# Acknowledgements
49
50Mongoose made better thanks to the contribution of following people:
51
52Arnout Vandecappelle, Benoît Amiaux, Boris Pek, Cody Hanson, Colin Leitner,
53Daniel Oaks, Eric Bakan, Erik Oomen, Filipp Kovalev, Ger Hobbelt,
54Hendrik Polczynski, Igor Okulist, Jay, Joe Mucchiello, John Safranek,
55José Miguel Gonçalves, Shueng Chuan, Katerina Blinova, Konstantin Sorokin,
56Marin Atanasov, Matt Healy, Mitch Hendrickson, Nigel Stewart, Pavel Khlebovich,
57Sebastian Reinhard, Stefan Doehla, abadc0de, nullable.type,
58T.Barmann, D.Hughes, J.C.Sloan, R.Romeo, L.E.Spencer, S.Kotay, R.M.Shorter,
59W.Mar, J.Wilander, Santa from Memphis, S.Davies, C.Beck,
60O.M.Vilhunen, C.Radik, G.Woodcock, M.Szczepkowski,
61Eternal Lands Dev Team, T.Tollet, C.Tangerino, G.Karsai, A.Bourgett,
62C.Blakemore, D.Fonaryov, T.Andrle, O.IJsselmuiden, R.Womack, M.Tomlinson,
63A.Slåttå, L.Farrell, J.D.P.Ballestero, V.Albaev, B.Harker, T.Scheffel, H.Klein,
64R.Merit, T.Bennett, H.Solis, A.Zincenko, M.S., S.Krul, K.Cooke, S.McCallum,
65F.Morenius, and 10 others.
66
67# Licensing
68
69Mongoose is released under commercial and
70[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) open
71source licenses. The GPLv2 open source License does not generally permit
72incorporating this software into non-open source programs.
73For those customers who do not wish to comply with the GPLv2 open
74source license requirements,
75[Cesanta Software](http://cesanta.com) offers a full,
76royalty-free commercial license and professional support
77without any of the GPL restrictions.
78
79# Other products by Cesanta Software: simple and effective
80
81- [SSL Wrapper](https://github.com/cesanta/ssl_wrapper) - application to
82  secure network communications
83- [Frozen](https://github.com/cesanta/frozen) - JSON parser and generator
84- [SLRE](https://github.com/cesanta/slre) - Super Light Regular Expression
85  library
86- [Net Skeleton](https://github.com/cesanta/net_skeleton) - framework for
87  building network applications
88- [SLDR](https://github.com/cesanta/sldr) - Super Light DNS Resolver
trunk/3rdparty/mongoose/docs/API.md
r0r242829
1# Mongoose API Reference
2
3    struct mg_server *mg_create_server(void *server_param);
4
5Creates web server instance. Returns opaque instance pointer, or NULL if
6there is not enough memory. `server_param`: Could be any pointer, or NULL.
7This pointer will be passed
8to the callback functions as `struct mg_connection::server_param` field.
9A common use case is to pass `this` pointer of the C++ wrapper class
10as `user_param`, to let the callback get the pointer to the C++ object.
11
12Note that this function doesn't make the
13server instance to serve. Serving is done by `mg_poll_server()` function.
14Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
15When server instance is created, it contains an information about
16the configuration and the state of each connection.
17Server instance is capable on listening on only one port. After creation,
18`struct mg_server` has a list
19of active connections and configuration parameters.
20
21Side-effect: on UNIX, `mg_create_server()` ignores SIGPIPE signals. If custom
22processing is required SIGPIPE, signal handler must be set up after
23calling `mg_create_server()`.
24
25Important: Mongoose does not install `SIGCHLD` handler. If CGI is used,
26`SIGCHLD` handler must be set up to reap CGI zombie processes.
27
28
29    void mg_destroy_server(struct mg_server **server);
30
31Deallocates web server instance, closes all pending connections, and makes
32server pointer a NULL pointer.
33
34    const char mg_set_option(struct mg_server *server, const char *name,
35                             const char *value);
36
37Sets a particular server option. Note that at least one option,
38`listening_port`, must be specified. To serve static files, `document_root`
39must be specified too. If `document_root` option is left unset, Mongoose
40will not access filesystem at all. `mg_set_option()` returns NULL if option was
41set successfully, otherwise it returns human-readable error string. It is
42allowed to call `mg_set_option()` by the same thread that does
43`mg_poll_server()` (Mongoose thread) and change server configuration while it
44is serving, in between `mg_poll_server()` calls.
45
46    int mg_poll_server(struct mg_server *server, int milliseconds);
47
48Performs one iteration of IO loop by iterating over all
49active connections, performing `select()` syscall on all sockets with a timeout
50of `milliseconds`. When `select()` returns, Mongoose
51does an IO for each socket that has data to be sent or received. Application
52code must call `mg_poll_server()` in a loop. It is an error to have more then
53one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
54that take `struct mg_server *` parameter. Mongoose does not
55mutex-protect `struct mg_server *`, therefore only single thread
56(Mongoose thread) should make Mongoose calls.
57
58`mg_poll_server()` calls user-specified event handler when certain events
59occur. Sequence of events for the accepted connection is this:
60
61   * `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
62      handler returns `MG_FALSE`, then Mongoose does not serve the request but
63      sends authorization request to the client. If `MG_TRUE` is returned,
64      then Mongoose continues on with the request.
65   * `MG_REQUEST` - Mongoose asks event handler to serve the request. If
66      event handler serves the request by sending a reply,
67      it should return `MG_TRUE`. Otherwise,
68      it should return `MG_FALSE` which tells Mongoose that request is not
69      served and Mongoose should serve it. For example, event handler might
70      choose to serve only RESTful API requests with URIs that start with
71      certain prefix, and let Mongoose serve all static files.
72      If event handler decides to serve the request, but doesn't have
73      all the data at the moment, it should return `MG_MORE`. That tells
74      Mongoose to keep the connection open after callback returns.
75
76      `mg_connection::connection_param` pointer is a placeholder to keep
77      user-specific data. For example, handler could decide to open a DB
78      connection and store DB connection handle in `connection_param`.
79   * `MG_POLL` is sent to every connection on every iteration of
80      `mg_poll_server()`. Event handler should return `MG_FALSE` to ignore
81      this event. If event handler returns `MG_TRUE`, then Mongoose assumes
82      that event handler has finished sending data, and Mongoose will
83      close the connection.
84   * `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
85      to the client. Event handler can choose to send a reply itself, in which
86      case event handler must return `MG_TRUE`. Otherwise, event handler must
87      return `MG_FALSE`.
88   * `MG_CLOSE` is sent when the connection is closed. This event is used
89      to cleanup per-connection state stored in `connection_param`
90      if it was allocated. Event handler return value is ignored.
91
92Sequence of events for the client connection is this:
93
94   * `MG_CONNECT` sent when Mongoose has connected to the remote host.
95   This event is sent to the connection initiated by `mg_connect()` call.
96   Connection status is held in `mg_connection::status_code`: if zero,
97   then connection was successful, otherwise connection was not established.
98   User should send a request upon successful connection.
99   Event handler should return `MG_TRUE` if connection was successful and
100   HTTP request has been sent. Otherwise, it should send `MG_FALSE`.
101   * `MG_REPLY` is sent when response has been received from the remote host.
102   If event handler sends another request, then it should return `MG_TRUE`.
103   Otherwise it should return `MG_FALSE` and Mongoose will close the connection.
104   * `MG_CLOSE` same as for the accepted connection.
105
106
107When mongoose buffers in HTTP request and successfully parses it, it sends
108`MG_REQUEST` event for GET requests immediately. For POST requests,
109Mongoose delays the call until the whole POST request is buffered in memory.
110POST data is available to the callback as `struct mg_connection::content`,
111and POST data length is in `struct mg_connection::content_len`.
112
113Note that websocket connections are treated the same way. Mongoose buffers
114websocket frame in memory, and calls event handler when frame is fully
115buffered. Frame data is available `struct mg_connection::content`, and
116data length is in `struct mg_connection::content_len`, i.e. very similar to
117the POST request. `struct mg_connection::is_websocket` flag indicates
118whether the request is websocket or not. Also, for websocket requests,
119there is `struct mg_connection::wsbits` field which contains first byte
120of the websocket frame which URI handler can examine. Note that to
121reply to the websocket client, `mg_websocket_write()` should be used.
122To reply to the plain HTTP client, `mg_write_data()` should be used.
123
124Return value: number of active connections.
125
126
127    const char **mg_get_valid_option_names(void);
128
129Returns a NULL-terminated array of option names and their default values.
130There are two entries per option in an array: an option name followed by a
131default value. A default value could be NULL. A NULL name indicates an end
132of the array.
133
134    const char *mg_get_option(const struct mg_server *server, const char *name);
135
136Returns the value of particular configuration parameter. If
137given parameter name is not valid, NULL is returned. For valid names, return
138value is guaranteed to be non-NULL. If parameter is not set, zero-length string
139is returned.
140
141    void mg_wakeup_server_ex(struct mg_server *, mg_handler_t func,
142                             const char *fmt, ...);
143
144Sends string message to a server. Function `func` is called for every active
145connection. String message is passed in `struct mg_connection::callback_param`.
146This function is designed to push data to the connected clients, and
147can be called from any thread. There is a limitation on the length of
148the message, currently at 8 kilobytes.
149
150    void mg_send_status(struct mg_connection *, int status_code);
151    void mg_send_header(struct mg_connection *, const char *name,
152                        const char *value);
153    void mg_send_data(struct mg_connection *, const void *data, int data_len);
154    void mg_printf_data(struct mg_connection *, const char *format, ...);
155
156These functions are used to construct a response to the client. HTTP response
157consists of three parts: a status line, zero or more HTTP headers,
158a response body.  Mongoose provides functions for all three parts:
159   * `mg_send_status()` is used to create status line. This function can be
160      called zero or once. If `mg_send_status()` is not called, then Mongoose
161      will send status 200 (success) implicitly.
162   * `mg_send_header()` adds HTTP header to the response. This function could
163      be called zero or more times.
164   * `mg_send_data()` and `mg_printf_data()` are used to send data to the
165     client. Note that Mongoose adds `Transfer-Encoding: chunked` header
166     implicitly, and sends data in chunks. Therefore, it is not necessary to
167     set `Content-Length` header. Note that `mg_send_data()` and
168     `mg_printf_data()` do not send data immediately. Instead, they spool
169     data in memory, and Mongoose sends that data later after URI handler
170     returns. If data to be sent is huge, an URI handler might
171     send data in pieces by saving state in
172     `struct mg_connection::connection_param` variable and returning `0`. Then
173     Mongoose will call a handler repeatedly after each socket write.
174
175<!-- -->
176     
177     void mg_send_file(struct mg_connection *, const char *path);
178
179Tells Mongoose to serve given file. Mongoose handles file according to
180it's extensions, i.e. Mongoose will invoke CGI script if `path` has CGI
181extension, it'll render SSI file if `path` has SSI extension, etc. If `path`
182points to a directory, Mongoose will show directory listing. If this function
183is used, no calls to `mg_send*` or `mg_printf*` functions must be made, and
184event handler must return `MG_MORE`.
185
186    int mg_websocket_write(struct mg_connection* conn, int opcode,
187                           const char *data, size_t data_len);
188
189Similar to `mg_write()`, but wraps the data into a websocket frame with a
190given websocket `opcode`. 
191
192    const char *mg_get_header(const struct mg_connection *, const char *name);
193
194Get the value of particular HTTP header. This is a helper function.
195It traverses http_headers array, and if the header is present in the array,
196returns its value. If it is not present, NULL is returned.
197
198
199    int mg_get_var(const struct mg_connection *conn, const char *var_name,
200                   char *buf, size_t buf_len);
201
202Gets HTTP form variable. Both POST buffer and query string are inspected.
203Form variable is url-decoded and written to the buffer. On success, this
204function returns the length of decoded variable. On error, -1 is returned if
205variable not found, and -2 is returned if destination buffer is too small
206to hold the variable. Destination buffer is guaranteed to be
207'\0' - terminated if it is not NULL or zero length.
208
209    int mg_parse_header(const char *hdr, const char *var_name, char *buf,
210                        size_t buf_size);
211
212This function parses HTTP header and fetches given variable's value in a buffer.
213A header should be like `x=123, y=345, z="other value"`. This function is
214designed to parse Cookie headers, Authorization headers, and similar. Returns
215the length of the fetched value, or 0 if variable not found.
216
217    int mg_modify_passwords_file(const char *passwords_file_name,
218                                 const char *domain,
219                                 const char *user,
220                                 const char *password);
221
222Add, edit or delete the entry in the passwords file. 
223This function allows an application to manipulate .htpasswd files on the
224fly by adding, deleting and changing user records. This is one of the
225several ways of implementing authentication on the server side. 
226If password is not NULL, entry is added (or modified if already exists).
227If password is NULL, entry is deleted. 
228Return: 1 on success, 0 on error.
229
230   
231    int mg_parse_multipart(const char *buf, int buf_len,
232                           char *var_name, int var_name_len,
233                           char *file_name, int file_name_len,
234                           const char **data, int *data_len);
235
236Parses a buffer that contains multipart form data. Stores chunk name
237in a `var_name` buffer. If chunk is an uploaded file, then `file_name`
238will have a file name. `data` and `data_len` will point to the chunk data.
239Returns number of bytes to skip to the next chunk.
240
241     struct mg_connection *mg_connect(struct mg_server *server,
242                                      const char *host, int port, int use_ssl);
243
244Create connection to the remote host. Returns `NULL` on error, non-null
245if the connection has been scheduled for connection. Upon a connection,
246Mongoose will send `MG_CONNECT` event to the event handler.
trunk/3rdparty/mongoose/docs/AndroidBuild.md
r0r242829
1# Mongoose Build on Android
2
3This is a small guide to help you run mongoose on Android. Currently it is
4tested on the HTC Wildfire. If you have managed to run it on other devices
5as well, please comment or drop an email in the mailing list.
6Note : You dont need root access to run mongoose on Android.
7
8- Clone Mongoose Git repo
9- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html)
10- Run `/path-to-ndk/ndk-build -C /path/to/mongoose`
11  That should generate mongoose/lib/armeabi/mongoose
12- Using the adb tool (you need to have Android SDK installed for that),
13  push the generated mongoose binary to `/data/local` folder on device.
14- From adb shell, navigate to `/data/local` and execute `./mongoose`.
15- To test if the server is running fine, visit your web-browser and
16  navigate to `http://127.0.0.1:8080` You should see the `Index of /` page.
17
18![screenshot](http://cesanta.com/images/android_build.png)
19
20
21Notes:
22
23- `jni` stands for Java Native Interface. Read up on Android NDK if you want
24  to know how to interact with the native C functions of mongoose in Android
25  Java applications.
26- TODO: A Java application that interacts with the native binary or a
27  shared library.
trunk/3rdparty/mongoose/docs/BasicWebsite.md
r0r242829
1How To Create Basic Website With Mongoose
2===========================================
3
4## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
5
6![screenshot](http://cesanta.com/images/tut_basic/tut1.png)
7
8## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited.
9
10![screenshot](http://cesanta.com/images/tut_basic/tut2.png)
11
12## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML code:
13
14![screenshot](http://cesanta.com/images/tut_basic/tut3.png)
15
16## 4. Save this file as `index.html`:
17
18![screenshot](http://cesanta.com/images/tut_basic/tut4.png)
19
20
21## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory:
22
23![screenshot](http://cesanta.com/images/tut_basic/tut5.png)
24
25## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
26
27![screenshot](http://cesanta.com/images/tut_basic/tut6.png)
28
29## 7. Click on the mongoose icon and choose "Go to my address" menu:
30![screenshot](http://cesanta.com/images/tut_basic/tut7.png)
31
32## 8. A browser will popup displaying `index.html` file. Now, you can expand your website by adding more content.
33
34![screenshot](http://cesanta.com/images/tut_basic/tut8.png)
trunk/3rdparty/mongoose/docs/Embed.md
r0r242829
1# Mongoose Embedding Guide
2
3Embedding Mongoose is done in two steps:
4
5   1. Copy
6    [mongoose.c](https://raw.github.com/cesanta/mongoose/master/mongoose.c) and
7    [mongoose.h](https://raw.github.com/cesanta/mongoose/master/mongoose.h)
8    to your application's source tree and include them in the build.
9   2. Somewhere in the application code, call `mg_create_server()` to create
10    a server, configure it with `mg_set_option()` and loop with
11    `mg_poll_server()` until done. Call `mg_destroy_server()` to cleanup.
12
13Here's a minimal application `app.c` that embeds mongoose:
14
15    #include "mongoose.h"
16
17    int main(void) {
18      struct mg_server *server = mg_create_server(NULL, NULL);
19      mg_set_option(server, "document_root", ".");      // Serve current directory
20      mg_set_option(server, "listening_port", "8080");  // Open port 8080
21
22      for (;;) {
23        mg_poll_server(server, 1000);   // Infinite loop, Ctrl-C to stop
24      }
25      mg_destroy_server(&server);
26
27      return 0;
28    }
29
30To compile it, put `mongoose.c`, `mongoose.h` and `app.c` into one
31folder, start terminal on UNIX or Visual Studio command line prompt on Windows,
32and run the following command:
33
34    cc app.c mongoose.c -pthread -o app     # on Unix
35    cl.exe app.c mongoose.c /TC /MD         # on Windows
36
37When run, this simple application opens port 8080 and serves static files,
38CGI files and lists directory content in the current working directory.
39
40It is possible to generate HTML page content. Mongoose can call user-defined
41function when certain events occur.
42That function is called _an event handler_, and it is the second parameter
43to `mg_create_server()` function. Here is the example event handler function:
44
45    int event_handler(struct mg_connection *conn, enum mg_event ev) {
46      switch (ev) {
47        case MG_AUTH: return MG_TRUE;
48        default: return MG_FALSE;
49      }
50    }
51
52Event handler is called by Mongoose with `struct mg_connection *`
53pointer and an event number. `struct mg_connection *conn`
54has all information about the request: HTTP headers, POST or websocket
55data buffer, etcetera. `enum mg_event ev` tells which exactly event is sent.
56For each event, an event handler returns a value which tells Mongoose how
57to behave.
58
59The sequence of events for every connection is this:
60
61   * `MG_AUTH` - Mongoose asks whether this connection is authorized. If event
62      handler returns `MG_FALSE`, then Mongoose does not serve the request but
63      sends authorization request to the client. If `MG_TRUE` is returned,
64      then Mongoose continues on with the request.
65   * `MG_REQUEST` - Mongoose asks event handler to serve the request. If
66      event handler serves the request by sending a reply,
67      it should return `MG_TRUE`. Otherwise,
68      it should return `MG_FALSE` which tells Mongoose that request is not
69      served and Mongoose should serve it. For example, event handler might
70      choose to serve only RESTful API requests with URIs that start with
71      certain prefix, and let Mongoose serve all static files.
72      If event handler decides to serve the request, but doesn't have
73      all the data at the moment, it should return `MG_MORE`. That tells
74      Mongoose to keep the connection open after callback returns.
75
76      `mg_connection::connection_param` pointer is a placeholder to keep
77      user-specific data. For example, handler could decide to open a DB
78      connection and store DB connection handle in `connection_param`.
79   * `MG_POLL` is sent to every connection on every iteration of
80      `mg_poll_server()`. Event handler should return `MG_FALSE` to ignore
81      this event. If event handler returns `MG_TRUE`, then Mongoose assumes
82      that event handler has finished sending data, and Mongoose will
83      close the connection.
84   * `MG_HTTP_ERROR` sent when Mongoose is about to send HTTP error back
85      to the client. Event handler can choose to send a reply itself, in which
86      case event handler must return `MG_TRUE`. Otherwise, event handler must
87      return `MG_FALSE`
88   * `MG_CLOSE` is sent when the connection is closed. This event is used
89      to cleanup per-connection state stored in `connection_param`
90      if it was allocated.
91
92Let's extend our minimal application example and
93create an URI that will be served by user's C code. The app will handle
94`/hello` URI by showing a hello message. So, when app is run,
95http://127.0.0.1:8080/hello will say hello, and here's the code:
96
97    #include <string.h>
98    #include "mongoose.h"
99
100    static int event_handler(struct mg_connection *conn, enum mg_event ev) {
101      if (ev == MG_AUTH) {
102        return MG_TRUE;   // Authorize all requests
103      } else if (ev == MG_REQUEST && !strcmp(conn->uri, "/hello")) {
104        mg_printf_data(conn, "%s", "Hello world");
105        return MG_TRUE;   // Mark as processed
106      } else {
107        return MG_FALSE;  // Rest of the events are not processed
108      }
109    }
110
111    int main(void) {
112      struct mg_server *server = mg_create_server(NULL, event_handler);
113      mg_set_option(server, "document_root", ".");
114      mg_set_option(server, "listening_port", "8080");
115
116      for (;;) {
117        mg_poll_server(server, 1000);  // Infinite loop, Ctrl-C to stop
118      }
119      mg_destroy_server(&server);
120
121      return 0;
122    }
123
124## Example code
125
126Mongoose source code contains number of examples, located in the
127[examples](https://github.com/cesanta/mongoose/blob/master/examples/) directory.
128To build any example, go to the respective directory and run `make`.
129
130## Compilation flags
131
132Below is the list of compilation flags that enable or disable certain
133features. By default, some features are enabled, and could be disabled
134by setting appropriate `NO_*` flag. Features that are disabled by default
135could be enabled by setting appropriate `USE_*` flag. Bare bones Mongoose
136is quite small, about 30 kilobytes of compiled x86 code. Each feature adds
137a couple of kilobytes to the executable size, and also has some runtime penalty.
138
139Note that some flags start with `NS_` prefix. This is because Mongoose uses
140[Net Skeleton](http://github.com/cesanta/net_skeleton) as a low-level
141networking engine. If user code has `#include <net_skeleton.h>`, then
142all Net Skeleton functions will be available too.
143
144
145    -DMONGOOSE_NO_AUTH          Disable MD5 authorization support
146    -DMONGOOSE_NO_CGI           Disable CGI support
147    -DMONGOOSE_NO_DAV           Disable WebDAV support
148                                (PUT, DELETE, MKCOL, PROPFIND methods)
149    -DMONGOOSE_NO_DIRECTORY_LISTING  Disable directory listing
150    -DMONGOOSE_NO_FILESYSTEM    Disables all file IO, serving from memory only
151    -DMONGOOSE_NO_LOGGING       Disable access/error logging
152    -DMONGOOSE_NO_THREADS
153    -DMONGOOSE_NO_WEBSOCKET     Disable WebSocket support
154    -DMONGOOSE_NO_USER          No concept of a user on used platform.
155                                (Platform does not provide getpwnam, setgid or setuid)
156
157    -DMONGOOSE_USE_IDLE_TIMEOUT_SECONDS=X Idle connection timeout, default is 30
158    -DMONGOOSE_USE_LUA          Enable Lua scripting
159    -DMONGOOSE_USE_LUA_SQLITE3  Enable sqlite3 binding for Lua
160    -DMONGOOSE_USE_POST_SIZE_LIMIT=X      POST requests larger than X will be
161                                          rejected, not set by default
162    -DMONGOOSE_USE_EXTRA_HTTP_HEADERS=X   Append X to the HTTP headers
163                                          for static files, empty by default
164
165    -DNS_ENABLE_DEBUG         Enables debug messages on stdout, very noisy
166    -DNS_ENABLE_SSL           Enable SSL
167    -DNS_ENABLE_IPV6          Enable IPv6 support
168    -DNS_ENABLE_HEXDUMP       Enables hexdump of sent and received traffic
169    -DNS_STACK_SIZE=X         Sets stack size to X for  ns_start_thread()
170    -DNS_DISABLE_THREADS      Disable threads support
171    -DNS_DISABLE_SOCKETPAIR   For systems without loopback interface
172    -DMONGOOSE_SEND_NS_EVENTS Send Net Skeleton events to the event handler
173                              in addition to the Mongoose events
trunk/3rdparty/mongoose/docs/FAQ.md
r0r242829
1# Mongoose FAQ
2
3## My Antivirus Software reports Mongoose as a security threat
4
5Mongoose doesn't contain any malicious logic. Antivirus reports a
6[false positive](http://en.wikipedia.org/wiki/Type_I_and_type_II_errors#False_positive_error).
7This is when certain byte sequence in Mongoose accidentally matches
8virus signature in the Antivirus database.
9
10## Download page doesn't work
11
12Please make sure Javascript is enabled in your browser, and that the
13antivirus software is not blocking the download.
14
15
16## MacOS message: "Mongoose.app is damaged and can’t be opened. You should move it to the Trash"
17
18This happens on newer MacOS systems. The reason for the message
19is the fact Mongoose.app is not digitally signed.
20Mongoose download procedure changes the app on the fly by injecting
21user information in the binary, making any prior digital signature void.
22Open "System Preferences" -> "Security"  and set "Allow apps downloaded from"
23to "Anywhere".  Revert the settings once Mongoose is installed.
24
25## PHP doesn't work: getting empty page, or 'File not found' error
26
27The reason for that is wrong paths to the interpreter. Remember that with PHP,
28correct interpreter is `php-cgi.exe` (`php-cgi` on UNIX). Solution: specify
29full path to the PHP interpreter, e.g.:
30
31    mongoose -cgi_interpreter /full/path/to/php-cgi
32
33## Mongoose fails to start
34
35If Mongoose exits immediately when run, this
36usually indicates a syntax error in the configuration file
37(named `mongoose.conf` by default) or the command-line arguments.
38Syntax checking is omitted from Mongoose to keep its size low. However,
39the Manual should be of help. Note: the syntax changes from time to time,
40so updating the config file might be necessary after executable update.
41
42### Embedding with OpenSSL on Windows might fail because of calling convention
43
44To force Mongoose to use `__stdcall` convention, add `/Gz` compilation
45flag to the Visual Studio project settings.
trunk/3rdparty/mongoose/docs/FileSharing.md
r0r242829
1How To Share Files With Mongoose
2===========================================
3
4## 1. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside the directory you want to share:
5
6![screenshot](http://cesanta.com/images/tut_sharing/tut1.png)
7
8## 2. Double-click mongoose executable. A browser will start automatically, an icon will appear on a system tray in the bottom right corner of the desktop:
9
10![screenshot](http://cesanta.com/images/tut_sharing/tut2.png)
11
12## 3. Click on the mongoose icon
13![screenshot](http://cesanta.com/images/tut_sharing/tut3.png)
14
15
16## 4. Click on "Go to my address" to launch a browser locally. Or, to access a folder from another machine, launch a browser and type in the URL:
17
18![screenshot](http://cesanta.com/images/tut_sharing/tut4.png)
trunk/3rdparty/mongoose/docs/Internals.md
r0r242829
1# Mongoose Internals
2
3Mongoose has single-threaded, event-driven, asynchronous, non-blocking core.
4`mg_create_server()` creates a web server instance. An instance is a container
5for the config options and list of active connections. To do the actual
6serving, user must call `mg_poll_server()`, which iterates over all
7active connections, performing `select()` syscall on all sockets with a
8timeout of specified number of milliseconds. When `select()` returns, Mongoose
9does an IO for each socket that has data to be sent or received. Application
10code must call `mg_poll_server()` in a loop.
11
12Mongoose server instance is designed to be used by a single thread.
13It is an error to have more then
14one thread calling `mg_poll_server()`, `mg_set_option()` or any other function
15that take `struct mg_server *` parameter. Mongoose does not
16mutex-protect `struct mg_server *`, therefore the best practice is
17to call server management functions from the same thread (an IO thread).
18On a multi-core systems, many server instances can be created, sharing the
19same listening socket and managed by separate threads (see [multi_threaded.c](https://github.com/cesanta/mongoose/blob/master/examples/multi_threaded.c))
20example.
21
22It is an error to pass and store `struct mg_connection *` pointers for
23later use to send data. The reason is that they can be invalidated by the
24next `mg_poll_server()` call. For such a task,
25there is `mg_iterate_over_connections()` API
26exists, which sends a callback function to the IO thread, then IO thread
27calls specified function for all active connection.
28
29When mongoose buffers in HTTP request and successfully parses it, it calls
30appropriate URI handler immediately for GET requests. For POST requests,
31Mongoose delays the call until the whole POST request is buffered in memory.
32POST data is available to the callback as `struct mg_connection::content`,
33and POST data length is in `struct mg_connection::content_len`.
34
35Note that websocket connections are treated the same way. Mongoose buffers
36websocket frame in memory, and calls URI handler when frame is fully
37buffered. Frame data is available `struct mg_connection::content`, and
38data length is in `struct mg_connection::content_len`, i.e. very similar to
39the POST request. `struct mg_connection::is_websocket` flag indicates
40whether the request is websocket or not. Also, for websocket requests,
41there is `struct mg_connection::wsbits` field which contains first byte
42of the websocket frame which URI handler can examine. Note that to
43reply to the websocket client, `mg_websocket_write()` should be used.
44To reply to the plain HTTP client, `mg_write()` should be used.
trunk/3rdparty/mongoose/docs/LuaSqlite.md
r0r242829
1# Mongoose Lua Server Pages
2
3Pre-built Windows and Mac mongoose binaries support Lua Server Pages
4functionality.
5That means it is possible to write PHP-like scripts with mongoose
6using Lua programming language instead of PHP. Lua is known
7for it's speed and small size. Mongoose uses Lua version 5.2.3, the
8documentation for it can be found at
9[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/).
10
11To create a Lua Page, make a file that is called `ANY_NAME.lp`. For example,
12`my_page.lp`. It is important to have a file
13name that ends up with `.lp`, cause this is the way mongoose recognises
14Lua Page file. The contents of the file, just like
15with PHP, is HTML with embedded Lua code. Lua code must be enclosed within
16`&lt;?  ?&gt;` blocks, and can appear anywhere on the page.
17
18Mongoose does not send HTTP headers for Lua pages. Therefore,
19every Lua Page must begin with HTTP status line and headers, like this:
20
21    <? mg.write('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') ?>
22    <html><body>
23      <span>Today is:</span> <? mg.write(os.date("%A")) ?>
24    </body></html>
25
26Note that this example uses function `mg.write()`, which prints data to the
27web page. Using function `mg.write()` is the way to generate web content from
28inside Lua code. In addition to `mg.write()`, all standard library functions
29are accessible from the Lua code (please check reference manual for details).
30Information about the request is available via the `mg.request_info` object.
31I contains request method, all headers, etcetera. Please refer to
32`struct mg_request_info` definition in
33[mongoose.h](https://github.com/cesanta/mongoose/blob/master/mongoose.h)
34to see what is available via the `mg.request_info` object.
35Check out [prime_numbers.lp](https://github.com/cesanta/mongoose/blob/master/examples/lua/prime_numbers.lp) for some example.
36
37Mongoose exports the following to the Lua Server Page:
38
39    mg.write(str)     -- writes string to the client
40    mg.onerror(msg)   -- error handler, can be overridden
41    mg.request_info   -- a table with request information
42
43Using Lua scripting it is easy to emulate SSI functionality. For example,
44to include the content of another file, one can write:
45
46    <? mg.write(io.open('MY_FILE.TXT'):read('*all')) ?>
47
48To serve a Lua Page, mongoose creates Lua context. That context is used for
49all Lua blocks within the page. That means, all Lua blocks on the same page
50share the same context. If one block defines a variable, for example, that
51variable is visible in all following blocks.
trunk/3rdparty/mongoose/docs/Options.md
r0r242829
1# Mongoose Configuration Options
2
3### access\_control\_list
4An Access Control List (ACL) allows restrictions to be put on the list of IP
5addresses which have access to the web server. In the case of the Mongoose
6web server, the ACL is a comma separated list of IP subnets, where each
7subnet is prepended by either a `-` or a `+` sign. A plus sign means allow,
8where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`,
9this means to deny only that single IP address.
10
11Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
12all accesses. On each request the full list is traversed, and
13the 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. Note that if the option is set, then all accesses are forbidden
14by default. Thus in a previous example, `-0.0.0.0` part is not necessary.
15For example, `$mongoose access_control_list +10.0.0.0/8`
16means disallow all, allow subnet 10/8 only.
17
18To learn more about subnet masks, see the
19[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
20
21Default: not set, all accesses are allowed.
22
23### access\_log\_file
24Path to a file for access logs. Either full path, or relative to the
25mongoose executable. Default: not set, no query logging is done.
26
27### auth_domain
28Authorization realm used in `.htpasswd` authorization. Default: `mydomain.com`
29
30### cgi_interpreter
31Path to an executable to be used use as an interpreter for __all__ CGI scripts
32regardless script extension.  Default: not set, Mongoose looks at
33[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\).
34
35For example, if both PHP and perl CGIs are used, then
36`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the
37respective CGI scripts. Note that paths should be either full file paths,
38or file paths relative to the directory where mongoose executable is located.
39
40If all CGIs use the same interpreter, for example they are all PHP, then
41`cgi_interpreter` option can be set to the path to `php-cgi.exe` executable and
42shebang line in the CGI scripts can be omitted.
43**Note**: PHP scripts must use `php-cgi.exe`, not `php.exe`.
44
45### cgi_pattern
46All files that match `cgi_pattern` are treated as CGI files. Default pattern
47allows CGI files be anywhere. To restrict CGIs to a certain directory,
48use `/path/to/cgi-bin/**.cgi` as a pattern. Note that **full file path** is
49matched against the pattern, not the URI.
50
51When Mongoose starts CGI program, it creates new environment for it (in
52contrast, usually child program inherits the environment from parent). Several
53environment variables however are inherited from Mongoose's environment,
54they are: `PATH`, `TMP`, `TEMP`, `TMPDIR`, `PERLLIB`, `MONGOOSE_CGI`. On UNIX
55it is also `LD_LIBRARY_PATH`. On Windows it is also `COMSPEC`, `SYSTEMROOT`,
56`SystemDrive`, `ProgramFiles`, `ProgramFiles(x86)`, `CommonProgramFiles(x86)`.
57
58Default: `**.cgi$|**.pl$|**.php$`
59
60### dav\_auth\_file
61Authentication file for WebDAV mutation requests: `PUT`, `DELETE`, `MKCOL`.
62The format of that file is the same as for the `.htpasswd` file
63used for digest authentication. It can be created and managed by
64`mongoose -A` command. Default: not set, WebDAV mutations are disallowed.
65
66### document_root
67A directory to serve. Default: current working directory.
68
69### enable\_directory\_listing
70Enable directory listing, either `yes` or `no`. Default: `yes`.
71
72### enable\_proxy
73Enable proxy functionality, either `yes` or `no`. If set to `yes`, then
74browsers can be configured to use Mongoose as a proxy. Default: `no`.
75
76
77### extra\_mime\_types
78Extra mime types to recognize, in form `extension1=type1,extension2=type2,...`.
79Extension must include dot.  Example:
80`mongoose -extra_mime_types .cpp=plain/text,.java=plain/text`. Default: not set.
81
82
83### global\_auth\_file
84Path to a global passwords file, either full path or relative to the mongoose
85executable. If set, per-directory `.htpasswd` files are ignored,
86and all requests are authorised against that file. Use `mongoose -A` to
87manage passwords, or third party utilities like
88[htpasswd-generator](http://www.askapache.com/online-tools/htpasswd-generator).
89Default: not set, per-directory `.htpasswd` files are respected.
90
91### hide\_files\_patterns
92A pattern for the files to hide. Files that match the pattern will not
93show up in directory listing and return `404 Not Found` if requested. Pattern
94must be for a file name only, not including directory name, e.g.
95`mongoose -hide_files_patterns secret.txt|even_more_secret.txt`. Default:
96not set.
97
98### index_files
99Comma-separated list of files to be treated as directory index
100files. Default: `index.html,index.htm,index.cgi,index.shtml,index.php`
101
102### listening_port
103Port to listen on. Port could be prepended by the specific IP address to bind
104to, e.g. `mongoose -listening_port 127.0.0.1:8080`. Otherwise Mongoose
105will bind to all addresses. To enable SSL, build Mongoose with
106`-DNS_ENABLE_SSL` compilation option, and specify `listening_port` as
107`ssl://PORT:SSL_CERTIFICATE.PEM`. Example SSL listener:
108`mongoose -listening_port ssl://8043:ssl_cert.pem`. Note that PEM file should
109be in PEM format, and must have both certificate and private key in it,
110concatenated together. More than one listening port can be specified,
111separated by comma,
112for example `mongoose -listening_port 8080,8000`. Default: 8080.
113
114### run\_as\_user
115Switch to given user credentials after startup. UNIX-only. This option is
116required when mongoose needs to bind on privileged port on UNIX, e.g.
117
118    $ sudo mongoose -listening_port 80 -run_as_user nobody
119
120Default: not set.
121
122### url\_rewrites
123Comma-separated list of URL rewrites in the form of
124`uri_pattern=file_or_directory_path`. When Mongoose receives the request,
125it constructs the file name to show by combining `document_root` and the URI.
126However, if the rewrite option is used and `uri_pattern` matches the
127requested URI, then `document_root` is ignored. Instead,
128`file_or_directory_path` is used, which should be a full path name or
129a path relative to the web server's current working directory. Note that
130`uri_pattern`, as all mongoose patterns, is a prefix pattern. If `uri_pattern`
131is a number, then it is treated as HTTP error code, and `file_or_directory_path`
132should be an URI to redirect to. Mongoose will issue `302` temporary redirect
133to the specified URI with following parameters:
134`?code=HTTP_ERROR_CODE&orig_uri=ORIGINAL_URI&query_string=QUERY_STRING`.
135
136If `uri_pattern` starts with `@` symbol, then Mongoose compares
137it with the `HOST` header of the request. If they are equal, Mongoose sets
138document root to `file_or_directory_path`, implementing virtual hosts support.
139
140Examples:
141
142    # Redirect all accesses to `.doc` files to a special script
143    mongoose -url_rewrites **.doc$=/path/to/cgi-bin/handle_doc.cgi
144
145    # Implement user home directories support
146    mongoose -url_rewrites /~joe/=/home/joe/,/~bill=/home/bill/
147
148    # Redirect 404 errors to a specific error page
149    mongoose -url_rewrites 404=/cgi-bin/error.cgi
150
151    # Virtual hosts example: serve foo.com domain from different directory
152    mongoose -url_rewrites @foo.com=/var/www/foo.com
153
154Default: not set.
trunk/3rdparty/mongoose/docs/PhpWebsite.md
r0r242829
1How To Create A PHP Website With Mongoose
2===========================================
3
4## 1. Create a directory which will contain your website files. For example, on drive `C:\`, create a directory called `my_website`:
5
6![screenshot](http://cesanta.com/images/tut_php/tut1.png)
7
8## 2. Inside `my_website` directory, create a new file called "index". This will be the default web page shown when the website is visited.
9
10![screenshot](http://cesanta.com/images/tut_php/tut2.png)
11
12## 3. Open index file with your favorite editor (for example, Notepad) and enter some HTML / PHP code:
13
14![screenshot](http://cesanta.com/images/tut_php/tut3.png)
15
16## 4. Save this file as `index.php`:
17
18![screenshot](http://cesanta.com/images/tut_php/tut4.png)
19
20
21## 5. Download Mongoose executable from http://cesanta.com/mongoose.shtml and copy the executable inside `my_website` directory:
22
23![screenshot](http://cesanta.com/images/tut_php/tut5.png)
24
25## 6. Double-click mongoose executable. An icon will appear on a system tray in the bottom right corner of the desktop:
26
27![screenshot](http://cesanta.com/images/tut_php/tut6.png)
28
29## 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:
30![screenshot](http://cesanta.com/images/tut_php/tut7.png)
31
32## 8. Click on the mongoose icon and choose "Edit Settings" menu.:
33![screenshot](http://cesanta.com/images/tut_php/tut8.png)
34
35## 9. A settings dialog will appear. Click on `cgi_interpreter` button:
36
37![screenshot](http://cesanta.com/images/tut_php/tut9.png)
38
39## 10. Choose `C:\php5\php-cgi.exe` and click "Save Settings":
40
41![screenshot](http://cesanta.com/images/tut_php/tut10.png)
42
43## 11. Click on the mongoose icon and choose "Go to my address" menu:
44![screenshot](http://cesanta.com/images/tut_php/tut11.png)
45
46
47## 12. A browser will popup displaying `index.php`.
48
49![screenshot](http://cesanta.com/images/tut_php/tut12.png)
trunk/3rdparty/mongoose/docs/ReleaseNotes.md
r0r242829
1# Mongoose Release Notes
2
3## Release 5.5, October 28 2014
4
5Changes in Libmongoose library:
6
7- Added new API function: `mg_forward()` for proxying functionality
8- Added new API function: `mg_send_file_data()` for sending file data
9- Added new utility API functions: `mg_mmap() and mg_munmap()`
10- Changed the way SSL settings are handled: removed `ssl_certificate` and
11  `ssl_ca_certificate` options, and instead made `listening_port` accept
12  `ssl://PORT:SSL_CERT:CA_CERT` notation
13- Added ability to listen on multiple ports, see `listening_port` documentation
14- Added `enable_proxy` option
15- Added [cookie_authentication](https://github.com/cesanta/mongoose/tree/master/examples/cookie_authentication) example
16- Added [websocket\_ssl\_proxy](https://github.com/cesanta/mongoose/tree/master/examples/websocket_ssl_proxy) example
17- Added [http_client](https://github.com/cesanta/mongoose/tree/master/examples/http_client) example
18- Increased default 'idle connection' timeout from 30 to 300 seconds
19- Fixed MinGW build
20- Refactored all examples, put each in it's own directory with dedicated build
21- Many smaller bugfixed, including SSL, CGI, API, proxy, etc
22
23Changes in pre-compiled binaries:
24
25- Support for multiple listening ports
26- Fixed CGI handling for scripts that specify interpreter in the hashbang line
27
28## Release 5.4, July 28 2014
29
30Changes in Libmongoose library:
31
32- Added `hexdump_file` option for low-level request/reply debugging
33- Added `mg_template()` API function for generating HTML pages from
34    templates with expansions
35- Fixed `struct mg_connection::local_ip` handling, `mg_set_option()`
36    behavior with NULL values
37- Added `mg_send_file()` call to send arbitrary file to the client
38- Added `mg_terminate_ssl()` for SSL termination functionality
39- Added HTTP proxy support, `enable_proxy` config option
40- Added `mg_next()` for iterating over existing active connections
41- Added client-side SSL auth, `ssl_ca_certificate` option
42- Added `mg_wakeup_server_ex()` for pushing messages to existing connections
43- Added `MG_WS_HANDSHAKE` and `MG_WS_CONNECT` events that are sent on
44     Websocket handshake is connection establishment, respectively
45- Removed server-side Lua support
46- Filesystem access, reading from socket/SSL performance improvements
47- DAV PROPFIND memory leak fixed
48- Added `big_upload.c` and enhanced `upload.c` example
49- Added `proxy.c` example that demonstrates proxy functionality and SSE pushes
50- Added `websocket2.c` example that shows simple web chat implementation
51    over websockets
52- Various minor fixes
53
54
55Changes in pre-compiled binaries:
56
57- Created HTML administration console
58- When server is started, browser is started automatically
59- Fixed directory listing bug when directory contains `#` character
60- Removed built-in Lua Server Pages in the binary, and instead
61  added Mongoose + Lua developer bundle which has Lua Server Pages support.
62  That also solves external Lua modules loading problem.
63
64
65## Release 5.3, March 10 2014
66
67Changes in Libmongoose library:
68
69   * Moved to the evented API. Updated API documentation is at
70       http://cesanta.com/docs/Embed.shtml
71       http://cesanta.com/docs/API.shtml
72   *  Added `MG_LUA` event for exporting custom variables to the Lua environment
73   *  Added virtual hosts capability, see `url_rewrites` option description at
74       http://cesanta.com/docs/Options.shtml
75   *  Added mjpg serving example
76   *  Cleaned up and documented HTTP client API, with unit tests
77   *  Added `mg_wakeup_server()` to awaken `mg_poll_server()`
78      from another thread
79   *  Moved Mongoose IO core to [https://github.com/cesanta/net_skeleton](Net Skeleton)
80   *  Added connection hexdump functionality for developers
81   *  Bug fixes
82
83Changes in pre-compiled binaries:
84
85   *  New awesome Mongoose logos by our designer Katrin - thanks Katrin!
86        Check them out at http://cesanta.com/products.shtml
87   *  Added Lua Server Pages support to the free version, quick intro is at
88        http://cesanta.com/docs/Lua.shtml
89   *  Added quick "Set shared directory" menu item to set `document_root`
90   *  Added SSI support to the Pro version
91   *  Removed SSL support from the Pro version
92
93## Release 5.2, Feb 1 2014
94
95   *  Windows binary made fully UNICODE aware. In previous versions,
96      the presence of non-ASCII chars in document root, CGI script name,
97      or directory name might have broken Mongoose as stand-alone
98      or as Windows service. Now Mongoose works with non-ASCII paths properly.
99      Internally, Mongoose uses UTF8 encoding. When making WinAPI calls,
100      mongoose converts UTF8 strings to wide chars and calls UNICODE API.
101   *  Enhanced authorization API by providing `mg_set_auth_handler()` and
102      `mg_authorize_digest()`
103   *  Removed `mg_add_uri_handler()`, added `mg_set_request_handler()`.
104      There is only oneURI handler that handles all requests, just like in 4.x.
105      The reason for this change is to provide an ability to catch all URIs,
106      and at the same time signal Mongoose to continue handling specific URIs.
107  *  Added `mg_parse_multipart()` API for file uploads.
108      Note that the restriction on uploading huge files still exists,
109      and will be eliminated in the next release.
110  *  Allowing mongoose to bind to port 0, in which case it'll bind to any
111      random unused port.
112  *  Moved `idle_timeout_ms` run-time option to compile-time flag
113  *  Added asynchronous HTTP client, not documented yet. Documentation and
114      examples are coming in the next couple of weeks. Async Websocket client
115      is scheduled for the next release. See usage examples at `unit_test.c`
116  *  Windows and MacOS pre-built binaries are now split to free and paid ones,
117    paid binaries include CGI, SSL, Lua, Sqlite, support and updates.
118    Linux pre-built binary includes all functionality and is free, and will
119    continue to be free.  Source code for Windows and MacOS GUI is closed.
120    Disclaimer: source code for the command line stand-alone server,
121    as well as Mongoose library itself, will never be closed.
122  *  Multiple bug fixes and minor enhancements
123
124## Release 5.1, Jan 10 2014
125
126   *  CGI-related bugs where fixed, primarily for Windows platform
127   *  Bugs on Windows related to UNICODE support were fixed
128   *  Added a feature to support "error pages" through redirect.
129      Done using `-url_redirects` option, details are on
130      http://cesanta.com/docs/Options.shtml
131
132## Release 5.0, Jan 6 2014
133
134   *  Internal core has been changed from blocking, thread-per-connection to
135      non-blocking, asynchronous, one thread for all.
136   *  API modification for server creation and response creation. That allowed
137     keep-alive support for dynamic requests, boosting the embedded performance
138     to 100+ thousands requests per second on a single core
139     (as measured on my development MacBook laptop)
140   * Unified handling of POST requests and Websocket requests by putting a
141    payload into `conn->content`, `conn->content_len` attributes.
142    That simplified user code and eliminated the need of `mg_read()`,
143    since mongoose buffers all data prior to calling the callback
144   * keep-alive support is the default
145   * Dropped SSI support and throttling support
146   *  Several configuraition parameters are gone:
147    *  `cgi_environment` (replaced with MONGOOSE_CGI),
148    *  `protect_uri` (not useful)
149    *  `ssi_pattern` (SSI support is gone)
150    *  `throttle` (throttling support is gone)
151    *  `error_log_file` (not used)
152    *   `enable_keep_alive` (enabled by default)
153    *   `listening_ports` (renamed to listening_port)
154    *   `num_threads` (core has changed to single thread)
155    *   `put_delete_auth_file` (renamed to dav_auth_file)
156    *   `authentication_domain` (renamed to auth_domain)
157   * Due to the async, non-blocking nature of the core, few restrictions
158      are now in place:
159    * user callbacks must not block
160    * POST and Websocket data are now buffered, and cannot be huge
161   * mongoose is now capable on listening on only one port
162
163## Release 4.1, Oct 2013
164## Release 4.0, Oct 2013
165## Release 3.8, Sep 2013
166
167## Release 3.7, Feb 2 2013
168
169  *  Added "redirect to SSL port" functionality, e.g. if you specify
170      `-listening_ports 8080r,8043s`
171      then all requests to HTTP port 8080 will be redirected to HTTPS port 8043
172  *  Added `mg_download()` API, an HTTP client interface!
173  *  Lua server pages now must output HTTP headers -- full control for Lua
174  *  Added pre-built binary for MacOS, with initial GUI support
175  *  API change: got rid of events, moved to struct `mg_callbacks`
176  *  Bugfixes, thanks to contributors
177
178
179## Release 3.7, Jan 18 2013
180  *  Fixed source code archive (main.c was missing)
181  *  Extended Windows GUI functionality:
182   * Added "Start browser" systray popup menu item
183   *  Enhanced configuration editor
184  *  Renamed config options:
185   * `put_delete_passwords_file` -> `put_delete_auth_file`
186   * `global_passwords_file` -> `global_auth_file`
187  *  `select()` changed to `poll()`, to avoid big file descriptor
188  `FD_SET` problem on UNIX
189  *  Couple of bugfixes, thanks to contributors
190
191
192Eearlier release notes could be found by searching
193[Mongoose mailing list](https://groups.google.com/forum/#!forum/mongoose-users)
trunk/3rdparty/mongoose/docs/SSL.md
r0r242829
1# Mongoose SSL guide
2
3SSL is a protocol that makes web communication secure. To enable SSL
4in mongoose, 3 steps are required:
5
6   1. Valid certificate file must be created
7   2. `ssl_certificate` options must be set to contain path to the
8       certificate file.
9   3. `listening_ports` option must contain a port number with letter `s`
10        appended to it, which instructs Mongoose to use SSL for all connections
11        made to that port.
12
13Below is the `mongoose.conf` file snippet for typical SSL setup:
14
15    document_root     www_root        # Serve files in www_root directory
16    listening_ports   80r,443s        # Redirect all HTTP requests to HTTPS
17    ssl_certificate   ssl_cert.pem    # Location of certificate file
18
19## How to create SSL certificate file
20
21SSL certificate file is a text file that must contain at least two
22sections:
23
24   1. A private key
25   2. A certificate
26
27Both sections should be chunks of text in PEM format. When PEM file is
28opened in a text editor, it looks like this:
29
30    -----BEGIN RSA PRIVATE KEY-----
31    MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH
32    hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg=
33    -----END RSA PRIVATE KEY-----
34    -----BEGIN CERTIFICATE-----
35    MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB
36    SEGI4JSxV56lYg==
37    -----END CERTIFICATE-----
38
39Two aforementioned sections are clearly seen. Typically, those section
40are bigger then in the example shown. The text between the `BEGIN` and
41`END` is the text representation of binary data, a private key and a
42certificate. Therefore, in order to create a certificate file,
43
44   * private key must be converted to PEM format
45   * certificate must be converted to PEM format
46   * those two should be concatenated into a single file
47
48If the certificate chain in used, a chain file also needs to be
49converted into PEM format and appended to the certificate file.
50
51## How SSL works
52
53SSL is a protocol that can encrypt communication between two parties. If third
54party observes all messages passed by, it would be very
55hard for the third party (though not impossible) to decrypt the communication.
56
57The idea is based on so-called public key encryption. Communicating parties
58have two keys: a public key and a private key. A public key is advertised
59to everybody, and it is contained in a certificate. A private key is kept
60secret. Security algorithm works in a way that anybody can encrypt
61a message using public key, and only private key can decrypt it.
62
63This is why web server needs both private key and certificate: private key
64is used to decrypt incoming messages, and certificate is used to tell the
65public key to the other party. When communication starts, parties exchange
66their public keys, and keep private keys to themselves. Man-in-the-middle
67who observes the communication is unable to decrypt the messages cause
68private keys are required for decryption.
69
70Encryption algorithms are built on top of hard mathematical problem, which
71makes it very expensive for man-in-the-middle to compute private keys.
72For example, RSA algorithm is based on a mathematical problem of factorization.
73It is easy to generate two very large prime numbers `P` and `Q` and make
74a product `P * Q`. But given a product, it is very hard to recover these
75two prime numbers - this is called factorization.
trunk/3rdparty/mongoose/docs/Usage.md
r0r242829
1# Mongoose User Guide
2
3Mongoose is small and easy to use web server built on top of
4mongoose library. It is designed with maximum simplicity in mind. For example,
5to share any directory, just drop mongoose executable in that directory,
6double-click it (on UNIX, run it from shell) and launch a browser at
7[http://localhost:8080](http://localhost:8080) Note that 'localhost' should
8be changed to a machine's name if a folder is accessed from other computer.
9
10On Windows and Mac, Mongoose iconifies itself to the system tray when started.
11Right-click on the icon to pop up a menu, where it is possible to stop
12mongoose, or configure it.
13
14On UNIX, `mongoose` is a command line utility. Running `mongoose` in
15terminal, optionally followed by configuration parameters
16(`mongoose [OPTIONS]`) or configuration file name
17(`mongoose [config_file_name]`) starts the
18web server:
19
20    $ mongoose -document_root /var/www  # Running mongoose with cmdline options
21    $ mongoose /etc/my_config.txt       # Running mongoose with config file
22    $ mongoose                          # Running with no parameters. This will
23                                        # serve current directory on port 8080
24
25Mongoose does not detach from terminal. Pressing `Ctrl-C` keys
26stops the server.
27
28When started, mongoose first searches for the configuration file.
29If configuration file is specified explicitly in the command line, then
30specified configuration file is used.
31Otherwise, mongoose would search for file `mongoose.conf` in the same directory
32where binary is located, and use it. Configuration file can be absent.
33
34Configuration file is a sequence of lines, each line containing
35command line argument name and it's value. Empty lines and lines beginning
36with `#` are ignored. Here is the example of `mongoose.conf` file:
37
38    # This is a comment
39    document_root C:\www
40    listening_port 80
41    ssl_certificate C:\mongoose\ssl_cert.pem
42
43Command line arguments are highest priority and can override
44configuration file settings. For example, if `mongoose.conf` has line
45`document_root /var/www`, and mongoose has been started as
46`mongoose -document_root /etc`, then `/etc` directory will be used as
47document root.
48
49Note that configuration options on the command line must start with `-`,
50and their names are the same as in the config file. Exampli gratia,
51the following two setups are equivalent:
52
53    $ mongoose -listening_port 1234 -document_root /var/www
54
55    $ cat > mongoose.conf
56    listening_ports 1234
57    document_root /var/www
58    ^D
59    $ mongoose
60
61Mongoose can also be used to modify `.htpasswd` passwords file:
62
63    $ mongoose -A .htpasswd mydomain.com user_name user_password
64
65Unlike other web servers, mongoose does not require CGI scripts be located in
66a special directory. CGI scripts can be anywhere. CGI (and SSI) files are
67recognized by the file name pattern. Mongoose uses shell-like glob
68patterns. Pattern match starts at the beginning of the string, so essentially
69patterns are prefix patterns. Syntax is as follows:
70
71     **         Matches everything
72     *          Matches everything but slash character, '/'
73     ?          Matches any character
74     $          Matches the end of the string
75     |          Matches if pattern on the left side or the right side matches.
76
77All other characters in the pattern match themselves. Examples:
78
79    # Pattern   Meaning
80    **.cgi$     Any string that ends with .cgi
81    /foo        Any string that begins with /foo
82    **a$|**b$   Any string that ends with a or b
83
84To restrict CGI files only to `/cgi-bin/` directory, use this setting:
85
86    $ mongoose -cgi_pattern /cgi-bin/*.cgi # Emulate /cgi-bin/ restriction
trunk/3rdparty/mongoose/examples/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4SUBDIRS = $(sort $(filter-out csharp/, $(dir $(wildcard */))))
5X = $(SUBDIRS)
6
7.PHONY: $(SUBDIRS)
8
9all: $(SUBDIRS)
10
11$(SUBDIRS):
12   @$(MAKE) -C $@
13
14clean:
15   for d in $(SUBDIRS) ; do $(MAKE) -C $$d clean ; done
No newline at end of file
trunk/3rdparty/mongoose/examples/big_upload/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = big_upload
5CFLAGS = -W -Wall -pthread -I../.. -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8$(PROG): $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/big_upload/big_upload.c
r0r242829
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include "mongoose.h"
5
6static int handle_request(struct mg_connection *conn) {
7  if (strcmp(conn->uri, "/upload") == 0) {
8    FILE *fp = (FILE *) conn->connection_param;
9    if (fp != NULL) {
10      fwrite(conn->content, 1, conn->content_len, fp); // Write last bits
11      mg_printf(conn, "HTTP/1.1 200 OK\r\n"
12                "Content-Type: text/plain\r\n"
13                "Connection: close\r\n\r\n"
14                "Written %ld of POST data to a temp file:\n\n",
15                (long) ftell(fp));
16
17      // Temp file will be destroyed after fclose(), do something with the
18      // data here -- for example, parse it and extract uploaded files.
19      // As an example, we just echo the whole POST buffer back to the client.
20      rewind(fp);
21      mg_send_file_data(conn, fileno(fp));
22      return MG_MORE;  // Tell Mongoose reply is not completed yet
23    } else {
24      mg_printf_data(conn, "%s", "Had no data to write...");
25      return MG_TRUE; // Tell Mongoose we're done with this request
26    }
27  } else {
28    mg_printf_data(conn, "%s",
29                   "<html><body>Upload example."
30                   "<form method=\"POST\" action=\"/upload\" "
31                   "  enctype=\"multipart/form-data\">"
32                   "<input type=\"file\" name=\"file\" /> <br/>"
33                   "<input type=\"submit\" value=\"Upload\" />"
34                   "</form></body></html>");
35    return MG_TRUE;   // Tell mongoose to close this connection
36  }
37}
38
39// Mongoose sends MG_RECV for every received POST chunk.
40// When last POST chunk is received, Mongoose sends MG_REQUEST, then MG_CLOSE.
41static int handle_recv(struct mg_connection *conn) {
42  FILE *fp = (FILE *) conn->connection_param;
43
44  // Open temporary file where we going to write data
45  if (fp == NULL && ((conn->connection_param = fp = tmpfile())) == NULL) {
46    return -1;  // Close connection on error
47  }
48
49  // Return number of bytes written to a temporary file: that is how many
50  // bytes we want to discard from the receive buffer
51  return fwrite(conn->content, 1, conn->content_len, fp);
52}
53
54// Make sure we free all allocated resources
55static int handle_close(struct mg_connection *conn) {
56  if (conn->connection_param != NULL) {
57    fclose((FILE *) conn->connection_param);
58    conn->connection_param = NULL;
59  }
60  return MG_TRUE;
61}
62
63static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
64  switch (ev) {
65    case MG_AUTH:     return MG_TRUE;
66    case MG_REQUEST:  return handle_request(conn);
67    case MG_RECV:     return handle_recv(conn);
68    case MG_CLOSE:    return handle_close(conn);
69    default:          return MG_FALSE;
70  }
71}
72
73int main(void) {
74  struct mg_server *server = mg_create_server(NULL, ev_handler);
75  mg_set_option(server, "listening_port", "8080");
76  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
77
78  for (;;) {
79    mg_poll_server(server, 1000);
80  }
81
82  mg_destroy_server(&server);
83  return 0;
84}
trunk/3rdparty/mongoose/examples/cookie_authentication/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = cookie_auth
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8$(PROG): $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/cookie_authentication/cookie_auth.c
r0r242829
1// Copyright (c) 2014 Cesanta Software
2// All rights reserved
3
4#include <stdio.h>
5#include <string.h>
6#include <time.h>
7#include "mongoose.h"
8
9static const char *s_login_uri = "/login.html";
10static const char *s_secret = ":-)";  // Must be known only to server
11
12static void generate_ssid(const char *user_name, const char *expiration_date,
13                          char *ssid, size_t ssid_size) {
14  char hash[33];
15  mg_md5(hash, user_name, ":", expiration_date, ":", s_secret, NULL);
16  snprintf(ssid, ssid_size, "%s|%s|%s", user_name, expiration_date, hash);
17}
18
19static int check_auth(struct mg_connection *conn) {
20  char ssid[100], calculated_ssid[100], name[100], expire[100];
21
22  // Always authenticate requests to login page
23  if (strcmp(conn->uri, s_login_uri) == 0) {
24    return MG_TRUE;
25  }
26
27  // Look for session ID in the Cookie.
28  // That session ID can be validated against the database that stores
29  // current active sessions.
30  mg_parse_header(mg_get_header(conn, "Cookie"), "ssid", ssid, sizeof(ssid));
31  if (sscanf(ssid, "%[^|]|%[^|]|", name, expire) == 2) {
32    generate_ssid(name, expire, calculated_ssid, sizeof(calculated_ssid));
33    if (strcmp(ssid, calculated_ssid) == 0) {
34      return MG_TRUE;  // Authenticate
35    }
36  }
37
38  // Auth failed, do NOT authenticate, redirect to login page
39  mg_printf(conn, "HTTP/1.1 302 Moved\r\nLocation: %s\r\n\r\n", s_login_uri);
40  return MG_FALSE;
41}
42
43static int check_login_form_submission(struct mg_connection *conn) {
44  char name[100], password[100], ssid[100], expire[100], expire_epoch[100];
45
46  mg_get_var(conn, "name", name, sizeof(name));
47  mg_get_var(conn, "password", password, sizeof(password));
48
49  // A real authentication mechanism should be employed here.
50  // Also, the whole site should be served through HTTPS.
51  if (strcmp(name, "Joe") == 0 && strcmp(password, "Doe") == 0) {
52    // Generate expiry date
53    time_t t = time(NULL) + 3600;  // Valid for 1 hour
54    snprintf(expire_epoch, sizeof(expire_epoch), "%lu", (unsigned long) t);
55    strftime(expire, sizeof(expire), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
56    generate_ssid(name, expire_epoch, ssid, sizeof(ssid));
57    // Set "session id" cookie, there could be some data encoded in it.
58    mg_printf(conn,
59              "HTTP/1.1 302 Moved\r\n"
60              "Set-Cookie: ssid=%s; expire=\"%s\"; http-only; HttpOnly;\r\n"
61              "Location: /\r\n\r\n",
62              ssid, expire);
63    return MG_TRUE;
64  }
65  return MG_FALSE;
66}
67
68static int serve_request(struct mg_connection *conn) {
69  if (strcmp(conn->uri, s_login_uri) == 0 &&
70      strcmp(conn->request_method, "POST") == 0) {
71    return check_login_form_submission(conn);
72  }
73  return MG_FALSE;  // Serve files in the document_root
74}
75
76static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
77  switch (ev) {
78    case MG_AUTH: return check_auth(conn);
79    case MG_REQUEST: return serve_request(conn);
80    default: return MG_FALSE;
81  }
82}
83
84int main(void) {
85  struct mg_server *server = mg_create_server(NULL, ev_handler);
86  mg_set_option(server, "listening_port", "8080");
87  mg_set_option(server, "document_root", ".");
88
89  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
90  for (;;) {
91    mg_poll_server(server, 1000);
92  }
93  mg_destroy_server(&server);
94
95  return 0;
96}
trunk/3rdparty/mongoose/examples/cookie_authentication/index.html
r0r242829
1<!DOCTYPE html>
2<html lang="en">
3<head>
4  <meta charset="utf-8" />
5  <title>WebSocket Test</title>
6  <meta name="viewport" content="width=device-width, initial-scale=1" />
7  <style type="text/css">
8    body {
9      background-color: #cde; margin: 0;
10      padding: 0; font: 14px Helvetica, Arial, sans-serif;
11    }
12    * { outline: none; }
13    div.content {
14      width: 800px; margin: 2em auto; padding: 20px 50px;
15      background-color: #fff; border-radius: 1em;
16    }
17    label { display: inline-block; min-width: 7em; }
18    input { border: 1px solid #ccc; padding: 0.4em; margin: 0 0 10px 0; }
19    a:link, a:visited { color: #69c; text-decoration: none; }
20    @media (max-width: 700px) {
21      body { background-color: #fff; }
22      div.content {
23        width: auto; margin: 0 auto; border-radius: 0; padding: 1em;
24      }
25    }
26</style>
27
28<body>
29  <div class="content">
30    <h1>Mongoose Cookie Base Authentication</h1>
31    <p>This is an index page. Authentication succeeded.</p>
32</body>
33</html>
No newline at end of file
trunk/3rdparty/mongoose/examples/cookie_authentication/login.html
r0r242829
1<!DOCTYPE html>
2<html lang="en">
3<head>
4  <meta charset="utf-8" />
5  <title>WebSocket Test</title>
6  <meta name="viewport" content="width=device-width, initial-scale=1" />
7  <style type="text/css">
8    body {
9      background-color: #cde; margin: 0;
10      padding: 0; font: 14px Helvetica, Arial, sans-serif;
11    }
12    * { outline: none; }
13    div.content {
14      width: 800px; margin: 2em auto; padding: 20px 50px;
15      background-color: #fff; border-radius: 1em;
16    }
17    label { display: inline-block; min-width: 7em; }
18    input { border: 1px solid #ccc; padding: 0.4em; margin: 0 0 10px 0; }
19    a:link, a:visited { color: #69c; text-decoration: none; }
20    @media (max-width: 700px) {
21      body { background-color: #fff; }
22      div.content {
23        width: auto; margin: 0 auto; border-radius: 0; padding: 1em;
24      }
25    }
26</style>
27
28<body>
29  <div class="content">
30    <h1>Mongoose Cookie Based Authentication</h1>
31    <p>Use name "Joe", password "Doe" to login.</p>
32    <form method="POST">
33      <div>
34        <label>Name:</label>
35        <input type="text" name="name"/>
36      </div><div>
37        <label>Password:</label>
38        <input type="password" name="password"/>
39      </div><div>
40        <input type="submit" value="Login"/>
41      </div>
42    </form>
43</body>
44</html>
No newline at end of file
trunk/3rdparty/mongoose/examples/csharp/example.cs
r0r242829
1// This file is part of mongoose web server project,
2// https://github.com/cesanta/mongoose
3
4using System;
5
6public class Program {
7  static private int EventHandler(IntPtr conn_ptr, int ev) {
8      MongooseConnection conn = (MongooseConnection)
9         System.Runtime.InteropServices.Marshal.PtrToStructure(
10            conn_ptr , typeof(MongooseConnection));
11
12      if (ev == 102) {
13         // MG_AUTH
14         return 1;
15      } else if (ev == 103) {
16         // MG_REQUEST
17       Mongoose.send_data(conn_ptr, "Hello from C#!\n");
18         Mongoose.send_data(conn_ptr, "URI: " + conn.uri + "\n");
19         Mongoose.send_data(conn_ptr, "HTTP Headers:\n");
20         
21         for (int i = 0; i < conn.num_headers; i++) {
22            IntPtr name = conn.http_headers[i].name;
23            IntPtr val = conn.http_headers[i].value;
24            System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name);
25            Mongoose.send_data(conn_ptr, "  " +
26               System.Runtime.InteropServices.Marshal.PtrToStringAnsi(name) + ": " +
27                  System.Runtime.InteropServices.Marshal.PtrToStringAnsi(val) + "\n");
28         }         
29      return 1;
30      }
31      return 0;
32  }
33
34  static void Main() {
35    Mongoose web_server = new Mongoose(".", "9001",
36        new MongooseEventHandler(EventHandler));
37
38    Console.WriteLine("Mongoose started, press Ctrl-C to exit.");
39      for (;;) {
40         web_server.poll(1000);
41      }
42  }
43}
trunk/3rdparty/mongoose/examples/csharp/mongoose.cs
r0r242829
1// This file is part of mongoose web server project,
2// https://github.com/cesanta/mongoose
3
4using System;
5using System.Runtime.InteropServices;
6
7[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader {
8  [MarshalAs(UnmanagedType.LPTStr)] public IntPtr name;
9  [MarshalAs(UnmanagedType.LPTStr)] public IntPtr value;
10};
11
12// mongoose.h :: struct mg_connection
13[StructLayout(LayoutKind.Sequential)] public struct MongooseConnection {
14  [MarshalAs(UnmanagedType.LPTStr)] public string request_method;
15  [MarshalAs(UnmanagedType.LPTStr)] public string uri;
16  [MarshalAs(UnmanagedType.LPTStr)] public string http_version;
17  [MarshalAs(UnmanagedType.LPTStr)] public string query_string;
18
19   [MarshalAs(UnmanagedType.ByValArray,SizeConst=48)] public char[] remote_ip;
20   [MarshalAs(UnmanagedType.LPTStr)] public string local_ip;
21   [MarshalAs(UnmanagedType.U2)] public short remote_port;
22   [MarshalAs(UnmanagedType.U2)] public short local_port;
23   
24   [MarshalAs(UnmanagedType.SysInt)] public int num_headers;
25  [MarshalAs(UnmanagedType.ByValArray,SizeConst=30)]
26    public MongooseHeader[] http_headers;
27   
28   [MarshalAs(UnmanagedType.LPTStr)] public IntPtr content;
29   [MarshalAs(UnmanagedType.SysInt)] public int content_len;
30   
31   [MarshalAs(UnmanagedType.SysInt)] public int is_websocket;
32   [MarshalAs(UnmanagedType.SysInt)] public int status_code;
33   [MarshalAs(UnmanagedType.SysInt)] public int wsbits;
34};
35
36public delegate int MongooseEventHandler(IntPtr c, int ev);
37
38public class Mongoose {
39  public const string dll_ = "mongoose";
40  private IntPtr server_;
41
42   [DllImport(dll_)] private static extern IntPtr
43      mg_create_server(IntPtr user_data, MongooseEventHandler eh);
44   [DllImport(dll_)] private static extern int
45      mg_poll_server(IntPtr server, int milli);
46  [DllImport(dll_)] private static extern IntPtr
47      mg_set_option(IntPtr server, string name, string value);
48   [DllImport(dll_)] public static extern int
49      mg_send_data(IntPtr conn, string data, int length);   
50
51  public Mongoose(string document_root,
52                  string listening_port,
53                  MongooseEventHandler event_handler) {
54    server_ = mg_create_server(IntPtr.Zero, event_handler);
55      mg_set_option(server_, "document_root", document_root);
56      mg_set_option(server_, "listening_port", listening_port);
57  }
58   
59  public static int send_data(IntPtr conn, string data) {
60    return mg_send_data(conn, data, data.Length);
61  }
62   
63   public void poll(int milli) {
64    mg_poll_server(server_, milli);
65  }
66   
67   // TODO: add destructor and call mg_destroy_server()
68}
trunk/3rdparty/mongoose/examples/digest_authentication/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = digest_auth
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8$(PROG): $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/digest_authentication/digest_auth.c
r0r242829
1#include <stdio.h>
2#include <string.h>
3#include "mongoose.h"
4
5static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
6
7  if (ev == MG_AUTH) {
8    int result = MG_FALSE; // Not authorized
9    FILE *fp;
10
11    // To populate passwords file, do
12    // mongoose -A my_passwords.txt mydomain.com admin admin
13    if ((fp = fopen("my_passwords.txt", "r")) != NULL) {
14      result = mg_authorize_digest(conn, fp);
15      fclose(fp);
16    }
17
18    return result;
19  }
20
21  return MG_FALSE;
22}
23
24int main(void) {
25  struct mg_server *server = mg_create_server(NULL, ev_handler);
26  mg_set_option(server, "listening_port", "8080");
27  mg_set_option(server, "document_root", ".");
28
29  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
30  for (;;) {
31    mg_poll_server(server, 1000);
32  }
33  mg_destroy_server(&server);
34
35  return 0;
36}
trunk/3rdparty/mongoose/examples/file_upload/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = file_upload
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8all: $(PROG)
9
10run: $(PROG)
11   ./$(PROG)
12
13$(PROG): $(SOURCES) Makefile
14   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
15
16win:
17   wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe
18
19clean:
20   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc*
trunk/3rdparty/mongoose/examples/file_upload/file_upload.c
r0r242829
1// Copyright (c) 2004-2012 Sergey Lyubka
2// This file is a part of mongoose project, http://github.com/valenok/mongoose
3
4#include <stdio.h>
5#include <string.h>
6#include "mongoose.h"
7
8static int send_index_page(struct mg_connection *conn) {
9  const char *data;
10  int data_len, ofs = 0;
11  char var_name[100], file_name[100];
12
13  mg_printf_data(conn, "%s",
14                 "<html><body>Upload example."
15                 "<form method=\"POST\" action=\"/handle_post_request\" "
16                 "  enctype=\"multipart/form-data\">"
17                 "<input type=\"file\" name=\"file\" /> <br/>"
18                 "<input type=\"submit\" value=\"Upload\" />"
19                 "</form>");
20
21  while ((ofs = mg_parse_multipart(conn->content + ofs, conn->content_len - ofs,
22                                   var_name, sizeof(var_name),
23                                   file_name, sizeof(file_name),
24                                   &data, &data_len)) > 0) {
25    mg_printf_data(conn, "var: %s, file_name: %s, size: %d bytes<br>",
26                   var_name, file_name, data_len);
27  }
28
29  mg_printf_data(conn, "%s", "</body></html>");
30
31  return MG_TRUE;
32}
33
34static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
35  switch (ev) {
36    case MG_AUTH:     return MG_TRUE;
37    case MG_REQUEST:  return send_index_page(conn);
38    default:          return MG_FALSE;
39  }
40}
41
42int main(void) {
43  struct mg_server *server;
44
45  // Create and configure the server
46  server = mg_create_server(NULL, ev_handler);
47  mg_set_option(server, "listening_port", "8080");
48
49  // Serve request. Hit Ctrl-C to terminate the program
50  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
51  for (;;) {
52    mg_poll_server(server, 1000);
53  }
54
55  // Cleanup, and free server instance
56  mg_destroy_server(&server);
57
58  return 0;
59}
trunk/3rdparty/mongoose/examples/form_submit/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = form_submit
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8$(PROG): $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/form_submit/form_submit.c
r0r242829
1#include <stdio.h>
2#include <string.h>
3#include "mongoose.h"
4
5static const char *html_form =
6  "<html><body>POST example."
7  "<form method=\"POST\" action=\"/handle_post_request\">"
8  "Input 1: <input type=\"text\" name=\"input_1\" /> <br/>"
9  "Input 2: <input type=\"text\" name=\"input_2\" /> <br/>"
10  "<input type=\"submit\" />"
11  "</form></body></html>";
12
13static void send_reply(struct mg_connection *conn) {
14  char var1[500], var2[500];
15
16  if (strcmp(conn->uri, "/handle_post_request") == 0) {
17    // User has submitted a form, show submitted data and a variable value
18    // Parse form data. var1 and var2 are guaranteed to be NUL-terminated
19    mg_get_var(conn, "input_1", var1, sizeof(var1));
20    mg_get_var(conn, "input_2", var2, sizeof(var2));
21
22    // Send reply to the client, showing submitted form values.
23    // POST data is in conn->content, data length is in conn->content_len
24    mg_send_header(conn, "Content-Type", "text/plain");
25    mg_printf_data(conn,
26                   "Submitted data: [%.*s]\n"
27                   "Submitted data length: %d bytes\n"
28                   "input_1: [%s]\n"
29                   "input_2: [%s]\n",
30                   conn->content_len, conn->content,
31                   conn->content_len, var1, var2);
32  } else {
33    // Show HTML form.
34    mg_send_data(conn, html_form, strlen(html_form));
35  }
36}
37
38static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
39  if (ev == MG_REQUEST) {
40    send_reply(conn);
41    return MG_TRUE;
42  } else if (ev == MG_AUTH) {
43    return MG_TRUE;
44  } else {
45    return MG_FALSE;
46  }
47}
48
49int main(void) {
50  struct mg_server *server = mg_create_server(NULL, ev_handler);
51
52  mg_set_option(server, "listening_port", "8080");
53
54  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
55  for (;;) {
56    mg_poll_server(server, 1000);
57  }
58
59  mg_destroy_server(&server);
60
61  return 0;
62}
trunk/3rdparty/mongoose/examples/hello_world/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = hello_world
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8all: $(PROG)
9
10run: $(PROG)
11   ./$(PROG)
12
13$(PROG): $(SOURCES) Makefile
14   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
15
16win:
17   wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe
18   wine $(PROG).exe
19
20clean:
21   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc*
trunk/3rdparty/mongoose/examples/hello_world/hello_world.c
r0r242829
1// Copyright (c) 2014 Cesanta Software
2// All rights reserved
3//
4// This example demostrates basic use of Mongoose embedded web server.
5// $Date: 2014-09-09 22:20:23 UTC $
6
7#include <stdio.h>
8#include <string.h>
9#include "mongoose.h"
10
11static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
12  switch (ev) {
13    case MG_AUTH: return MG_TRUE;
14    case MG_REQUEST:
15      mg_printf_data(conn, "Hello! Requested URI is [%s]", conn->uri);
16      return MG_TRUE;
17    default: return MG_FALSE;
18  }
19}
20
21int main(void) {
22  struct mg_server *server;
23
24  // Create and configure the server
25  server = mg_create_server(NULL, ev_handler);
26  mg_set_option(server, "listening_port", "8080");
27
28  // Serve request. Hit Ctrl-C to terminate the program
29  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
30  for (;;) {
31    mg_poll_server(server, 1000);
32  }
33
34  // Cleanup, and free server instance
35  mg_destroy_server(&server);
36
37  return 0;
38}
trunk/3rdparty/mongoose/examples/http_client/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = http_client
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8unix: $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/http_client/http_client.c
r0r242829
1// Copyright (c) 2014 Cesanta Software
2// All rights reserved
3//
4// This example demostrates how to connect to the remote Web server,
5// download data, process it and send back a reply.
6
7#include <signal.h>
8#include <stdlib.h>
9
10#include "mongoose.h"
11
12static int s_received_signal = 0;
13static struct mg_server *s_server = NULL;
14static const char *s_remote_addr = "glosbe.com:80";
15
16static void signal_handler(int sig_num) {
17  signal(sig_num, signal_handler);
18  s_received_signal = sig_num;
19}
20
21static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
22  struct mg_connection *client, *orig;
23
24  switch (ev) {
25    case MG_AUTH:
26      return MG_TRUE;
27
28    case MG_CONNECT:
29      // Send request to the remote host.
30      // TODO(lsm): handle connect error here.
31      mg_printf(conn, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
32                "/gapi/translate?from=eng&dest=fra&format=json&phrase=cat",
33                s_remote_addr);
34      return MG_TRUE;
35
36    case MG_REPLY:
37      // Send reply to the original connection
38      orig = (struct mg_connection *) conn->connection_param;
39      mg_send_status(orig, conn->status_code);
40      mg_send_header(orig, "Content-Type", "text/plain");
41      mg_send_data(orig, conn->content, conn->content_len);
42      mg_send_data(orig, "", 0);  // Last chunk: mark the end of reply
43
44      // Disconnect connections
45      orig->connection_param = NULL;
46      conn->connection_param = NULL;
47      return MG_TRUE;
48
49    case MG_REQUEST:
50      if ((client = mg_connect(s_server, s_remote_addr)) != NULL) {
51        // Interconnect requests
52        client->connection_param = conn;
53        conn->connection_param = client;
54        return MG_MORE;
55      } else {
56        mg_printf_data(conn, "%s", "cannot send API request");
57        return MG_TRUE;
58      }
59
60    default:
61      return MG_FALSE;
62  }
63}
64
65int main(void) {
66  s_server = mg_create_server(NULL, ev_handler);
67
68  mg_set_option(s_server, "listening_port", "8080");
69
70  // Setup signal handlers
71  signal(SIGTERM, signal_handler);
72  signal(SIGINT, signal_handler);
73
74  printf("Listening on port %s\n", mg_get_option(s_server, "listening_port"));
75  while (s_received_signal == 0) {
76    mg_poll_server(s_server, 1000);
77  }
78  mg_destroy_server(&s_server);
79  printf("Existing on signal %d\n", s_received_signal);
80
81  return EXIT_SUCCESS;
82}
trunk/3rdparty/mongoose/examples/mjpg_streamer/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = mjpg_streamer
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8$(PROG): $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/mjpg_streamer/mjpg_streamer.c
r0r242829
1#include <sys/stat.h>
2#include <stdio.h>
3#include <string.h>
4#include <stdlib.h>
5#include <time.h>
6#include "mongoose.h"
7
8static void send_file(struct mg_connection *conn, const char *path) {
9  char buf[1024];
10  struct stat st;
11  int n;
12  FILE *fp;
13
14  if (stat(path, &st) == 0 && (fp = fopen(path, "rb")) != NULL) {
15    mg_printf(conn, "--w00t\r\nContent-Type: image/jpeg\r\n"
16              "Content-Length: %lu\r\n\r\n", (unsigned long) st.st_size);
17    while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
18      mg_write(conn, buf, n);
19    }
20    fclose(fp);
21    mg_write(conn, "\r\n", 2);
22  }
23}
24
25struct conn_state {
26  int file_index;
27  time_t last_poll;
28};
29
30static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
31  const char **file_names = (const char **) conn->server_param;
32  struct conn_state *state;
33  time_t now = time(NULL);
34
35  switch (ev) {
36
37    case MG_AUTH:
38      return MG_TRUE;
39
40    case MG_REQUEST:
41      if (strcmp(conn->uri, "/stream") != 0) {
42        mg_send_header(conn, "Content-Type", "text/html");
43        mg_printf_data(conn, "%s",
44                       "Go to <a href=/stream>/stream</a> for MJPG stream");
45        return MG_TRUE;
46      }
47
48      mg_printf(conn, "%s",
49                "HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n"
50                "Pragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\n"
51                "Connection: close\r\nContent-Type: multipart/x-mixed-replace; "
52                "boundary=--w00t\r\n\r\n");
53
54      send_file(conn, file_names[0]);
55
56      state = (struct conn_state *) malloc(sizeof(*state));
57      conn->connection_param = state;
58      state->file_index = 1;  // First file is already sent
59      state->last_poll = time(NULL);
60      return MG_MORE;
61
62    case MG_POLL:
63      state = (struct conn_state *) conn->connection_param;
64
65      if (state != NULL && now > state->last_poll) {
66        if (file_names[state->file_index] != NULL) {
67          send_file(conn, file_names[state->file_index]);
68          state->file_index++;
69          if (file_names[state->file_index] == NULL) {
70            return MG_TRUE;  // No more images, close connection
71          }
72        }
73        state->last_poll = now;
74      }
75      return MG_FALSE;
76
77    case MG_CLOSE:
78      free(conn->connection_param);
79      conn->connection_param = NULL;
80      return MG_FALSE;
81
82    default:
83      return MG_FALSE;
84  }
85}
86
87int main(int argc, char *argv[]) {
88  struct mg_server *server;
89
90  if (argc < 3) {
91    printf("Usage: %s image1.jpg image2.jpg ...\n", argv[0]);
92    return 1;
93  }
94
95  server = mg_create_server(&argv[1], ev_handler);
96  mg_set_option(server, "listening_port", "8080");
97
98  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
99  for (;;) {
100    mg_poll_server(server, 1000);
101  }
102  mg_destroy_server(&server);
103
104  return 0;
105}
trunk/3rdparty/mongoose/examples/multi_threaded_server/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = multi_threaded_server
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8$(PROG): $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/multi_threaded_server/multi_threaded_server.c
r0r242829
1#include "mongoose.h"
2
3// Start a browser and hit refresh couple of times. The replies will
4// come from both server instances.
5static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
6  if (ev == MG_REQUEST) {
7    mg_send_header(conn, "Content-Type", "text/plain");
8    mg_printf_data(conn, "This is a reply from server instance # %s",
9                   (char *) conn->server_param);
10    return MG_TRUE;
11  } else if (ev == MG_AUTH) {
12    return MG_TRUE;
13  } else {
14    return MG_FALSE;
15  }
16}
17
18static void *serve(void *server) {
19  for (;;) mg_poll_server((struct mg_server *) server, 1000);
20  return NULL;
21}
22
23int main(void) {
24  struct mg_server *server1, *server2;
25
26  server1 = mg_create_server((void *) "1", ev_handler);
27  server2 = mg_create_server((void *) "2", ev_handler);
28
29  // Make both server1 and server2 listen on the same sockets
30  mg_set_option(server1, "listening_port", "8080");
31  mg_copy_listeners(server1, server2);
32
33  // server1 goes to separate thread, server 2 runs in main thread.
34  // IMPORTANT: NEVER LET DIFFERENT THREADS HANDLE THE SAME SERVER.
35  mg_start_thread(serve, server1);
36  mg_start_thread(serve, server2);
37  getchar();
38
39  return 0;
40}
trunk/3rdparty/mongoose/examples/proxy_server/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = proxy_server
5FLAGS = -I../..  -DNS_ENABLE_SSL
6CFLAGS = -W -Wall -g -O0 -pthread  -lssl $(FLAGS) $(CFLAGS_EXTRA)
7SOURCES = $(PROG).c ../../mongoose.c
8
9unix: $(SOURCES)
10   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
11
12clean:
13   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/proxy_server/proxy_server.c
r0r242829
1// Copyright (c) 2014 Cesanta Software Limited
2// All rights reserved
3//
4// To build and run this example:
5//    git clone https://github.com/cesanta/net_skeleton.git
6//    git clone https://github.com/cesanta/mongoose.git
7//    cd mongoose/examples
8//    make proxy
9//    ./proxy
10//
11//  Configure your browser to use localhost:2014 as a proxy for all protocols
12//  Then, navigate to https://cesanta.com
13
14#include <sys/stat.h>
15#include <signal.h>
16#include <stdarg.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <time.h>
21
22#ifdef _WIN32
23#define sleep(x) Sleep((x) * 1000)
24#else
25#include <unistd.h>
26#endif
27
28#include "mongoose.h"
29
30static int s_received_signal = 0;
31static struct mg_server *s_server = NULL;
32
33#define SSE_CONNECTION              ((void *) 1)
34
35static void elog(int do_exit, const char *fmt, ...) {
36  va_list ap;
37  va_start(ap, fmt);
38  vfprintf(stderr, fmt, ap);
39  va_end(ap);
40  fputc('\n', stderr);
41  if (do_exit) exit(EXIT_FAILURE);
42}
43
44static void signal_handler(int sig_num) {
45  signal(sig_num, signal_handler);
46  s_received_signal = sig_num;
47}
48
49static int sse_push(struct mg_connection *conn, enum mg_event ev) {
50  if (ev == MG_POLL && conn->connection_param == SSE_CONNECTION) {
51    mg_printf(conn, "data: %s\r\n\r\n", (const char *) conn->callback_param);
52  }
53  return MG_TRUE;
54}
55
56static void *sse_pusher_thread_func(void *param) {
57  while (s_received_signal == 0) {
58    mg_wakeup_server_ex(s_server, sse_push, "%lu %s",
59                        (unsigned long) time(NULL), (const char *) param);
60    sleep(1);
61  }
62  return NULL;
63}
64
65// Return: 1 if regular file, 2 if directory, 0 if not found
66static int exists(const char *path) {
67  struct stat st;
68  return stat(path, &st) != 0 ? 0 : S_ISDIR(st.st_mode)  == 0 ? 1 : 2;
69}
70
71// Return: 1 if regular file, 2 if directory, 0 if not found
72static int is_local_file(const char *uri, char *path, size_t path_len) {
73  snprintf(path, path_len, "%s/%s",
74           mg_get_option(s_server, "document_root"), uri);
75  return exists(path);
76}
77
78static int try_to_serve_locally(struct mg_connection *conn) {
79  char path[500], buf[2000];
80  int n, res;
81  FILE *fp = NULL;
82
83  if ((res = is_local_file(conn->uri, path, sizeof(path))) == 2) {
84    strncat(path, "/index.html", sizeof(path) - strlen(path) - 1);
85    res = exists(path);
86    printf("PATH: [%s]\n", path);
87  }
88  if (res == 0) return MG_FALSE;
89
90  if ((fp = fopen(path, "rb")) != NULL) {
91    printf("Serving [%s] locally \n", path);
92    mg_send_header(conn, "Connection", "close");
93    mg_send_header(conn, "Content-Type", mg_get_mime_type(path, "text/plain"));
94    while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
95      mg_send_data(conn, buf, n);
96    }
97    mg_send_data(conn, "", 0);
98    fclose(fp);
99  }
100  return fp == NULL ? MG_FALSE : MG_TRUE;
101}
102
103static int is_resource_present_locally(const char *uri) {
104  char path[500];
105  return is_local_file(uri, path, sizeof(path)) || strcmp(uri, "/api/sse") == 0;
106}
107
108static int proxy_event_handler(struct mg_connection *conn, enum mg_event ev) {
109  static const char target_url[] = "http://cesanta.com";
110  static int target_url_size = sizeof(target_url) - 1;
111  const char *host;
112
113  switch (ev) {
114    case MG_REQUEST:
115      host = mg_get_header(conn, "Host");
116      printf("[%s] [%s] [%s]\n", conn->request_method, conn->uri,
117             host == NULL ? "" : host);
118      if (strstr(conn->uri, "/qqq") != NULL) s_received_signal = SIGTERM;
119
120      // Proxied HTTPS requests use "CONNECT foo.com:443"
121      // Proxied HTTP requests use "GET http://..... "
122      // Serve requests for target_url from the local FS.
123      if (memcmp(conn->uri, target_url, target_url_size) == 0 &&
124          is_resource_present_locally(conn->uri + target_url_size)) {
125        conn->uri += target_url_size;   // Leave only path in the URI
126      }
127
128      if (strcmp(conn->uri, "/api/sse") == 0) {
129        conn->connection_param = SSE_CONNECTION;
130        mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n"
131                  "Content-Type: text/event-stream\r\n"
132                  "Cache-Control: no-cache\r\n\r\n");
133        return MG_MORE;
134      }
135
136      if (host != NULL && strstr(host, "cesanta") != NULL) {
137        return try_to_serve_locally(conn);
138      }
139
140      // Enable man-in-the-middle SSL mode for oracle.com
141      if (!strcmp(conn->request_method, "CONNECT") &&
142          !strcmp(host, "oracle.com")) {
143        mg_terminate_ssl(conn, "ssl_cert.pem");  // MUST return MG_MORE after
144        return MG_MORE;
145      }
146
147      return MG_FALSE;
148    case MG_AUTH:
149      return MG_TRUE;
150    default:
151      return MG_FALSE;
152  }
153}
154
155static void setopt(struct mg_server *s, const char *opt, const char *val) {
156  const char *err_msg = mg_set_option(s, opt, val);
157  if (err_msg != NULL) {
158    elog(1, "Error setting [%s]: [%s]", opt, err_msg);
159  }
160}
161
162int main(int argc, char *argv[]) {
163  const char *port = "2014", *dump = NULL, *root = "proxy_web_root";
164  int i;
165
166  // Parse command line options
167  for (i = 1; i < argc; i++) {
168    if (strcmp(argv[i], "-port") == 0 && i + 1 < argc) {
169      port = argv[++i];
170    } else if (strcmp(argv[i], "-root") == 0 && i + 1 < argc) {
171      root = argv[++i];
172    } else if (strcmp(argv[i], "-dump") == 0 && i + 1 < argc) {
173      dump = argv[++i];
174    } else {
175      elog(1, "Usage: %s [-cert FILE] [-ca_cert FILE] [-port PORT]", argv[0]);
176    }
177  }
178
179  signal(SIGTERM, signal_handler);
180  signal(SIGINT, signal_handler);
181
182  // Create and configure proxy server
183  s_server = mg_create_server(NULL, &proxy_event_handler);
184  setopt(s_server, "enable_proxy",        "yes");
185  setopt(s_server, "document_root",       root);
186  setopt(s_server, "listening_port",      port);
187  setopt(s_server, "hexdump_file",        dump);
188
189  // Start two SSE pushing threads
190  mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_1");
191  mg_start_thread(sse_pusher_thread_func, (void *) "sse_pusher_thread_2");
192
193  // Start serving in the main thread
194  printf("Starting on port %s\n", mg_get_option(s_server, "listening_port"));
195  while (s_received_signal == 0) {
196    mg_poll_server(s_server, 1000);
197  }
198  printf("Existing on signal %d\n", s_received_signal);
199  mg_destroy_server(&s_server);
200
201  return EXIT_SUCCESS;
202}
trunk/3rdparty/mongoose/examples/proxy_server/proxy_web_root/app1/index.html
r0r242829
1<html>
2<head>
3  <title>App1 Index</title>
4  <style>
5    img { height: 40px; }
6  </style>
7</head>
8<body>
9
10  <h1>App1 index page. Served locally from the the proxy server filesystem</h1>
11
12  <p>image that references non-existent local resource. Forwarded to
13    the 'real' proxy target:</p>
14    <img src="http://cesanta.com/images/logo.png" />
15
16  <p>Google logo via HTTPS (external resource, served by remote host):</p>
17  <img src="https://www.google.ie/images/srpr/logo11w.png" />
18
19  <p>Same image via HTTP:</p>
20  <img src="http://www.google.ie/images/srpr/logo11w.png" />
21
22</body>
23</html>
trunk/3rdparty/mongoose/examples/proxy_server/proxy_web_root/app2/index.html
r0r242829
1<html>
2<head>
3  <title>App2 Index</title>
4  <meta charset="utf-8">
5  <script>
6  window.onload = function() {
7    // Using secure websocket connection, wss://
8    var ws = new WebSocket('wss://echo.websocket.org');
9    var div = document.getElementById('events');
10    ws.onmessage = function(ev) {
11      var el = document.createElement('div');
12      el.innerHTML = 'websocket message: ' + ev.data;
13      div.appendChild(el);
14      // Keep only last 5 messages in the list
15      while (div.childNodes.length > 5) div.removeChild(div.firstChild);
16    };
17
18    // Send random stuff to the websocket connection periodically.
19    // websocket server much echo that stuff back.
20    window.setInterval(function() {
21      var d = new Date();
22      ws.send(d.toString());
23    }, 1000);
24  };
25  </script>
26</head>
27<body>
28  <h1>App2 index page. Served locally from the
29    the proxy's filesystem.</h1>
30  <p>
31    Following div shows proxy forwarding of websocket connection, served by
32    ws://echo.websocket.org:
33  </p>
34
35  <div id="events"></div>
36</body>
37</html>
trunk/3rdparty/mongoose/examples/proxy_server/proxy_web_root/index.html
r0r242829
1<html>
2<head>
3  <title> proxy index </title>
4  <script type="text/javascript">
5  window.onload = function() {
6    var es = new EventSource("/api/sse");
7    var div = document.getElementById('events');
8    es.onmessage = function(ev) {
9      var el = document.createElement('div');
10      el.innerHTML = 'sse message: ' + ev.data;
11      div.appendChild(el);
12      // Keep only last 5 messages in the list
13      while (div.childNodes.length > 5) div.removeChild(div.firstChild);
14    };
15  };
16  </script>
17</head>
18<body>
19  <h1> proxy index page.</h1>
20  <ul>
21    <li><a href="app1">App1</a> - App1 root</li>
22    <li><a href="app2">App2</a> - App2 root</li>
23  </ul>
24
25  <h2>SSE pushes, done by separate threads at random times:</h2>
26  <div id="events"></div>
27
28</body>
29</html>
trunk/3rdparty/mongoose/examples/proxy_server/ssl_cert.pem
r0r242829
1-----BEGIN RSA PRIVATE KEY-----
2MIIEogIBAAKCAQEAwONaLOP7EdegqjRuQKSDXzvHmFMZfBufjhELhNjo5KsL4ieH
3hMSGCcSV6y32hzhqR5lvTViaQez+xhc58NZRu+OUgEhodRBW/vAOjpz/xdMz5HaC
4EhP3E9W1pkitVseS8B5rrgJo1BfCGai1fPav1nutPq2Kj7vMy24+g460Lonf6ln1
5di4aTIRtAqXtUU6RFpPJP35PkCXbTK65O8HJSxxt/XtfoezHCU5+UIwmZGYx46UB
6Wzg3IfK6bGPSiHU3pdiTol0uMPt/GUK+x4NyZJ4/ImsNAicRwMBdja4ywHKXJehH
7gXBthsVIHbL21x+4ibsg9eVM/XioTV6tW3IrdwIDAQABAoIBACFfdLutmkQFBcRN
8HAJNNHmmsyr0vcUOVnXTFyYeDXV67qxrYHQlOHe6LqIpKq1Mon7O2kYMnWvooFAP
9trOnsS6L+qaTYJdYg2TKjgo4ubw1hZXytyB/mdExuaMSkgMgtpia+tB5lD+V+LxN
10x1DesZ+veFMO3Zluyckswt4qM5yVa04YFrt31H0E1rJfIen61lidXIKYmHHWuRxK
11SadjFfbcqJ6P9ZF22BOkleg5Fm5NaxJmyQynOWaAkSZa5w1XySFfRjRfsbDr64G6
12+LSG8YtRuvfxnvUNhynVPHcpE40eiPo6v8Ho6yZKXpV5klCKciodXAORsswSoGJa
13N3nnu/ECgYEA6Yb2rM3QUEPIALdL8f/OzZ1GBSdiQB2WSAxzl9pR/dLF2H+0pitS
14to0830mk92ppVmRVD3JGxYDRZQ56tlFXyGaCzJBMRIcsotAhBoNbjV0i9n5bLJYf
15BmjU9yvWcgsTt0tr3B0FrtYyp2tCvwHqlxvFpFdUCj2oRw2uGpkhmNkCgYEA03M6
16WxFhsix3y6eVCVvShfbLBSOqp8l0qiTEty+dgVQcWN4CO/5eyaZXKxlCG9KMmKxy
17Yx+YgxZrDhfaZ0cxhHGPRKEAxM3IKwT2C8/wCaSiLWXZZpTifnSD99vtOt4wEfrG
18+AghNd5kamFiM9tU0AyvhJc2vdJFuXrfeC7ntM8CgYBGDA+t4cZcbRhu7ow/OKYF
19kulP3nJgHP/Y+LMrl3cEldZ2jEfZmCElVNQvfd2XwTl7injhOzvzPiKRF3jDez7D
20g8w0JAxceddvttJRK9GoY4l7OoeKpjUELSnEQkf+yUfOsTbXPXVY7jMfeNL6jE6b
21qN7t3qv8rmXtejMBE3G6cQKBgGR5W2BMiRSlxqKx1cKlrApV87BUe1HRCyuR3xuA
22d6Item7Lx1oEi7vb242yKdSYnpApWQ06xTh83Y/Ly87JaIEbiM0+h+P8OEIg0F1a
23iB+86AcUX1I8KseVy+Np0HbpfwP8GrFfA5DaRPK7pXMopEtby8cAJ1XZZaI1/ZvZ
24BebHAoGAcQU9WvCkT+nIp9FpXfBybYUsvgkaizMIqp66/l3GYgYAq8p1VLGvN4v5
25ec0dW58SJrCpqsM3NP78DtEzQf9OOsk+FsjBFzDU2RkeUreyt2/nQBj/2mN/+hEy
26hYN0Zii2yTb63jGxKY6gH1R/r9dL8kXaJmcZrfSa3AgywnteJWg=
27-----END RSA PRIVATE KEY-----
28-----BEGIN CERTIFICATE-----
29MIIDBjCCAe4CCQCX05m0b053QzANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJB
30VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
31cyBQdHkgTHRkMB4XDTA4MTIwNzEwMjUyMloXDTE4MTIwNTEwMjUyMlowRTELMAkG
32A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
33IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
34AMDjWizj+xHXoKo0bkCkg187x5hTGXwbn44RC4TY6OSrC+Inh4TEhgnElest9oc4
35akeZb01YmkHs/sYXOfDWUbvjlIBIaHUQVv7wDo6c/8XTM+R2ghIT9xPVtaZIrVbH
36kvAea64CaNQXwhmotXz2r9Z7rT6tio+7zMtuPoOOtC6J3+pZ9XYuGkyEbQKl7VFO
37kRaTyT9+T5Al20yuuTvByUscbf17X6HsxwlOflCMJmRmMeOlAVs4NyHyumxj0oh1
38N6XYk6JdLjD7fxlCvseDcmSePyJrDQInEcDAXY2uMsBylyXoR4FwbYbFSB2y9tcf
39uIm7IPXlTP14qE1erVtyK3cCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAW4yZdqpB
40oIdiuXRosr86Sg9FiMg/cn+2OwQ0QIaA8ZBwKsc+wIIHEgXCS8J6316BGQeUvMD+
41plNe0r4GWzzmlDMdobeQ5arPRB89qd9skE6pAMdLg3FyyfEjz3A0VpskolW5VBMr
42P5R7uJ1FLgH12RyAjZCWYcCRqEMOffqvyMCH6oAjyDmQOA5IssRKX/HsHntSH/HW
43W7slTcP45ty1b44Nq22/ubYk0CJRQgqKOIQ3cLgPomN1jNFQbAbfVTaK1DpEysrQ
445V8a8gNW+3sVZmV6d1Mj3pN2Le62wUKuV2g6BNU7iiwcoY8HI68aRxz2hVMS+t5f
45SEGI4JSxV56lYg==
46-----END CERTIFICATE-----
47-----BEGIN DH PARAMETERS-----
48MEYCQQD+ef8hZ4XbdoyIpJyCTF2UrUEfX6mYDvxuS5O1UNYcslUqlj6JkA11e/yS
496DK8Z86W6mSj5CEk4IjbyEOECXH7AgEC
50-----END DH PARAMETERS-----
trunk/3rdparty/mongoose/examples/restful_api/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = restful_api
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8$(PROG): $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/restful_api/index.html
r0r242829
1<!DOCTYPE html>
2<html lang="en">
3<head>
4  <meta charset="utf-8" />
5  <title>RESTful API demo</title>
6  <meta name="viewport" content="width=device-width, initial-scale=1" />
7  <style type="text/css">
8    * { outline: none; font: 16px/1.4 Helvetica, Arial, sans-serif; }
9    body {
10      background-color: #cde; margin: 0;
11      padding: 0; font: 16px/1.4 Helvetica, Arial, sans-serif;
12    }
13    div.content {
14      width: 800px; margin: 2em auto; padding: 20px 50px;
15      background-color: #fff; border-radius: 1em;
16    }
17    label { display: inline-block; min-width: 7em; }
18    input { border: 1px solid #ccc; padding: 0.2em; }
19    a:link, a:visited { color: #69c; text-decoration: none; }
20    @media (max-width: 700px) {
21      body { background-color: #fff; }
22      div.content { width: auto; margin: 0 auto; padding: 1em; }
23    }
24</style>
25
26<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
27<script language="javascript" type="text/javascript">
28  jQuery(function() {
29
30    $(document).on('keyup', '#n1, #n2', function() {
31      $.ajax({
32        url: '/api/sum',
33        method: 'POST',
34        dataType: 'json',
35        data: { n1: $('#n1').val(), n2: $('#n2').val() },
36        success: function(json) {
37          $('#result').html(json.result);
38        }
39      });
40    });
41
42  });
43</script>
44</head>
45<body>
46  <div class="content">
47    <h1>RESTful API demo.</h1>
48
49    <p>
50      This page demonstrates how Mongoose web server could be used to implement
51      RESTful APIs. Enter numbers below, and press Submit. Browser will send
52      two numbers to <tt>/api/sum</tt> URI, Mongoose calclulates the sum of
53      two and returns the result.
54    </p>
55
56    <div>
57      <label>Number 1:</label> <input type="text" id="n1" />
58    </div><div>
59      <label>Number 2:</label> <input type="text" id="n2" />
60    </div><div>
61     <label>Result:</label> <span id="result">&nbsp;</span>
62    </div><div>
63
64  </div>
65</body>
66</html>
trunk/3rdparty/mongoose/examples/restful_api/restful_api.c
r0r242829
1#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include "mongoose.h"
5
6static const char *s_no_cache_header =
7  "Cache-Control: max-age=0, post-check=0, "
8  "pre-check=0, no-store, no-cache, must-revalidate\r\n";
9
10static void handle_restful_call(struct mg_connection *conn) {
11  char n1[100], n2[100];
12
13  // Get form variables
14  mg_get_var(conn, "n1", n1, sizeof(n1));
15  mg_get_var(conn, "n2", n2, sizeof(n2));
16
17  mg_printf_data(conn, "{ \"result\": %lf }", strtod(n1, NULL) + strtod(n2, NULL));
18}
19
20static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
21  switch (ev) {
22    case MG_AUTH: return MG_TRUE;
23    case MG_REQUEST:
24      if (!strcmp(conn->uri, "/api/sum")) {
25        handle_restful_call(conn);
26        return MG_TRUE;
27      }
28      mg_send_file(conn, "index.html", s_no_cache_header);
29      return MG_MORE;
30    default: return MG_FALSE;
31  }
32}
33
34int main(void) {
35  struct mg_server *server;
36
37  // Create and configure the server
38  server = mg_create_server(NULL, ev_handler);
39  mg_set_option(server, "listening_port", "8000");
40
41  // Serve request. Hit Ctrl-C to terminate the program
42  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
43  for (;;) {
44    mg_poll_server(server, 1000);
45  }
46
47  // Cleanup, and free server instance
48  mg_destroy_server(&server);
49
50  return 0;
51}
trunk/3rdparty/mongoose/examples/send_file/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = send_file
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8all: $(PROG)
9
10run: $(PROG)
11   ./$(PROG)
12
13$(PROG): $(SOURCES) Makefile
14   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
15
16win:
17   wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /I../.. /Fe$(PROG).exe
18   wine $(PROG).exe
19
20clean:
21   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc*
trunk/3rdparty/mongoose/examples/send_file/send_file.c
r0r242829
1// Copyright (c) 2014 Cesanta Software
2// All rights reserved
3//
4// This example demostrates how to send arbitrary files to the client.
5
6#include "mongoose.h"
7
8static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
9  switch (ev) {
10    case MG_REQUEST:
11      mg_send_file(conn, "send_file.c", NULL);  // Also could be a dir, or CGI
12      return MG_MORE; // It is important to return MG_MORE after mg_send_file!
13    case MG_AUTH: return MG_TRUE;
14    default: return MG_FALSE;
15  }
16}
17
18int main(void) {
19  struct mg_server *server = mg_create_server(NULL, ev_handler);
20  mg_set_option(server, "listening_port", "8080");
21
22  printf("Starting on port %s\n", mg_get_option(server, "listening_port"));
23  for (;;) mg_poll_server(server, 1000);
24  mg_destroy_server(&server);
25
26  return 0;
27}
trunk/3rdparty/mongoose/examples/web_server/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = web_server
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8$(PROG): $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/web_server/web_server.c
r0r242829
1// Copyright (c) 2004-2013 Sergey Lyubka
2// Copyright (c) 2013-2014 Cesanta Software Limited
3
4#undef UNICODE                    // Use ANSI WinAPI functions
5#undef _UNICODE                   // Use multibyte encoding on Windows
6#define _MBCS                     // Use multibyte encoding on Windows
7#define _WIN32_WINNT 0x500        // Enable MIIM_BITMAP
8#define _CRT_SECURE_NO_WARNINGS   // Disable deprecation warning in VS2005
9#define _XOPEN_SOURCE 600         // For PATH_MAX on linux
10#undef WIN32_LEAN_AND_MEAN        // Let windows.h always include winsock2.h
11
12#include <sys/stat.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <signal.h>
16#include <string.h>
17#include <errno.h>
18#include <limits.h>
19#include <stddef.h>
20#include <stdarg.h>
21#include <ctype.h>
22#include <time.h>
23
24#include "mongoose.h"
25
26#ifdef _WIN32
27#include <windows.h>
28#include <direct.h>  // For chdir()
29#include <winsvc.h>
30#include <shlobj.h>
31
32#ifndef PATH_MAX
33#define PATH_MAX MAX_PATH
34#endif
35
36#ifndef S_ISDIR
37#define S_ISDIR(x) ((x) & _S_IFDIR)
38#endif
39
40#define DIRSEP '\\'
41#define snprintf _snprintf
42#define vsnprintf _vsnprintf
43#define sleep(x) Sleep((x) * 1000)
44#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
45#define SIGCHLD 0
46typedef struct _stat file_stat_t;
47#define stat(x, y) _stat((x), (y))
48#else
49typedef struct stat file_stat_t;
50#include <sys/wait.h>
51#include <unistd.h>
52
53#ifdef IOS
54#include <ifaddrs.h>
55#endif
56
57#define DIRSEP '/'
58#define __cdecl
59#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
60#endif // _WIN32
61
62#define MAX_OPTIONS 100
63#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
64
65#ifndef MVER
66#define MVER MONGOOSE_VERSION
67#endif
68
69static int exit_flag;
70static char server_name[50];        // Set by init_server_name()
71static char s_config_file[PATH_MAX];  // Set by process_command_line_arguments
72static struct mg_server *server;    // Set by start_mongoose()
73static const char *s_default_document_root = ".";
74static const char *s_default_listening_port = "8080";
75static char **s_argv = { NULL };
76
77static void set_options(char *argv[]);
78
79#if !defined(CONFIG_FILE)
80#define CONFIG_FILE "mongoose.conf"
81#endif /* !CONFIG_FILE */
82
83static void __cdecl signal_handler(int sig_num) {
84  // Reinstantiate signal handler
85  signal(sig_num, signal_handler);
86
87#ifndef _WIN32
88  // Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX)
89  // reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid()
90  // fails if SIGCHLD is ignored, making system() non-functional.
91  if (sig_num == SIGCHLD) {
92    do {} while (waitpid(-1, &sig_num, WNOHANG) > 0);
93  } else
94#endif
95  { exit_flag = sig_num; }
96}
97
98static void vnotify(const char *fmt, va_list ap, int must_exit) {
99  char msg[200];
100
101  vsnprintf(msg, sizeof(msg), fmt, ap);
102  fprintf(stderr, "%s\n", msg);
103
104  if (must_exit) {
105    exit(EXIT_FAILURE);
106  }
107}
108
109static void notify(const char *fmt, ...) {
110  va_list ap;
111  va_start(ap, fmt);
112  vnotify(fmt, ap, 0);
113  va_end(ap);
114}
115
116static void die(const char *fmt, ...) {
117  va_list ap;
118  va_start(ap, fmt);
119  vnotify(fmt, ap, 1);
120  va_end(ap);
121}
122
123static void show_usage_and_exit(void) {
124  const char **names;
125  int i;
126
127  fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
128          MVER, __DATE__);
129  fprintf(stderr, "Usage:\n");
130#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
131  fprintf(stderr, "  mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
132#endif
133  fprintf(stderr, "  mongoose [config_file]\n");
134  fprintf(stderr, "  mongoose [-option value ...]\n");
135  fprintf(stderr, "\nOPTIONS:\n");
136
137  names = mg_get_valid_option_names();
138  for (i = 0; names[i] != NULL; i += 2) {
139    fprintf(stderr, "  -%s %s\n",
140            names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
141  }
142  exit(EXIT_FAILURE);
143}
144
145#define EV_HANDLER NULL
146
147static char *sdup(const char *str) {
148  char *p;
149  if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
150    strcpy(p, str);
151  }
152  return p;
153}
154
155static void set_option(char **options, const char *name, const char *value) {
156  int i;
157
158  for (i = 0; i < MAX_OPTIONS - 3; i++) {
159    if (options[i] == NULL) {
160      options[i] = sdup(name);
161      options[i + 1] = sdup(value);
162      options[i + 2] = NULL;
163      break;
164    } else if (!strcmp(options[i], name)) {
165      free(options[i + 1]);
166      options[i + 1] = sdup(value);
167      break;
168    }
169  }
170
171  if (i == MAX_OPTIONS - 3) {
172    die("%s", "Too many options specified");
173  }
174}
175
176static void process_command_line_arguments(char *argv[], char **options) {
177  char line[MAX_CONF_FILE_LINE_SIZE], opt[sizeof(line)], val[sizeof(line)],
178       *p, cpath[PATH_MAX];
179  FILE *fp = NULL;
180  size_t i, cmd_line_opts_start = 1, line_no = 0;
181
182  // Should we use a config file ?
183  if (argv[1] != NULL && argv[1][0] != '-') {
184    snprintf(cpath, sizeof(cpath), "%s", argv[1]);
185    cmd_line_opts_start = 2;
186  } else if ((p = strrchr(argv[0], DIRSEP)) == NULL) {
187    // No command line flags specified. Look where binary lives
188    snprintf(cpath, sizeof(cpath), "%s", CONFIG_FILE);
189  } else {
190    snprintf(cpath, sizeof(cpath), "%.*s%c%s",
191             (int) (p - argv[0]), argv[0], DIRSEP, CONFIG_FILE);
192  }
193  abs_path(cpath, s_config_file, sizeof(s_config_file));
194
195  fp = fopen(s_config_file, "r");
196
197  // If config file was set in command line and open failed, die
198  if (cmd_line_opts_start == 2 && fp == NULL) {
199    die("Cannot open config file %s: %s", s_config_file, strerror(errno));
200  }
201
202  // Load config file settings first
203  if (fp != NULL) {
204    fprintf(stderr, "Loading config file %s\n", s_config_file);
205
206    // Loop over the lines in config file
207    while (fgets(line, sizeof(line), fp) != NULL) {
208      line_no++;
209
210      // Ignore empty lines and comments
211      for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
212      if (line[i] == '#' || line[i] == '\0') {
213        continue;
214      }
215
216      if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
217        printf("%s: line %d is invalid, ignoring it:\n %s",
218               s_config_file, (int) line_no, line);
219      } else {
220        set_option(options, opt, val);
221      }
222    }
223
224    fclose(fp);
225  }
226
227  // If we're under MacOS and started by launchd, then the second
228  // argument is process serial number, -psn_.....
229  // In this case, don't process arguments at all.
230  if (argv[1] == NULL || memcmp(argv[1], "-psn_", 5) != 0) {
231    // Handle command line flags.
232    // They override config file and default settings.
233    for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
234      if (argv[i][0] != '-' || argv[i + 1] == NULL) {
235        show_usage_and_exit();
236      }
237      set_option(options, &argv[i][1], argv[i + 1]);
238    }
239  }
240}
241
242static void init_server_name(void) {
243  const char *descr = "";
244  snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s%s",
245           MVER, descr);
246}
247
248static int is_path_absolute(const char *path) {
249#ifdef _WIN32
250  return path != NULL &&
251    ((path[0] == '\\' && path[1] == '\\') ||  // UNC path, e.g. \\server\dir
252     (isalpha(path[0]) && path[1] == ':' && path[2] == '\\'));  // E.g. X:\dir
253#else
254  return path != NULL && path[0] == '/';
255#endif
256}
257
258static char *get_option(char **options, const char *option_name) {
259  int i;
260
261  for (i = 0; options[i] != NULL; i++)
262    if (!strcmp(options[i], option_name))
263      return options[i + 1];
264
265  return NULL;
266}
267
268static void *serving_thread_func(void *param) {
269  struct mg_server *srv = (struct mg_server *) param;
270  while (exit_flag == 0) {
271    mg_poll_server(srv, 1000);
272  }
273  return NULL;
274}
275
276static int path_exists(const char *path, int is_dir) {
277  file_stat_t st;
278  return path == NULL || (stat(path, &st) == 0 &&
279                          ((S_ISDIR(st.st_mode) ? 1 : 0) == is_dir));
280}
281
282static void verify_existence(char **options, const char *name, int is_dir) {
283  const char *path = get_option(options, name);
284  if (!path_exists(path, is_dir)) {
285    notify("Invalid path for %s: [%s]: (%s). Make sure that path is either "
286           "absolute, or it is relative to mongoose executable.",
287           name, path, strerror(errno));
288  }
289}
290
291static void set_absolute_path(char *options[], const char *option_name) {
292  char path[PATH_MAX], abs[PATH_MAX], *option_value;
293  const char *p;
294
295  // Check whether option is already set
296  option_value = get_option(options, option_name);
297
298  // If option is already set and it is an absolute path,
299  // leave it as it is -- it's already absolute.
300  if (option_value != NULL && !is_path_absolute(option_value)) {
301    // Not absolute. Use the directory where mongoose executable lives
302    // be the relative directory for everything.
303    // Extract mongoose executable directory into path.
304    if ((p = strrchr(s_config_file, DIRSEP)) == NULL) {
305      getcwd(path, sizeof(path));
306    } else {
307      snprintf(path, sizeof(path), "%.*s", (int) (p - s_config_file),
308               s_config_file);
309    }
310
311    strncat(path, "/", sizeof(path) - 1);
312    strncat(path, option_value, sizeof(path) - 1);
313
314    // Absolutize the path, and set the option
315    abs_path(path, abs, sizeof(abs));
316    set_option(options, option_name, abs);
317  }
318}
319
320#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
321int modify_passwords_file(const char *fname, const char *domain,
322                          const char *user, const char *pass) {
323  int found;
324  char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
325  FILE *fp, *fp2;
326
327  found = 0;
328  fp = fp2 = NULL;
329
330  // Regard empty password as no password - remove user record.
331  if (pass != NULL && pass[0] == '\0') {
332    pass = NULL;
333  }
334
335  (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
336
337  // Create the file if does not exist
338  if ((fp = fopen(fname, "a+")) != NULL) {
339    fclose(fp);
340  }
341
342  // Open the given file and temporary file
343  if ((fp = fopen(fname, "r")) == NULL) {
344    return 0;
345  } else if ((fp2 = fopen(tmp, "w+")) == NULL) {
346    fclose(fp);
347    return 0;
348  }
349
350  // Copy the stuff to temporary file
351  while (fgets(line, sizeof(line), fp) != NULL) {
352    if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
353      continue;
354    }
355
356    if (!strcmp(u, user) && !strcmp(d, domain)) {
357      found++;
358      if (pass != NULL) {
359        mg_md5(ha1, user, ":", domain, ":", pass, NULL);
360        fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
361      }
362    } else {
363      fprintf(fp2, "%s", line);
364    }
365  }
366
367  // If new user, just add it
368  if (!found && pass != NULL) {
369    mg_md5(ha1, user, ":", domain, ":", pass, NULL);
370    fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
371  }
372
373  // Close files
374  fclose(fp);
375  fclose(fp2);
376
377  // Put the temp file in place of real file
378  remove(fname);
379  rename(tmp, fname);
380
381  return 1;
382}
383#endif
384
385static void start_mongoose(int argc, char *argv[]) {
386  s_argv = argv;
387  if ((server = mg_create_server(NULL, EV_HANDLER)) == NULL) {
388    die("%s", "Failed to start Mongoose.");
389  }
390
391#if !defined(MONGOOSE_NO_AUTH) && !defined(MONGOOSE_NO_FILESYSTEM)
392  // Edit passwords file if -A option is specified
393  if (argc > 1 && !strcmp(argv[1], "-A")) {
394    if (argc != 6) {
395      show_usage_and_exit();
396    }
397    exit(modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
398         EXIT_SUCCESS : EXIT_FAILURE);
399  }
400#endif
401
402  // Show usage if -h or --help options are specified
403  if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
404    show_usage_and_exit();
405  }
406  set_options(argv);
407}
408
409static void set_options(char *argv[]) {
410  char *options[MAX_OPTIONS];
411  int i;
412
413  options[0] = NULL;
414  set_option(options, "document_root", s_default_document_root);
415  set_option(options, "listening_port", s_default_listening_port);
416
417  // Update config based on command line arguments
418  process_command_line_arguments(argv, options);
419
420  // Make sure we have absolute paths for files and directories
421  // https://github.com/valenok/mongoose/issues/181
422  set_absolute_path(options, "document_root");
423  set_absolute_path(options, "dav_auth_file");
424  set_absolute_path(options, "cgi_interpreter");
425  set_absolute_path(options, "access_log_file");
426  set_absolute_path(options, "global_auth_file");
427  set_absolute_path(options, "ssl_certificate");
428
429  if (!path_exists(get_option(options, "document_root"), 1)) {
430    set_option(options, "document_root", s_default_document_root);
431    set_absolute_path(options, "document_root");
432    notify("Setting document_root to [%s]",
433           mg_get_option(server, "document_root"));
434  }
435
436  // Make extra verification for certain options
437  verify_existence(options, "document_root", 1);
438  verify_existence(options, "cgi_interpreter", 0);
439  verify_existence(options, "ssl_certificate", 0);
440
441  for (i = 0; options[i] != NULL; i += 2) {
442    const char *msg = mg_set_option(server, options[i], options[i + 1]);
443    if (msg != NULL) {
444      notify("Failed to set option [%s] to [%s]: %s",
445             options[i], options[i + 1], msg);
446      if (!strcmp(options[i], "listening_port")) {
447        mg_set_option(server, "listening_port", s_default_listening_port);
448        notify("Setting %s to [%s]", options[i], s_default_listening_port);
449      }
450    }
451    free(options[i]);
452    free(options[i + 1]);
453  }
454
455  // Change current working directory to document root. This way,
456  // scripts can use relative paths.
457  chdir(mg_get_option(server, "document_root"));
458
459#if 0
460  // Add an ability to pass listening socket to mongoose
461  {
462    const char *env = getenv("MONGOOSE_LISTENING_SOCKET");
463    if (env != NULL && atoi(env) > 0 ) {
464      mg_set_listening_socket(server, atoi(env));
465    }
466  }
467#endif
468
469  // Setup signal handler: quit on Ctrl-C
470  signal(SIGTERM, signal_handler);
471  signal(SIGINT, signal_handler);
472#ifndef _WIN32
473  signal(SIGCHLD, signal_handler);
474#endif
475}
476
477int main(int argc, char *argv[]) {
478  init_server_name();
479  start_mongoose(argc, argv);
480  printf("%s serving [%s] on port %s\n",
481         server_name, mg_get_option(server, "document_root"),
482         mg_get_option(server, "listening_port"));
483  fflush(stdout);  // Needed, Windows terminals might not be line-buffered
484  serving_thread_func(server);
485  printf("Exiting on signal %d ...", exit_flag);
486  fflush(stdout);
487  mg_destroy_server(&server);
488  printf("%s\n", " done.");
489
490  return EXIT_SUCCESS;
491}
trunk/3rdparty/mongoose/examples/websocket_chat/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = websocket_chat
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8$(PROG): $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/websocket_chat/index.html
r0r242829
1<!DOCTYPE html>
2<html lang="en">
3<head>
4  <meta charset="utf-8" />
5  <title>WebSocket Test</title>
6  <meta name="viewport" content="width=device-width, initial-scale=1" />
7  <style type="text/css">
8    body {
9      background-color: #cde; margin: 0;
10      padding: 0; font: 14px Helvetica, Arial, sans-serif;
11    }
12    div.content {
13      width: 800px; margin: 2em auto; padding: 20px 50px;
14      background-color: #fff; border-radius: 1em;
15    }
16    #messages {
17      border: 2px solid #fec; border-radius: 1em;
18      height: 10em; overflow: scroll; padding: 0.5em 1em;
19    }
20    a:link, a:visited { color: #69c; text-decoration: none; }
21    @media (max-width: 700px) {
22      body { background-color: #fff; }
23      div.content {
24        width: auto; margin: 0 auto; border-radius: 0;
25        padding: 1em;
26      }
27    }
28</style>
29
30<script language="javascript" type="text/javascript">
31
32  var rooms = [];
33  var ws = new WebSocket('ws://' + location.host + '/ws');
34
35  if (!window.console) { window.console = { log: function() {} } };
36
37  ws.onopen = function(ev)  { console.log(ev); };
38  ws.onerror = function(ev) { console.log(ev); };
39  ws.onclose = function(ev) { console.log(ev); };
40  ws.onmessage = function(ev) {
41    console.log(ev);
42    var m = (ev.data || '').match(/^(\S+) (.+)/);
43    if (m[1] == 'id') {
44      document.getElementById('my_id').innerText = m[2];
45    } else if (m[1] == 'msg') {
46      var div = document.createElement('div');
47      div.innerHTML = m[2];
48      document.getElementById('messages').appendChild(div);
49    }
50  };
51
52  window.onload = function() {
53    document.getElementById('send_button').onclick = function(ev) {
54      var msg = document.getElementById('send_input').value;
55      ws.send('msg ' + msg);
56    };
57    document.getElementById('room_sel').onchange = function(ev) {
58      var roomName = this.value || '?';
59      ws.send('join ' + roomName);
60    };
61  };
62</script>
63</head>
64<body>
65  <div class="content">
66    <h1>Websocket PubSub Demonstration</h1>
67
68    <p>
69      This page demonstrates how Mongoose web server could be used to implement
70      <a href="http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">
71       publish–subscribe pattern</a>. Open this page in several browser
72       windows. Each window initiates persistent
73       <a href="http://en.wikipedia.org/wiki/WebSocket">WebSocket</a>
74      connection with Mongoose, making each browser window a websocket client.
75      Join a room, send messages, and see messages sent by other clients.
76    </p>
77
78    <p>
79      My ID: <b><span id="my_id"></b></span>
80    </p>
81    <p>
82      Join room: <select id="room_sel">
83        <option value="">-- select room -- </option>
84        <option>A</option>
85        <option>B</option>
86      </select>
87    </p>
88
89    <div id="messages">
90    </div>
91
92    <p>
93      <input type="text" id="send_input" />
94      <button id="send_button">Send Message</button>
95    </p>
96  </div>
97</body>
98</html>
trunk/3rdparty/mongoose/examples/websocket_chat/websocket_chat.c
r0r242829
1// Copyright (c) 2013-2014 Cesanta Software Limited
2// $Date: 2014-09-09 17:07:55 UTC $
3
4#include <string.h>
5#include <time.h>
6#include <signal.h>
7#include <stdlib.h>
8#include "mongoose.h"
9
10static int s_signal_received = 0;
11static struct mg_server *s_server = NULL;
12
13// Data associated with each websocket connection
14struct conn_data {
15  int room;
16};
17
18static void signal_handler(int sig_num) {
19  signal(sig_num, signal_handler);  // Reinstantiate signal handler
20  s_signal_received = sig_num;
21}
22
23static void handle_websocket_message(struct mg_connection *conn) {
24  struct conn_data *d = (struct conn_data *) conn->connection_param;
25  struct mg_connection *c;
26
27  printf("[%.*s]\n", (int) conn->content_len, conn->content);
28  if (conn->content_len > 5 && !memcmp(conn->content, "join ", 5)) {
29    // Client joined new room
30    d->room = conn->content[5];
31  } else if (conn->content_len > 4 && !memcmp(conn->content, "msg ", 4) &&
32             d->room != 0 && d->room != '?') {
33    // Client has sent a message. Push this message to all clients
34    // that are subscribed to the same room as client
35    for (c = mg_next(s_server, NULL); c != NULL; c = mg_next(s_server, c)) {
36      struct conn_data *d2 = (struct conn_data *) c->connection_param;
37      if (!c->is_websocket || d2->room != d->room) continue;
38      mg_websocket_printf(c, WEBSOCKET_OPCODE_TEXT, "msg %c %p %.*s",
39                          (char) d->room, conn,
40                          conn->content_len - 4, conn->content + 4);
41    }
42  }
43}
44
45static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
46  switch (ev) {
47    case MG_REQUEST:
48      if (conn->is_websocket) {
49        handle_websocket_message(conn);
50        return MG_TRUE;
51      } else {
52        mg_send_file(conn, "index.html", NULL);  // Return MG_MORE after!
53        return MG_MORE;
54      }
55    case MG_WS_CONNECT:
56      // New websocket connection. Send connection ID back to the client.
57      conn->connection_param = calloc(1, sizeof(struct conn_data));
58      mg_websocket_printf(conn, WEBSOCKET_OPCODE_TEXT, "id %p", conn);
59      return MG_FALSE;
60    case MG_CLOSE:
61      free(conn->connection_param);
62      return MG_TRUE;
63    case MG_AUTH:
64      return MG_TRUE;
65    default:
66      return MG_FALSE;
67  }
68}
69
70int main(void) {
71  s_server = mg_create_server(NULL, ev_handler);
72  mg_set_option(s_server, "listening_port", "8080");
73
74  signal(SIGTERM, signal_handler);
75  signal(SIGINT, signal_handler);
76
77  printf("Started on port %s\n", mg_get_option(s_server, "listening_port"));
78  while (s_signal_received == 0) {
79    mg_poll_server(s_server, 100);
80  }
81  mg_destroy_server(&s_server);
82  return 0;
83}
trunk/3rdparty/mongoose/examples/websocket_echo_server/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = websocket_echo_server
5CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = $(PROG).c ../../mongoose.c
7
8$(PROG): $(SOURCES)
9   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS)
10
11clean:
12   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/websocket_echo_server/index.html
r0r242829
1<!DOCTYPE html>
2<meta charset="utf-8" />
3<title>WebSocket Test</title>
4<script language="javascript" type="text/javascript">
5
6  var out = function(message) {
7    var div = document.createElement('div');
8    div.innerHTML = message;
9    document.getElementById('output').appendChild(div);
10  };
11
12  window.onload = function() {
13    var url = 'ws://' + location.host + '/ws';
14    var num_messages = 0;
15
16    websocket = new WebSocket(url);
17    websocket.onopen = function(ev) {
18      out('CONNECTED');
19      var msg = 'Не всё подчиняется разуму. Но всё подчиняется упорству. ';
20      out('SENT: ' + msg);
21      websocket.send(msg);
22    };
23    websocket.onclose = function(ev) {
24      out('DISCONNECTED');
25    };
26    websocket.onmessage = function(ev) {
27      if (!ev.data) {
28        out('<span style="color: blue;">PING... </span>');
29      } else {
30        out('<span style="color: blue;">RESPONSE: ' + ev.data + ' </span>');
31        num_messages++;
32      }
33      if (num_messages > 3) {
34        websocket.send('exit');
35      }
36    };
37    websocket.onerror = function(ev) {
38      out('<span style="color: red; ">ERROR: </span> ' + ev.data);
39    };
40  };
41</script>
42<style> div {font: small Verdana; } </style>
43<h2>Mongoose WebSocket Test</h2>
44
45<div id="output"></div>
46</html>
trunk/3rdparty/mongoose/examples/websocket_echo_server/websocket_echo_server.c
r0r242829
1// Copyright (c) 2013-2014 Cesanta Software Limited
2// $Date: 2014-09-09 17:07:55 UTC $
3
4#include <string.h>
5#include <time.h>
6#include "mongoose.h"
7
8static void push_message(struct mg_server *server, time_t current_time) {
9  struct mg_connection *c;
10  char buf[20];
11  int len = sprintf(buf, "%lu", (unsigned long) current_time);
12
13  // Iterate over all connections, and push current time message to websocket ones.
14  for (c = mg_next(server, NULL); c != NULL; c = mg_next(server, c)) {
15    if (c->is_websocket) {
16      mg_websocket_write(c, 1, buf, len);
17    }
18  }
19}
20
21static int send_reply(struct mg_connection *conn) {
22  if (conn->is_websocket) {
23    // This handler is called for each incoming websocket frame, one or more
24    // times for connection lifetime.
25    // Echo websocket data back to the client.
26    mg_websocket_write(conn, 1, conn->content, conn->content_len);
27    return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ?
28      MG_FALSE : MG_TRUE;
29  } else {
30    mg_send_file(conn, "index.html", NULL);
31    return MG_MORE;
32  }
33}
34
35static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
36  switch (ev) {
37    case MG_AUTH: return MG_TRUE;
38    case MG_REQUEST: return send_reply(conn);
39    default: return MG_FALSE;
40  }
41}
42
43int main(void) {
44  struct mg_server *server = mg_create_server(NULL, ev_handler);
45  time_t current_timer = 0, last_timer = time(NULL);
46
47  mg_set_option(server, "listening_port", "8080");
48
49  printf("Started on port %s\n", mg_get_option(server, "listening_port"));
50  for (;;) {
51    mg_poll_server(server, 100);
52    current_timer = time(NULL);
53    if (current_timer - last_timer > 0) {
54      last_timer = current_timer;
55      push_message(server, current_timer);
56    }
57  }
58
59  mg_destroy_server(&server);
60  return 0;
61}
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = ws_ssl
5CFLAGS = -W -Wall -I../.. -I. -pthread -g -O0 $(CFLAGS_EXTRA)
6SOURCES = ws_ssl.c ../../mongoose.c ssl_wrapper.c
7
8all: $(PROG)
9
10$(PROG): $(SOURCES)
11   $(CC) -o $(PROG) $(SOURCES) \
12      -DNS_ENABLE_SSL -DSSL_WRAPPER_USE_AS_LIBRARY -lssl $(CFLAGS)
13
14clean:
15   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/certs/ws1_ca.pem
r0r242829
1-----BEGIN RSA PRIVATE KEY-----
2MIIEowIBAAKCAQEAwizPnrCx+/kPSdEeSJFLDXrBH+cSQsSLrCm99G1hCjzqSlIk
31BhkZMEHxBaiVLky4+M/nwhjwwRHI10h6U2Or3tbPLv7z94cPf+uCx1aF7TE3Fm3
46YnDk+CjrYVFN5GRPGOPPdFxGoc+vFvQJyAAimvchnS1ZoEQFvermwzOnKspA6gc
5Px+7wnOeju9TyJuDr5ngtDXFnkcpkBNPxz3En4MJY4xJiaueafh9pIES2vSl7uP0
6J/qot9v2rdiL7nt1H1vwseeEkZhQ+NLB5e2z4psyktJcwDX7wQ6j7JnKfHeP+ixO
7TUORgV4foBMVOqo//Guo92Q5HoLNK77V0y4+ZQIDAQABAoIBAGEsx+LlDs3JQQty
8KjOq8uKWElyC6bKcZkIMydGvg6b6AU6ceW3jnyqFJ/vMUAUSghNmQQq3yiVo2Kks
9DLKTa9sKYwisE0NeJsgoUtOhJttCTlrwU4f+t/AjtgY68f7zTLnqIV+Ql4ftM0pU
10sIFEFMExZbWsZrQb1w+Hd0wrRqNEbSOfSjHeigvuw1T3GH2tSBUTGTpcoewCzy7U
11PKS5pkYyiKySQQNqZTac3NHPjxdK6xxzwURZp1irKdiPdt04KHLVLX8KXelt/J0k
12AeYkVbpFIeQ9rNBerMEp6uRBt+nE5mvP+xx1XPqKRxuxbMyTnBXeOM2zS/a/dBiz
13fwokwcECgYEA9RSsv9AQ/AR8tt+aPEQvjhJ5pn/YbCb1DA9IDXpaq3tzacGd8JHj
143kUtb79nosu85LvSkAYmtzgfJs6xZyUkscra6q+xlsJ12QRxLzqfxcp9Y0wsdqM4
15AcOwuiPKrjkWxOQpyWPWRwbmAefLfRMekHt4Y/QY0CwhslpnsOsj3O0CgYEAytOE
168I4GBfSQfSjXwfrso++Oi75VSsl5ZeiMGihfEhYFTE8/3rEZf7nf9iFSkN3TT+7f
17pFqQzddzPBZXlpVM6k1jcEjdpJizbeR8DmICpABFrZvKz1o8pQ2Yw+FYI86ih0x4
18806snMNgg/RgcVijXKFrC5joJOI+DVgwWoQyMFkCgYBxt4MkiV2oIkjf7ca6GgVa
19zbXGjOGV5Umkq96J6nDxyplVw/IN8xOhScX4aP6kahaep4vfKguCzjaeIh/stS5e
20lLqZVKZ5Roe6B7ag7HnAI+GkVm73KWrOXse8xui/iFvJRfkhqgJ9+HR3A9/GjD2N
21Ws0Uy+lLhn6oLAya6bA9TQKBgAVfZP4aRP6TY+Bs3Io+41XUWqpI+GlqvNR+PHfU
226e/ItYs37jEv78T6X3xdlZpQxfAwG6x22a8aLetBjEBo5Aiw1Bl9VKGvidE3ZDHd
23VsSRXUckAVNMyJ52pb1KktMf/h4nYGzRgLEGW+Ai8QsPlgQ2ImfEPSH8/DfORjmf
24ltTBAoGBAMxIZ52DrJvuxogSOfA1MoCD6a90trkXCquvi+A/fXojZ8BHmMQshvhK
25rAO7SDIV1i1Nh3jQ/oFWE8KOprqrOLO6jNTyF65vh+zk7ztGsEME9FkDhHasUiXf
26t5PE9KeTChHRvIa4FGCl9We9GftE5Ii77LWMOIq22pyxYbvHQFEf
27-----END RSA PRIVATE KEY-----
28-----BEGIN CERTIFICATE-----
29MIIDlDCCAnygAwIBAgIJAIOoO+AapJ5WMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNV
30BAMTA3dzMTEMMAoGA1UEChMDd3MxMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVi
31bGluMB4XDTE0MDgwMzA5MTU0NVoXDTI0MDczMTA5MTU0NVowOjEMMAoGA1UEAxMD
32d3MxMQwwCgYDVQQKEwN3czExCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZEdWJsaW4w
33ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCLM+esLH7+Q9J0R5IkUsN
34esEf5xJCxIusKb30bWEKPOpKUiTUGGRkwQfEFqJUuTLj4z+fCGPDBEcjXSHpTY6v
35e1s8u/vP3hw9/64LHVoXtMTcWbfpicOT4KOthUU3kZE8Y4890XEahz68W9AnIACK
36a9yGdLVmgRAW96ubDM6cqykDqBw/H7vCc56O71PIm4OvmeC0NcWeRymQE0/HPcSf
37gwljjEmJq55p+H2kgRLa9KXu4/Qn+qi32/at2Ivue3UfW/Cx54SRmFD40sHl7bPi
38mzKS0lzANfvBDqPsmcp8d4/6LE5NQ5GBXh+gExU6qj/8a6j3ZDkegs0rvtXTLj5l
39AgMBAAGjgZwwgZkwHQYDVR0OBBYEFL54xAgtJTW6US4Mbr4QG0yKzvaxMGoGA1Ud
40IwRjMGGAFL54xAgtJTW6US4Mbr4QG0yKzvaxoT6kPDA6MQwwCgYDVQQDEwN3czEx
41DDAKBgNVBAoTA3dzMTELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpboIJAIOo
42O+AapJ5WMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJz/RzMa9Wa2
43eEXed7ijH1gcWtgVsVT1xZo0ksFl+QJ5Be1AJpOIe8nKdzYjxPWUkofIoaGHdMLL
44Uc/udRzsXncup+0mD+Yos6Cqyo9yHq7L1HbXfKYZtBXIjWHdF2+RP8j9tHfITXYI
45Pb2zsQ+A6PYpp5OLGZTDAnI2qffqsmwXFNhPfFhOANrGlOjsvy1P7JDzvymj/90m
46NomlO3vjxLHOf6MvedTgCB0dRcAoUWPgbxPWifjBmGBjQjA4ukMQ58wbBQgvIoCW
47obrXmLCNZIkpWTw4gMRYquY880IYK/OuFNJH/dawxx/WzuVr7IdLmbFY15zf5TUb
48ZpIpwqRCysg=
49-----END CERTIFICATE-----
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/certs/ws1_client.pem
r0r242829
1-----BEGIN RSA PRIVATE KEY-----
2MIIEpQIBAAKCAQEAwOp1RS9RnE8L5TszDPIOmpf/1pqb+ki99l/sGhqB/KZBKCuq
3uoCc2VPK3PCByBE15/xJ7t691FnJfForI9DO5p2R0FPD6o357hqRsh0dJNBm0VgG
4iNtLQ8lyYoE72HJbkgCAUZW4N0OBibOmvp/s3Fmr7rEjW5LmZxOtX+iULKIUZ0w6
5gJlM8G5QA1SuqndN0PbPx+RZKSXZCoJj+Nboqw03nyJUexzs+lynR9ITMziUaaVM
64rzG+P6joQAnUkDNydxo/d4tj4xaioZdKxWLYEbj2BUtOlJJydotWo2wcG85ONrT
7Gw0ltR1vku1hidMm2QL30uGIL5SeqyE8TqWk4QIDAQABAoIBAQCxHtKauc45MA4g
84hCGAzvLTmETnRI2YlEfAoTYlpvf5pkOE8GFyI25r4gjACJ4GO0gWG9dBF7Pt7wZ
9EwRmttEvxV3aIv5OvRnKNdSs7rQSV9D+xc4CGy1oSG1f6X2TxbMzQoiN32OqQa2O
10S0Z94IFs8lu8JCDtc9tcqiFVXEmnC3RvJZOShWpsCsbmh5ue1Xed0MQQ47vt7Zt7
11I0cutvwSFJMsZkZUJp5+KjVNYo9TEJxVD3m2NJNJxBfBoRVHXNii3hUEHcTIdIAz
12omtRwBU8AKgJirGIRo1h2ZFyubI8ScGOJWIiWMQvQqTHKiOaz3yUar1NSG+kFn0U
13cj7s3FhdAoGBAOQbx8Jwhtp4iOkP6aW1nVTgiaTj4LMlyJZioFwgPFRIcA3oRHt9
145SRetmgFZNvcuNw1udfeaObKmlzxwUruprwOpihgAQWJFTtOjQNrt2gsYuX4l3W6
15T46dO2W1pV+mW4A5gt0aqhLv7pCS4lpreNAqyHSPqcQWcCeiTzmp/LfDAoGBANiB
16FQOTyMElR9OzKwmcGfIvnhUfJQwi5qNE3R+xXiP5x36a9HQBey5ri2lnBle0Ksr/
17G+RKoflmk1eFXVHN0w35yw0dVco//WE4vOknldNEnIT85k02ld8lDTa2Q/EJZtPH
18un6zeU0Q2/4SZ/GXPssEZPlpPM7WtQzztlH3+sqLAoGBAKnhppvAgi4ippQsLa4j
1929BiiSAsNiQ1d3XIbfUubL+4UvuIh7gQwp6biu1dVwgHEgWuXYHPOgDn0p51zaao
20pbRYlJZtKVWeChnpHkv15NnIdL8grGwZHTbxElNlPIxHsM2GB1fzi8YeumUhf0In
212AnwUum8NIq8yzo5PxeK6ZNRAoGBAIEA2Q6ankJH/nZsCbbeJq+iI+Wd+ysyGI8s
22Vz2tJ9Tz3iTYG9SLlWRhfF4/nw3fMqhmPa5Xsg+zSRQbSTGXHKz1LEISOq4aVtX5
23QscCaUnLVh//uRJE9iRSJX92NyGGYpjKJ5ubQSnkY9EOEpVnc2jwo2HhjPQKBzNC
24fF53Dh5lAoGALwTN5uxrBZLPu4DtZkOosKkv4l+kzFoOjLJR4vA7ONBx2CSe9G7F
25tSsH7lZS3b0mxBWjO90WhaSvtMWWrfqq8vrqmoTE795fYxNoLfCLK13W31aTDUsI
26pQRJIL30MPvASbcFHN2MD2dXz2nQzY8C9lvtvap/krYiDKDU2L7+iP8=
27-----END RSA PRIVATE KEY-----
28-----BEGIN CERTIFICATE-----
29MIIC7DCCAdQCBRQHBXNHMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMTEM
30MAoGA1UEChMDd3MxMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0
31MDgwMzA5MTU0NVoXDTI0MDczMTA5MTU0NVowOjEMMAoGA1UEAxMDd3MxMQwwCgYD
32VQQKEwN3czExCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG
33SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDA6nVFL1GcTwvlOzMM8g6al//Wmpv6SL32
34X+waGoH8pkEoK6q6gJzZU8rc8IHIETXn/Enu3r3UWcl8Wisj0M7mnZHQU8Pqjfnu
35GpGyHR0k0GbRWAaI20tDyXJigTvYcluSAIBRlbg3Q4GJs6a+n+zcWavusSNbkuZn
36E61f6JQsohRnTDqAmUzwblADVK6qd03Q9s/H5FkpJdkKgmP41uirDTefIlR7HOz6
37XKdH0hMzOJRppUzivMb4/qOhACdSQM3J3Gj93i2PjFqKhl0rFYtgRuPYFS06UknJ
382i1ajbBwbzk42tMbDSW1HW+S7WGJ0ybZAvfS4YgvlJ6rITxOpaThAgMBAAEwDQYJ
39KoZIhvcNAQEFBQADggEBABPLmq6zKOMY0WRjtBoSymq6f+vXeEwtWCfVejdG6RlG
40/PTdCKNvp3OL7FDnmQQ+r5rMs4+Os4fX/g315QFKXu01rqxmFb2XVNhhaECdUWtK
41QP6ZoVZviUiDjhK6a+05aerPCJpkGy/lz0W6gmj4qhuAQbobxb6UbzqTRYY+ZwGk
42+SI3TAVCdmXFlxN/M9b0DbmkseRG8GGFmyRYyRb84vbV6zemFI++5ROUT9zXT7ey
43nYfFJvAAk5jJhY5UP2aMlVWYYa4jUZrrPLoiBLUuRrp67EKGebCH9mgCIf8ztNJF
44fpuvcz++LUeRyTlAGDefe+FyHGIwFzIfZn39D7CaRvM=
45-----END CERTIFICATE-----
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/certs/ws1_server.pem
r0r242829
1-----BEGIN RSA PRIVATE KEY-----
2MIIEpAIBAAKCAQEAyal0BZKRYd+Wpxv4MB8LjjgXv/MxSN5oSAKThlCZ/AWG0FEP
3d4nrBACT2xUxwo+xbYl3joiwL/eCPAp6QNKRGhvXVOnSIFVSjKZWbdX+toqK9pDS
4QMDTL4ZJvK6pLZXknyHjEr0PxZh22F7iS1+C8HxBPj0Xgg/u5/+jPhFPPZ1d5elv
54cm/z+xy6RjFlA80aIeK7dcWssOsOIPjUNFfmoYgR63ScZIlUZj6j8VX9oX7fJID
6jumGajDxgD2nBWFbHcGKin6bz/wZ+OIhXOCDdY7oKuMW4JiBwbfBtedkQuQYS11s
7PRFFYDLoZH59Ivcu0c4F2tomE86qM8THsI910wIDAQABAoIBAG55FAQRfO8/C0rU
8eavy9eOdOvV+hltC66G3N5X3BcQYSvhHz89OkJ6KqnT0MWRCT5KQIhzFKK++SWwW
92U41jCPfaKEtzlzEIQrH/MUC3Byn3OSiBWxPteFtEWv5ytgcKzg52iljxQYcNc7m
10e9WKpzKS/zLXSM+JZvlVA9p2pRA88kUZ/EE5H+FW3kHj5eGNqX+cxUVpL0VKTiLv
11aXWjYotpmDJW/Rn9wRQethm6Gdx3bvo+LEVlJRELNq8NM5H/tZIVRzudJOgzsw5v
123OQGhfKB6Eg/vqSFoZxX6ApXDxmtaSO83B0kK550bDVv4sBnOExGjKCzybt04tet
13KtLPPoECgYEA5WUD+mFL99sCX6pzIyUVlxp9eUhVy5nvhoF6u3mvhay2XsZUY0wy
14+/qVqYSZTvuvJ6JSXc4iVIX8u/gj7914805AwujepIF/8E0AaXLBMndzDE4ze5S5
152RHI7Cy4/3AWOcQ9wFFmUdSs7/6oAkcQtvzP40hGg3J2jAEhIdCqmbMCgYEA4Q0G
16BYP9XeTdh0C+BcP9B5VUEC0jerYS8VqVqriB+9JfT3InI7K08sOG2DiQQBhAHuzL
17RhCECU2a9j0+u5F5JNeY5m3IhU73Lw+lOlUkMaAO6x7JJEzhXhonE7Kv8fWygr/0
18OB7yzqz+YsWdQ2VOPZ88ntlAYE65vzcaVswZY2ECgYEAr7Gt2VA6Ei0A5Wq0Yr+d
19iKz2WzUG2TkelqOG8B4kTDrbNz2qFp+fERV9GWgAz9i+75lIgqZF7vzsdL96LtYv
20NBLEUURwegjhh5hCb4E/7bpFOLCQh9+CdHpFrHYYfzRHIZlnPmxZ9OTyS6J85bmu
21WKjLRKXvs++wUkzvJmoesDcCgYEAkTOB6xUZ5/a+J4HSGI43NylVr4owFgBbgHVd
22k2SwGPXGoM+aCSJINUmKOv9jsrbyyAEntfD5/7aegLlLPGHDs82WzTWP5tLoEOkb
23ReOhEpOejHy0ckNYNQrSo5bqhkZsAogu3fa52jcrejbeHJnEPWX8CtFJA9pHZeP7
24jnzo9IECgYBefHg0dymSj2xxN0XmC+S5cvQu2K/NYUpatoWvHnPiQ87wIM0AWz0O
25D0ghEI+Ze57NbtSrrcTE7hY/LHrAqXGAB9XNIM5g9Pp/lM+XjzKVr1FMf4xpuHf1
26VJJRHrOU14CvMvKbgbPrL6B0d5yrYmeex7GxNw0ZVvtjCa502Eck+w==
27-----END RSA PRIVATE KEY-----
28-----BEGIN CERTIFICATE-----
29MIIC7DCCAdQCBRQHBXNGMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMTEM
30MAoGA1UEChMDd3MxMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0
31MDgwMzA5MTU0NVoXDTI0MDczMTA5MTU0NVowOjEMMAoGA1UEAxMDd3MxMQwwCgYD
32VQQKEwN3czExCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG
33SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJqXQFkpFh35anG/gwHwuOOBe/8zFI3mhI
34ApOGUJn8BYbQUQ93iesEAJPbFTHCj7FtiXeOiLAv94I8CnpA0pEaG9dU6dIgVVKM
35plZt1f62ior2kNJAwNMvhkm8rqktleSfIeMSvQ/FmHbYXuJLX4LwfEE+PReCD+7n
36/6M+EU89nV3l6W/hyb/P7HLpGMWUDzRoh4rt1xayw6w4g+NQ0V+ahiBHrdJxkiVR
37mPqPxVf2hft8kgOO6YZqMPGAPacFYVsdwYqKfpvP/Bn44iFc4IN1jugq4xbgmIHB
38t8G152RC5BhLXWw9EUVgMuhkfn0i9y7RzgXa2iYTzqozxMewj3XTAgMBAAEwDQYJ
39KoZIhvcNAQEFBQADggEBAE20gAykuuaCoP49GnZ/Z6ZItFry4Fl6iCWBDdEsWI9R
40wRNYumeaeejdFPXfSJdTT7UlrVK1WWGLQLq+ixHRDX+V9T67ou85F92H/OxbUoPr
41iz/TZAEBTC1GvTJl49lsfPl1dTWH8T4Ej2hxCUvIJrkCkI2Ov4Wwef6A26USrwBt
42S/CPInjCe6qkE5E8xfTDl8k5IIgMadTPhi5sbV2piBJoN4floJPqR0hdDKbgUymn
435WNSiRkuI6UIDZwQEp+A8TmFBHbSwfTGt2Sz5iI27P8J6pFvR5eRA1k57dRUWNXC
44WAU1nqteP3QAjj9L3o8IO0T62scaiJX8x01gTmVVe2I=
45-----END CERTIFICATE-----
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/certs/ws2_ca.pem
r0r242829
1-----BEGIN RSA PRIVATE KEY-----
2MIIEowIBAAKCAQEAwXIMpHuTNsro2pxe6y1mu27md2Olhvfx26rO3maO0/stIC2z
3G/xQatFDLIWzfKFYOT0iSEj252ENFDCw6aDRKpiaUFtXcMAWkNkkKntEyoEgE45k
4rTrvpay0v0B+ojwjA1Jz/9v35cgvDwTs3vNFno5HhI0m2YF4ocTmeHJ6u0xRL/qy
5atfKsfuVq5s5CXOYCXp3Ux6kJ1c22J0EdZMvS75SVjAZgRkqQpqt9L3e2ZBCEgUr
6w0KwlERvpeJF+sJJOshXjfrDzvwL8IpPnGZLJNINFbSJMk5MFGcMyq/28pSZLB9E
7Dh8vk5D5gUnxM60ONUy2nYPcYr5p1PLDiC8hfQIDAQABAoIBAB0Twpi6xn8W8vdh
8R9c75NRJsDTD8q6d+GnXe+7sJY3xlG/gzqpnO8NCn0FC+57BNdystsl8xjgzW17s
9jrsfZDFt7MwlXrhg90NgkFIeY1G5JRQrdDChykHx+t1AmYhTV8P5EdykuNd+RqyQ
10RfahRJa3tkJTYUKSdoqCaU4zjwU2CSxltuJx24V+WoZE12EwewJ8HPg2XTnbsGE7
11Fnx5s29O4ItM70CC0536AY/OgfuPix5z573VQiilqqfOQkVkKa1fHd6tGpWU+3kH
12X9FnhEBf9cN9tVgmaB0sCSVVrfgqSXg1EwKHqe/+FCumuesA68Q35+/K3b+QrNiR
13ka2yliECgYEA+V/4pbgG/lPYvTwWhKxGXXdJmrSPgZC0mwE+fRuYkeptbIkS0pwt
14/UDTXk9nttj1f1ZJ3NgQbT/1w8jpXfsCJ8VeGzL9+ADhRKWVFRlu/nyFCMXawLEV
15rot7SEr8BW/m8moHgY5lYipM3dXJPk0F+KLrN60U/aNmFUtPGW802BkCgYEAxpWy
16FGL2sEQ0QaRDTcqqF5faVqw+5rVGvN+EX5o26E0QWWnoo3L2c2/5X93iBl+Fqtnm
179jSIQUC3rYOawKnZ/HcIE2ergFit/p6JaV9NiLDRtDUmSzlffEGVCj0neYFsnWp5
18zcmkUyZ6fr19EmKQWfdteNBlXue32TjVlFbfUQUCgYAfMbgi0sBdNBPaqBeRBRPQ
19QUm9xnRlGrrc4Oz2LWuKZS7G8uad3deK5H8MPxaUMtOS2DJpI8X6RJPzp8A5d1qv
20quq4sEpAqauEMMpTV1khEGZ70HQqwnwZ12zWgDrCW1siW80QkcVw4CW5YjLITk4+
216fJOhqInkDcG1uLQJa8QkQKBgQCfs8l4DbJ4RRGFbLXXvNGXkb68j18yqLxPrq3F
22OL9JiJhKYBsAP7clVPrG9ykLmQxlP0I35D1jxMkymLD+mlo9Z/itqmTJHggnyZWW
23kVdIQ3MSKuA2BNjek9tpVY8Gb2hLHFMChVRKrpo6jOclvvB5+bsnOukbLtyyq7tP
24xaFohQKBgByCmlltjOBWZLFLeA1x8j3inm9zM/FAJuANbHUOZ1RwrRcNFbDv/FXm
25rLPnPCaH5AwAWhVRJcNHo37Ee0s/xqe+Q4dG4xL943k+6KlopAw1SXhuXF6PnBfF
26y+ArVlh9d2oWN5cBEzRddnWnKJuMi70kMzYf6dIW9s/dHbq/gFDy
27-----END RSA PRIVATE KEY-----
28-----BEGIN CERTIFICATE-----
29MIIDlDCCAnygAwIBAgIJAJDtcXU2wiJIMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNV
30BAMTA3dzMjEMMAoGA1UEChMDd3MyMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVi
31bGluMB4XDTE0MDgwMzA5MTU0OFoXDTI0MDczMTA5MTU0OFowOjEMMAoGA1UEAxMD
32d3MyMQwwCgYDVQQKEwN3czIxCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZEdWJsaW4w
33ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBcgyke5M2yujanF7rLWa7
34buZ3Y6WG9/Hbqs7eZo7T+y0gLbMb/FBq0UMshbN8oVg5PSJISPbnYQ0UMLDpoNEq
35mJpQW1dwwBaQ2SQqe0TKgSATjmStOu+lrLS/QH6iPCMDUnP/2/flyC8PBOze80We
36jkeEjSbZgXihxOZ4cnq7TFEv+rJq18qx+5WrmzkJc5gJendTHqQnVzbYnQR1ky9L
37vlJWMBmBGSpCmq30vd7ZkEISBSvDQrCURG+l4kX6wkk6yFeN+sPO/Avwik+cZksk
380g0VtIkyTkwUZwzKr/bylJksH0QOHy+TkPmBSfEzrQ41TLadg9xivmnU8sOILyF9
39AgMBAAGjgZwwgZkwHQYDVR0OBBYEFLK4flD5QD/mRufsPx63xlEKM8pwMGoGA1Ud
40IwRjMGGAFLK4flD5QD/mRufsPx63xlEKM8pwoT6kPDA6MQwwCgYDVQQDEwN3czIx
41DDAKBgNVBAoTA3dzMjELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpboIJAJDt
42cXU2wiJIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEYD+CReikYr
43Rzvk+/Vdi/7IcaH9CFknIdtineSIw1y98nxnbnNJqxwfNaRblbYvg6OFdUI3POuI
44+rdYLCFl8z3tWqRHLkGqHSJA9xcng3jLJxz0+ctiVcekJvXaB3O6eSZjhGbmmI/s
45CQhdy2zpEIVOeUq50DrSJp9CknyGu/IkaGx5GOZtkiHMrpig50CRjX1lS6qrMNYp
46vB8gfuqpjsL4Ar3vg+lgMSwNWXBNHrIRPHB5VEzBEdmLFZlvueR0ooEMCklpwX/a
47lFImVc6JcY1pBEkHTiTLGMpGAHG3I1aVUaWb3L+V+2ym/KNRNL5C2+1eiqql5u8m
48HUaOcNC90ew=
49-----END CERTIFICATE-----
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/certs/ws2_client.pem
r0r242829
1-----BEGIN RSA PRIVATE KEY-----
2MIIEpAIBAAKCAQEA0ucemqFwBFziVOgTgx4mZII4WnGDpA/rWAGHvUZOqy2dQ3Pz
3woGKxBaVPAl5kxEosROVGa7dTuq5yFZ4XIGvwCKxF30vCmdGCytqq6MMp904jG32
4KikkmjCApIMGxMO4eoBHZZxiyVvKTbg9M2CRXErwnYWhFH/qGdPnuo0CEaHJA4VK
5A9incT9dpeEhU30R6ajAe/je9rCj2OMLMFSMfd51L/VYfO60zDwUNY7YVIghQZgJ
6e44EVGsp1pXaqaD6o3PvGY3ohw2aZjJzlJ7MJHbKV9lft98R3pklbpBzMH849cEy
7Q/51L/rlfTUgCpTy7wEZpWHQNtHfu/1rhJjpNQIDAQABAoIBAQCUNIHXG/dBuZv7
8GpMLotZL7w520Co30lAJqhmfMpb5x7YpvoPffXTsUwpQBECAzqAPv7kZMT6nxF8F
9n245Y5EDrd1QqlGyN9yK4Nm2/39XPygL1wITopHsIIVmFgVdpEQxIZAKoZjx8yT4
109K1dO1Eq0CbCKzOE2lbCC51eBNUdWZIMxwC6O/j/KoIkZ/HwlG2hpUuXg8x/XawA
11ZJDCoTcWHCjYP10FxKVN3vAyWM2IM44o9IbsAGEOswR4gUwRsgq6Ehc1U59XUHi+
12x30oda55I1/8xD+SfP/zk2dDPHkv/hq5+258GU/THsw2+AAexocvSIS/g9EppTEg
13biFaDKzJAoGBAPqey10JeyiOlHbBjoSSa7lJYUjocQANFQ4ayOAgfNX72iyabrKF
14p9sVAeO/nM00+DPrm2wWws03ScsPeh+9BDJMPRBUHfSNp7+F+oyj7PWHBEFHbyO9
155HnYZP+1vhdG2dYPIY2gRSFXpiGn3j0M1D0w9c7Ilm8ee3krdR4E/mw3AoGBANdu
16EfS1dK3+m7sEgc2+3U32z83GpuCHeUIKGPYMLI0fIb1CPpboHU9YjOFJZH9iIIdl
1700JC963O3+pqLe0XbMOmBVt9QjZfhfB+AY+JHtbPgoQVLtq9X/WvW7h3xn6S971r
18Crkhqay3Cs4BzsgYDYraQCTw3oq4twR9Nuy4etfzAoGAXOsG5wWe3diO/sCggFJx
19Eg88vHVBgA1ZoxMXKtGgtw1bRHI1XIblRvqw6qmeDw72fvl5dEe0DbXT7C9ezemc
20ZrGRaj5lpMfoS7/2trIIJrfaQgGkGRJMZUhvmcbeJW8lUJHnlMS5HLWMaKn+YZAi
21GFXQrMv9ylD44mHUWD7tvV0CgYBNctPfvvCQsQ05ofgsiKa1Jbs1hmpuJCYy2MB6
22jIvjvEJ78PnhdNc8tGAJikIoDZYWN0RI+RxkDxCvDLcwGpDOkbwxVQnd1F+pwxM6
23kBhXL8kDRT5QA28hO4bk/aKN1LZeEcKMJg8C+ddXkozNoOAVgDs5TKMlCh057u41
24EmmPgwKBgQDOlYi7fPYOCy0yjHMxSrp2SZOS06AMWGbbCoGkjRtvuP+FmKSNB+LZ
25pOSEPJgzjsRutKjneww4LpV6dViAyTcP5JoeQpokHf7UVo7yq2QH/iwF3zJwsC/S
26OuVLkqpZzWuye/QCH5NOTfw27ye8jG8VcQW2QPbcbkLXLM7zg2yX7g==
27-----END RSA PRIVATE KEY-----
28-----BEGIN CERTIFICATE-----
29MIIC7DCCAdQCBRQHBXNJMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMjEM
30MAoGA1UEChMDd3MyMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0
31MDgwMzA5MTU0OFoXDTI0MDczMTA5MTU0OFowOjEMMAoGA1UEAxMDd3MyMQwwCgYD
32VQQKEwN3czIxCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG
33SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDS5x6aoXAEXOJU6BODHiZkgjhacYOkD+tY
34AYe9Rk6rLZ1Dc/PCgYrEFpU8CXmTESixE5UZrt1O6rnIVnhcga/AIrEXfS8KZ0YL
35K2qrowyn3TiMbfYqKSSaMICkgwbEw7h6gEdlnGLJW8pNuD0zYJFcSvCdhaEUf+oZ
360+e6jQIRockDhUoD2KdxP12l4SFTfRHpqMB7+N72sKPY4wswVIx93nUv9Vh87rTM
37PBQ1jthUiCFBmAl7jgRUaynWldqpoPqjc+8ZjeiHDZpmMnOUnswkdspX2V+33xHe
38mSVukHMwfzj1wTJD/nUv+uV9NSAKlPLvARmlYdA20d+7/WuEmOk1AgMBAAEwDQYJ
39KoZIhvcNAQEFBQADggEBACCCAJypO9DFU6GeOH+FwE0JCLTypHoIwERWxNL7xfjg
40rwVqIxwAEo+fJjL+QY7JbAb/eqKaXIBYkAF2lFc4iEmecXX/A3Aqw95AYi78o7HD
41MwRPqJha9mxLcCWwjX8XK8pT152BvYFPNhi+6jd++rDRxKDfmNvgdUQ2/YW6a5Wv
42zEmLDPUWRIuMQIEmOa2/JhlllDviMExTw51nbqYgCghycRvDACyQAuu8re7P6gcg
43bXObNlfxcU/8Ph6MFI+2S9ODtQ4BHyuKd4kRNsYn8vV42a0h3bCYSGPk3kSIgxd7
44XijwHT/o8E9ddH2BvDv+6Nhno9C6/MbezEOIs4nlhRk=
45-----END CERTIFICATE-----
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/certs/ws2_server.pem
r0r242829
1-----BEGIN RSA PRIVATE KEY-----
2MIIEpQIBAAKCAQEA29iFnMf4pjty6knOt4wT0joPUlU2dGCFWxQ5Eg77Yx3Lou2a
3FOzNGp7zLYH8gauOK+mgY+B9cBv8PvVtUQQKB0SKTTV8ZNKVyP3O5F2gRSJ+tHtT
4FqaPfG0WPOn/f02YbOpAe6Rk+NnlJoeuBxG4uapUSq7r0mTGJQe+52y1LcMs23ID
5ENBfDoUMVt5vCnF+cYK5ndeFVLyPO3JjohDH1zv3a9ECG28rtjKHLpNYFDsPJaD7
6UPuyyk3hIvfPCZoJOUlEVfpLT/lM+9oCPnq9PwIp5NqYofkuc3eKooCo7N4r4IlP
7Ajktaao6b0ScNwNQB3leOIdEUFSIYy8N1JszVwIDAQABAoIBAFlIvjrGG/2m9yyf
8fQyeHw6p9b8CTHNHH+G1fNgQrZe7ahBpXsJQyZueIjTBLcOb4MmEwFbPvSHiu7b2
9Bcd5VHlPJLvmlPZ9b8eJDJVCUOzC7aJu03fHfU6THwzuG42f/d9942JTiY5nL+FO
10CSdl0xfUTRdnou53buFrG+TxCUPj13HP1HY6DAVzEIq1H4TZwIZo7KRRTIYpTB3P
116yvr9xsISLlnmfQ4tp2pApl5o+bHJEhr1VO6SAT/pSyShi79KmMMqYtyTmOMz7w6
12VJkre5ybnXBDN6tfMHWqdobJ4gRWK9rqf+LIZig5XQnyzkue8k+I7aPgO4xNFh56
13dkejQcECgYEA9MDCWViqpfvof+epiKzccqnIRnz1EfHdRQjiKsKGRe39+K+pyaqJ
14FOOPFy3aOw6M4cFWwcdMYzKTItvzyLVhDqMzT5eup/NVqo5tPoy93XPf2qRYiTl4
152j5wvm0RVkYEONd3pk2lbfbUmn7XQXj0+AG60SvsqErF/UhIBGec/xsCgYEA5fLC
16EdiiC98kr4sVaE8G854WI+aAkStqO5YXyrnsMzRsqk8KVVYE1yCC9rYyymDBYmlx
17uEW+vbWqLc8PO3v4ty3o5ff303lPMWIrvKiUldjqMjS6ncWxsQjU0bygGVgOgHO7
18c8rjiDH5M0JgWSREYUVFT5mW/5+Y1LVT8mYNlHUCgYEAhMSX6N23XGkFW3Twu2qB
19/1Vohgw86OoaDNvfzDBPpFmQ3rlz0ijHSeSTd5BxBH5FICXACUgygNErjcphOSxj
20JQyUxgVTQlo2y1mNm1O/nwS/lxx1xqK9ky4x/Kqvr+w1WBxSFI2kQr2V4OUTobma
21sXpGvDcmnrhJJLd0EaefO6cCgYEA3Xw/S9tC8nZjqqYn34nHI16Q6tF54tpTf8Np
22dT4x8Xw8cqqhRGMPVHsfSi1irKYXfwgbnienuqlBmtAHVv9pKF+TJfb7gXkmO2XY
23xOYIAHGn2uYJHjCun9vmyYKLHv4/MaDH3Jd/I88mviXgEdyp9Js5UJua4utB1Rg3
24HJMJ34UCgYEAr0PpHEBMbZXZBybNU96+jRTgkrNeJpzlnMy7et2IsRAtLjZ0mpbn
25NaX8i8eO+ubweqFdhOvbh7Hd0zr7BzrYcUG1e3njhtxJE1MgWL5plnLVUbIyDAm3
26iBpIHIBASNCN3sqeq+VqXvavRmeZh5O0vyLP46/kxZx0rzR/NCi9xxU=
27-----END RSA PRIVATE KEY-----
28-----BEGIN CERTIFICATE-----
29MIIC7DCCAdQCBRQHBXNIMA0GCSqGSIb3DQEBBQUAMDoxDDAKBgNVBAMTA3dzMjEM
30MAoGA1UEChMDd3MyMQswCQYDVQQGEwJJRTEPMA0GA1UEBxMGRHVibGluMB4XDTE0
31MDgwMzA5MTU0OFoXDTI0MDczMTA5MTU0OFowOjEMMAoGA1UEAxMDd3MyMQwwCgYD
32VQQKEwN3czIxCzAJBgNVBAYTAklFMQ8wDQYDVQQHEwZHYWx3YXkwggEiMA0GCSqG
33SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb2IWcx/imO3LqSc63jBPSOg9SVTZ0YIVb
34FDkSDvtjHcui7ZoU7M0anvMtgfyBq44r6aBj4H1wG/w+9W1RBAoHRIpNNXxk0pXI
35/c7kXaBFIn60e1MWpo98bRY86f9/TZhs6kB7pGT42eUmh64HEbi5qlRKruvSZMYl
36B77nbLUtwyzbcgMQ0F8OhQxW3m8KcX5xgrmd14VUvI87cmOiEMfXO/dr0QIbbyu2
37Mocuk1gUOw8loPtQ+7LKTeEi988Jmgk5SURV+ktP+Uz72gI+er0/Aink2pih+S5z
38d4qigKjs3ivgiU8COS1pqjpvRJw3A1AHeV44h0RQVIhjLw3UmzNXAgMBAAEwDQYJ
39KoZIhvcNAQEFBQADggEBALi/RmqeXGazT/WRj9+ZqdcnbcHwK5wwr2/YkpFPJ0Hf
40ZDm+2vgjDdTT6cJS6fau0M5nliYdz89aQQo1j9RSRZnzlc/2YCFXyRLCOJYaINbj
411MEUAvNDGL7xTpepK9hVkXASRkbyNXERXRKFI1N+vpKu6UorT6/osEV/qM+MFJ3s
4224xE8/J3J4MirVQVt6eY6Jb+tkliOPMIugr6YQlLsqJygEWATP8Qsr81XSfcZhVq
43rXzVt7QV8dO0nStMjKK5omrtEAhVnASk7w1tFHkpBF1rqXGoo9ML40RnFZ+E5zqi
44iZtzp+NzzLnEnWMNs+fJpPJ96P0kbq2bQzuSBcUynq0=
45-----END CERTIFICATE-----
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/net_skeleton.h
r0r242829
1// Copyright (c) 2014 Cesanta Software Limited
2// All rights reserved
3//
4// This software is dual-licensed: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License version 2 as
6// published by the Free Software Foundation. For the terms of this
7// license, see <http://www.gnu.org/licenses/>.
8//
9// You are free to use this software under the terms of the GNU General
10// Public License, but WITHOUT ANY WARRANTY; without even the implied
11// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12// See the GNU General Public License for more details.
13//
14// Alternatively, you can license this software under a commercial
15// license, as set out in <http://cesanta.com/>.
16//
17// $Date: 2014-09-28 05:04:41 UTC $
18
19#ifndef NS_SKELETON_HEADER_INCLUDED
20#define NS_SKELETON_HEADER_INCLUDED
21
22#define NS_SKELETON_VERSION "2.1.0"
23
24#undef UNICODE                  // Use ANSI WinAPI functions
25#undef _UNICODE                 // Use multibyte encoding on Windows
26#define _MBCS                   // Use multibyte encoding on Windows
27#define _INTEGRAL_MAX_BITS 64   // Enable _stati64() on Windows
28#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005+
29#undef WIN32_LEAN_AND_MEAN      // Let windows.h always include winsock2.h
30#define _XOPEN_SOURCE 600       // For flockfile() on Linux
31#define __STDC_FORMAT_MACROS    // <inttypes.h> wants this for C++
32#define __STDC_LIMIT_MACROS     // C++ wants that for INT64_MAX
33#ifndef _LARGEFILE_SOURCE
34#define _LARGEFILE_SOURCE       // Enable fseeko() and ftello() functions
35#endif
36#define _FILE_OFFSET_BITS 64    // Enable 64-bit file offsets
37
38#ifdef _MSC_VER
39#pragma warning (disable : 4127)  // FD_SET() emits warning, disable it
40#pragma warning (disable : 4204)  // missing c99 support
41#endif
42
43#include <sys/types.h>
44#include <sys/stat.h>
45#include <assert.h>
46#include <ctype.h>
47#include <errno.h>
48#include <fcntl.h>
49#include <stdarg.h>
50#include <stddef.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <time.h>
55#include <signal.h>
56
57#ifdef _WIN32
58#ifdef _MSC_VER
59#pragma comment(lib, "ws2_32.lib")    // Linking with winsock library
60#endif
61#include <windows.h>
62#include <process.h>
63#ifndef EINPROGRESS
64#define EINPROGRESS WSAEINPROGRESS
65#endif
66#ifndef EWOULDBLOCK
67#define EWOULDBLOCK WSAEWOULDBLOCK
68#endif
69#ifndef __func__
70#define STRX(x) #x
71#define STR(x) STRX(x)
72#define __func__ __FILE__ ":" STR(__LINE__)
73#endif
74#ifndef va_copy
75#define va_copy(x,y) x = y
76#endif // MINGW #defines va_copy
77#define snprintf _snprintf
78#define vsnprintf _vsnprintf
79#define sleep(x) Sleep((x) * 1000)
80#define to64(x) _atoi64(x)
81typedef int socklen_t;
82typedef unsigned char uint8_t;
83typedef unsigned int uint32_t;
84typedef unsigned short uint16_t;
85typedef unsigned __int64 uint64_t;
86typedef __int64   int64_t;
87typedef SOCKET sock_t;
88typedef struct _stati64 ns_stat_t;
89#ifndef S_ISDIR
90#define S_ISDIR(x) ((x) & _S_IFDIR)
91#endif
92#else
93#include <errno.h>
94#include <fcntl.h>
95#include <netdb.h>
96#include <pthread.h>
97#include <stdarg.h>
98#include <unistd.h>
99#include <arpa/inet.h>  // For inet_pton() when NS_ENABLE_IPV6 is defined
100#include <netinet/in.h>
101#include <sys/socket.h>
102#include <sys/select.h>
103#define closesocket(x) close(x)
104#define __cdecl
105#define INVALID_SOCKET (-1)
106#define to64(x) strtoll(x, NULL, 10)
107typedef int sock_t;
108typedef struct stat ns_stat_t;
109#endif
110
111#ifdef NS_ENABLE_DEBUG
112#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \
113  fflush(stdout); } while(0)
114#else
115#define DBG(x)
116#endif
117
118#ifndef ARRAY_SIZE
119#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
120#endif
121
122#ifdef NS_ENABLE_SSL
123#ifdef __APPLE__
124#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
125#endif
126#include <openssl/ssl.h>
127#else
128typedef void *SSL;
129typedef void *SSL_CTX;
130#endif
131
132#ifdef __cplusplus
133extern "C" {
134#endif // __cplusplus
135
136union socket_address {
137  struct sockaddr sa;
138  struct sockaddr_in sin;
139#ifdef NS_ENABLE_IPV6
140  struct sockaddr_in6 sin6;
141#else
142  struct sockaddr sin6;
143#endif
144};
145
146// Describes chunk of memory
147struct ns_str {
148  const char *p;
149  size_t len;
150};
151
152// IO buffers interface
153struct iobuf {
154  char *buf;
155  size_t len;
156  size_t size;
157};
158
159void iobuf_init(struct iobuf *, size_t initial_size);
160void iobuf_free(struct iobuf *);
161size_t iobuf_append(struct iobuf *, const void *data, size_t data_size);
162void iobuf_remove(struct iobuf *, size_t data_size);
163void iobuf_resize(struct iobuf *, size_t new_size);
164
165// Callback function (event handler) prototype, must be defined by user.
166// Net skeleton will call event handler, passing events defined above.
167struct ns_connection;
168typedef void (*ns_callback_t)(struct ns_connection *, int event_num, void *evp);
169
170// Events. Meaning of event parameter (evp) is given in the comment.
171#define NS_POLL    0  // Sent to each connection on each call to ns_mgr_poll()
172#define NS_ACCEPT  1  // New connection accept()-ed. union socket_address *addr
173#define NS_CONNECT 2  // connect() succeeded or failed. int *success_status
174#define NS_RECV    3  // Data has benn received. int *num_bytes
175#define NS_SEND    4  // Data has been written to a socket. int *num_bytes
176#define NS_CLOSE   5  // Connection is closed. NULL
177
178
179struct ns_mgr {
180  struct ns_connection *active_connections;
181  const char *hexdump_file;         // Debug hexdump file path
182  sock_t ctl[2];                    // Socketpair for mg_wakeup()
183  void *user_data;                  // User data
184};
185
186
187struct ns_connection {
188  struct ns_connection *next, *prev;  // ns_mgr::active_connections linkage
189  struct ns_connection *listener;     // Set only for accept()-ed connections
190  struct ns_mgr *mgr;
191
192  sock_t sock;                // Socket
193  union socket_address sa;    // Peer address
194  struct iobuf recv_iobuf;    // Received data
195  struct iobuf send_iobuf;    // Data scheduled for sending
196  SSL *ssl;
197  SSL_CTX *ssl_ctx;
198  void *user_data;            // User-specific data
199  void *proto_data;           // Application protocol-specific data
200  time_t last_io_time;        // Timestamp of the last socket IO
201  ns_callback_t callback;     // Event handler function
202
203  unsigned int flags;
204#define NSF_FINISHED_SENDING_DATA   (1 << 0)
205#define NSF_BUFFER_BUT_DONT_SEND    (1 << 1)
206#define NSF_SSL_HANDSHAKE_DONE      (1 << 2)
207#define NSF_CONNECTING              (1 << 3)
208#define NSF_CLOSE_IMMEDIATELY       (1 << 4)
209#define NSF_WANT_READ               (1 << 5)
210#define NSF_WANT_WRITE              (1 << 6)
211#define NSF_LISTENING               (1 << 7)
212#define NSF_UDP                     (1 << 8)
213
214#define NSF_USER_1                  (1 << 20)
215#define NSF_USER_2                  (1 << 21)
216#define NSF_USER_3                  (1 << 22)
217#define NSF_USER_4                  (1 << 23)
218#define NSF_USER_5                  (1 << 24)
219#define NSF_USER_6                  (1 << 25)
220};
221
222void ns_mgr_init(struct ns_mgr *, void *user_data);
223void ns_mgr_free(struct ns_mgr *);
224time_t ns_mgr_poll(struct ns_mgr *, int milli);
225void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t);
226
227struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *);
228struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t,
229                                  ns_callback_t, void *);
230struct ns_connection *ns_bind(struct ns_mgr *, const char *,
231                              ns_callback_t, void *);
232struct ns_connection *ns_connect(struct ns_mgr *, const char *,
233                                 ns_callback_t, void *);
234
235int ns_send(struct ns_connection *, const void *buf, int len);
236int ns_printf(struct ns_connection *, const char *fmt, ...);
237int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap);
238
239// Utility functions
240void *ns_start_thread(void *(*f)(void *), void *p);
241int ns_socketpair(sock_t [2]);
242int ns_socketpair2(sock_t [2], int sock_type);  // SOCK_STREAM or SOCK_DGRAM
243void ns_set_close_on_exec(sock_t);
244void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
245int ns_hexdump(const void *buf, int len, char *dst, int dst_len);
246int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
247int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len);
248
249#ifdef __cplusplus
250}
251#endif // __cplusplus
252
253#endif // NS_SKELETON_HEADER_INCLUDED
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/ssl_wrapper.c
r0r242829
1// Copyright (c) 2014 Cesanta Software Limited
2// All rights reserved
3//
4// This software is dual-licensed: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License version 2 as
6// published by the Free Software Foundation. For the terms of this
7// license, see <http://www.gnu.org/licenses/>.
8//
9// You are free to use this software under the terms of the GNU General
10// Public License, but WITHOUT ANY WARRANTY; without even the implied
11// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12// See the GNU General Public License for more details.
13//
14// Alternatively, you can license this software under a commercial
15// license, as set out in <http://cesanta.com/products.html>.
16//
17// $Date$
18
19#include "net_skeleton.h"
20#include "ssl_wrapper.h"
21
22static void ev_handler(struct ns_connection *nc, int ev, void *p) {
23  const char *target_addr = (const char *) nc->mgr->user_data;
24  struct ns_connection *pc = (struct ns_connection *) nc->user_data;
25  struct iobuf *io = &nc->recv_iobuf;
26
27  (void) p;
28  switch (ev) {
29    case NS_ACCEPT:
30      // Create a connection to the target, and interlink both connections
31      nc->user_data = ns_connect(nc->mgr, target_addr, ev_handler, nc);
32      if (nc->user_data == NULL) {
33        nc->flags |= NSF_CLOSE_IMMEDIATELY;
34      }
35      break;
36
37    case NS_CLOSE:
38      // If either connection closes, unlink them and shedule closing
39      if (pc != NULL) {
40        pc->flags |= NSF_FINISHED_SENDING_DATA;
41        pc->user_data = NULL;
42      }
43      nc->user_data = NULL;
44      break;
45
46    case NS_RECV:
47      // Forward arrived data to the other connection, and discard from buffer
48      if (pc != NULL) {
49        ns_send(pc, io->buf, io->len);
50        iobuf_remove(io, io->len);
51      }
52      break;
53
54    default:
55      break;
56  }
57}
58
59void *ssl_wrapper_init(const char *local_addr, const char *target_addr,
60                       const char **err_msg) {
61  struct ns_mgr *mgr = (struct ns_mgr *) calloc(1, sizeof(mgr[0]));
62  *err_msg = NULL;
63
64  if (mgr == NULL) {
65    *err_msg = "malloc failed";
66  } else {
67    ns_mgr_init(mgr, (void *) target_addr);
68    if (ns_bind(mgr, local_addr, ev_handler, NULL) == NULL) {
69      *err_msg = "ns_bind() failed: bad listening_port";
70      ns_mgr_free(mgr);
71      free(mgr);
72      mgr = NULL;
73    }
74  }
75
76  return mgr;
77}
78
79void ssl_wrapper_serve(void *param, volatile int *quit) {
80  struct ns_mgr *mgr = (struct ns_mgr *) param;
81
82  while (*quit == 0) {
83    ns_mgr_poll(mgr, 1000);
84  }
85  ns_mgr_free(mgr);
86  free(mgr);
87}
88
89#ifndef SSL_WRAPPER_USE_AS_LIBRARY
90static int s_received_signal = 0;
91
92static void signal_handler(int sig_num) {
93  signal(sig_num, signal_handler);
94  s_received_signal = sig_num;
95}
96
97static void show_usage_and_exit(const char *prog) {
98  fprintf(stderr, "Usage: %s <listening_address> <target_address>\n", prog);
99  exit(EXIT_FAILURE);
100}
101
102int main(int argc, char *argv[]) {
103  void *wrapper;
104  const char *err_msg;
105
106  if (argc != 3) {
107    show_usage_and_exit(argv[0]);
108  }
109
110  // Setup signal handlers
111  signal(SIGTERM, signal_handler);
112  signal(SIGINT, signal_handler);
113  signal(SIGPIPE, SIG_IGN);
114
115  if ((wrapper = ssl_wrapper_init(argv[1], argv[2], &err_msg)) == NULL) {
116    fprintf(stderr, "Error: %s\n", err_msg);
117    exit(EXIT_FAILURE);
118  }
119  ssl_wrapper_serve(wrapper, &s_received_signal);
120
121  return EXIT_SUCCESS;
122}
123#endif // SSL_WRAPPER_USE_AS_LIBRARY
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/ssl_wrapper.h
r0r242829
1// Copyright (c) 2014 Cesanta Software Limited
2// All rights reserved
3//
4// This software is dual-licensed: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License version 2 as
6// published by the Free Software Foundation. For the terms of this
7// license, see <http://www.gnu.org/licenses/>.
8//
9// You are free to use this software under the terms of the GNU General
10// Public License, but WITHOUT ANY WARRANTY; without even the implied
11// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12// See the GNU General Public License for more details.
13//
14// Alternatively, you can license this software under a commercial
15// license, as set out in <http://cesanta.com/products.html>.
16//
17// $Date$
18
19#ifndef SSL_WRAPPER_HEADER_INCLUDED
20#define SSL_WRAPPER_HEADER_INCLUDED
21
22#ifdef __cplusplus
23extern "C" {
24#endif // __cplusplus
25
26void *ssl_wrapper_init(const char *listen_addr, const char *target_addr,
27                       const char **err_msg);
28void ssl_wrapper_serve(void *, volatile int *stop_marker);
29
30#ifdef __cplusplus
31}
32#endif // __cplusplus
33
34#endif // SSL_WRAPPER_HEADER_INCLUDED
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/ws_ssl.c
r0r242829
1// Copyright (c) 2014 Cesanta Software
2// All rights reserved
3//
4// This example demostrates proxying of WebSocket traffic, regardless of the
5// protocol (ws:// or wss://).
6// To use this example:
7//    1. configure your browser to use a proxy on port 2014
8//    2. import certs/ws1_ca.pem and certs/ws2_ca.pem into the trusted
9//       certificates list on your browser
10//    3. make && ./ws_ssl
11//    4. Point your browser to http://ws_ssl.com
12//  A page with 4 sections should appear, showing websocket echoes
13
14#include "net_skeleton.h"
15#include "mongoose.h"
16#include "ssl_wrapper.h"
17
18#define S1_PEM  "certs/ws1_server.pem"
19#define C1_PEM  "certs/ws1_client.pem"
20#define CA1_PEM "certs/ws1_ca.pem"
21#define S2_PEM  "certs/ws2_server.pem"
22#define C2_PEM  "certs/ws2_client.pem"
23#define CA2_PEM "certs/ws2_ca.pem"
24
25struct config {
26  const char *uri;
27  const char *wrapper_server_addr;
28  const char *wrapper_client_addr;
29  const char *target_addr;
30};
31
32static struct config s_wrappers[] = {
33  {
34    "ws1:80",
35    "tcp://127.0.0.1:7001",
36    "tcp://127.0.0.1:7001",
37    "tcp://127.0.0.1:9001"
38  },
39  {
40    "ws1:443",
41    "ssl://127.0.0.1:7002:" S1_PEM,
42    "tcp://127.0.0.1:7002",
43    "tcp://127.0.0.1:9001"
44  },
45  {
46    "ws2:80",
47    "tcp://127.0.0.1:7003",
48    "tcp://127.0.0.1:7003",
49    "ssl://127.0.0.1:9002:" C2_PEM ":" CA2_PEM
50  },
51  {
52    "ws2:443",
53    "ssl://127.0.0.1:7004:" S2_PEM,
54    "tcp://127.0.0.1:7004",
55    "ssl://127.0.0.1:9002:" C2_PEM ":" CA2_PEM
56  },
57};
58
59static int s_received_signal = 0;
60
61static void signal_handler(int sig_num) {
62  signal(sig_num, signal_handler);
63  s_received_signal = sig_num;
64}
65
66static int ev_handler(struct mg_connection *conn, enum mg_event ev) {
67  int i;
68
69  switch (ev) {
70    case MG_AUTH:
71      return MG_TRUE;
72
73    case MG_REQUEST:
74      printf("==> [%s] [%s]\n", conn->request_method, conn->uri);
75
76      if (strcmp(conn->request_method, "CONNECT") == 0) {
77        // Iterate over configured wrappers, see if we can use one of them
78        for (i = 0; i < (int) ARRAY_SIZE(s_wrappers); i++) {
79          if (strcmp(conn->uri, s_wrappers[i].uri) == 0) {
80            mg_forward(conn, s_wrappers[i].wrapper_client_addr);
81            return MG_MORE;
82          }
83        }
84
85        // No suitable wrappers found. Disallow that CONNECT request.
86        mg_send_status(conn, 405);
87        return MG_TRUE;
88      }
89
90      // Not a CONNECT request, serve HTML file.
91      mg_send_file(conn, "ws_ssl.html", NULL);
92      return MG_MORE;
93
94    default:
95      return MG_FALSE;
96  }
97}
98
99static int ws_handler(struct mg_connection *conn, enum mg_event ev) {
100  switch (ev) {
101    case MG_AUTH:
102      return MG_TRUE;
103    case MG_REQUEST:
104      if (conn->is_websocket) {
105        // Simple websocket echo server
106        mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT,
107                           conn->content, conn->content_len);
108      } else {
109        mg_printf_data(conn, "%s", "websocket connection expected");
110      }
111      return MG_TRUE;
112    default:
113      return MG_FALSE;
114  }
115}
116
117static void *serve_thread_func(void *param) {
118  struct mg_server *server = (struct mg_server *) param;
119  printf("Listening on port %s\n", mg_get_option(server, "listening_port"));
120  while (s_received_signal == 0) {
121    mg_poll_server(server, 1000);
122  }
123  mg_destroy_server(&server);
124  return NULL;
125}
126
127static void *wrapper_thread_func(void *param) {
128  struct config *c = (struct config *) param;
129  const char *err_msg;
130  void *wrapper;
131
132  wrapper = ssl_wrapper_init(c->wrapper_server_addr, c->target_addr, &err_msg);
133  if (wrapper == NULL) {
134    fprintf(stderr, "Error: %s\n", err_msg);
135    exit(EXIT_FAILURE);
136  }
137  //((struct ns_mgr *) wrapper)->hexdump_file = "/dev/stderr";
138  ssl_wrapper_serve(wrapper, &s_received_signal);
139
140  return NULL;
141}
142
143int main(void) {
144  struct mg_server *proxy_server = mg_create_server(NULL, ev_handler);
145  struct mg_server *ws1_server = mg_create_server(NULL, ws_handler);
146  struct mg_server *ws2_server = mg_create_server(NULL, ws_handler);
147  size_t i;
148
149  ((struct ns_mgr *) proxy_server)->hexdump_file = "/dev/stderr";
150
151  // Configure proxy server to listen on port 2014
152  mg_set_option(proxy_server, "listening_port", "2014");
153  //mg_set_option(proxy_server, "enable_proxy", "yes");
154
155  // Configure two websocket echo servers:
156  //    ws1 is WS, listening on 9001
157  //    ws2 is WSS, listening on 9002
158  // Note that HTML page thinks that ws1 is WSS, and ws2 is WS,
159  // where in reality it is vice versa and proxy server makes the decision.
160  mg_set_option(ws1_server, "listening_port", "tcp://127.0.0.1:9001");
161  mg_set_option(ws2_server, "listening_port",
162                "ssl://127.0.0.1:9002:" S2_PEM ":" CA2_PEM);
163
164  // Setup signal handlers
165  signal(SIGTERM, signal_handler);
166  signal(SIGINT, signal_handler);
167
168  // Start SSL wrappers, each in it's own thread
169  for (i = 0; i < ARRAY_SIZE(s_wrappers); i++) {
170    ns_start_thread(wrapper_thread_func, &s_wrappers[i]);
171  }
172
173  // Start websocket servers in separate threads
174  mg_start_thread(serve_thread_func, ws1_server);
175  mg_start_thread(serve_thread_func, ws2_server);
176
177  // Finally, start proxy server in this thread: this call blocks
178  serve_thread_func(proxy_server);
179
180  printf("Existing on signal %d\n", s_received_signal);
181  return EXIT_SUCCESS;
182}
trunk/3rdparty/mongoose/examples/websocket_ssl_proxy/ws_ssl.html
r0r242829
1<html>
2<head>
3  <title>Websocket Proxy SSL Test</title>
4  <meta charset="utf-8">
5  <script>
6  window.onload = function() {
7    var protocols = ['ws://', 'wss://'];
8    var websocketServers = ['ws1', 'ws2'];
9    //protocols = ['wss://'];
10    //websocketServers = ['ws1']
11
12    var createWebsocketConnection = function(proto, server) {
13      var conn = new WebSocket(proto + server);
14
15      var div = document.createElement('div');
16      var h2 = document.createElement('h2');
17      h2.innerHTML = 'Connection to ' + proto + server;
18      document.body.appendChild(h2);
19      document.body.appendChild(div);
20
21      conn.onmessage = function(ev) {
22        var el = document.createElement('div');
23        el.innerHTML = 'websocket message: ' + ev.data;
24        div.appendChild(el);
25        // Keep only last 5 messages in the list
26        while (div.childNodes.length > 5) div.removeChild(div.firstChild);
27      };
28
29      // Send some string to the websocket connection periodically.
30      // websocket server much echo it back.
31      window.setInterval(function() { conn.send(Math.random()); }, 1000);
32    };
33
34    for (var i = 0; i < protocols.length; i++) {
35      for (var j = 0; j < websocketServers.length; j++) {
36        createWebsocketConnection(protocols[i], websocketServers[j]);
37      }
38    }
39  };
40  </script>
41  <style>
42    body > div {
43      border: 1px solid #ccc; background: #f0f0f0; padding: 0 1em;
44      margin: 0 2em; min-height: 4em; max-width: 40em;
45    }
46  </style>
47</head>
48<body>
49</body>
50</html>
No newline at end of file
trunk/3rdparty/mongoose/jni/Android.mk
r0r242829
1LOCAL_PATH := $(call my-dir)/..
2include $(CLEAR_VARS)
3
4LOCAL_CFLAGS    := -std=c99 -O2 -W -Wall -pthread -pipe $(COPT)
5LOCAL_MODULE    := mongoose
6LOCAL_SRC_FILES := examples/web_server/web_server.c mongoose.c
7
8include $(BUILD_EXECUTABLE)
trunk/3rdparty/mongoose/mongoose.c
r0r242829
1// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
2// Copyright (c) 2013-2014 Cesanta Software Limited
3// All rights reserved
4//
5// This library is dual-licensed: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License version 2 as
7// published by the Free Software Foundation. For the terms of this
8// license, see <http://www.gnu.org/licenses/>.
9//
10// You are free to use this library under the terms of the GNU General
11// Public License, but WITHOUT ANY WARRANTY; without even the implied
12// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13// See the GNU General Public License for more details.
14//
15// Alternatively, you can license this library under a commercial
16// license, as set out in <http://cesanta.com/>.
17
18#ifdef NOEMBED_NET_SKELETON
19#include "net_skeleton.h"
20#else
21// net_skeleton start
22// Copyright (c) 2014 Cesanta Software Limited
23// All rights reserved
24//
25// This software is dual-licensed: you can redistribute it and/or modify
26// it under the terms of the GNU General Public License version 2 as
27// published by the Free Software Foundation. For the terms of this
28// license, see <http://www.gnu.org/licenses/>.
29//
30// You are free to use this software under the terms of the GNU General
31// Public License, but WITHOUT ANY WARRANTY; without even the implied
32// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
33// See the GNU General Public License for more details.
34//
35// Alternatively, you can license this software under a commercial
36// license, as set out in <http://cesanta.com/>.
37//
38// $Date: 2014-09-28 05:04:41 UTC $
39
40#ifndef NS_SKELETON_HEADER_INCLUDED
41#define NS_SKELETON_HEADER_INCLUDED
42
43#define NS_SKELETON_VERSION "2.1.0"
44
45#undef UNICODE                  // Use ANSI WinAPI functions
46#undef _UNICODE                 // Use multibyte encoding on Windows
47#define _MBCS                   // Use multibyte encoding on Windows
48#define _INTEGRAL_MAX_BITS 64   // Enable _stati64() on Windows
49#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005+
50#undef WIN32_LEAN_AND_MEAN      // Let windows.h always include winsock2.h
51#define _XOPEN_SOURCE 600       // For flockfile() on Linux
52#define __STDC_FORMAT_MACROS    // <inttypes.h> wants this for C++
53#define __STDC_LIMIT_MACROS     // C++ wants that for INT64_MAX
54#ifndef _LARGEFILE_SOURCE
55#define _LARGEFILE_SOURCE       // Enable fseeko() and ftello() functions
56#endif
57#define _FILE_OFFSET_BITS 64    // Enable 64-bit file offsets
58
59#ifdef _MSC_VER
60#pragma warning (disable : 4127)  // FD_SET() emits warning, disable it
61#pragma warning (disable : 4204)  // missing c99 support
62#endif
63
64#include <sys/types.h>
65#include <sys/stat.h>
66#include <assert.h>
67#include <ctype.h>
68#include <errno.h>
69#include <fcntl.h>
70#include <stdarg.h>
71#include <stddef.h>
72#include <stdio.h>
73#include <stdlib.h>
74#include <string.h>
75#include <time.h>
76#include <signal.h>
77
78#ifdef _WIN32
79#ifdef _MSC_VER
80#pragma comment(lib, "ws2_32.lib")    // Linking with winsock library
81#endif
82#include <windows.h>
83#include <process.h>
84#ifndef EINPROGRESS
85#define EINPROGRESS WSAEINPROGRESS
86#endif
87#ifndef EWOULDBLOCK
88#define EWOULDBLOCK WSAEWOULDBLOCK
89#endif
90#ifndef __func__
91#define STRX(x) #x
92#define STR(x) STRX(x)
93#define __func__ __FILE__ ":" STR(__LINE__)
94#endif
95#ifndef va_copy
96#define va_copy(x,y) x = y
97#endif // MINGW #defines va_copy
98#define snprintf _snprintf
99#define vsnprintf _vsnprintf
100#define sleep(x) Sleep((x) * 1000)
101#define to64(x) _atoi64(x)
102typedef int socklen_t;
103typedef unsigned char uint8_t;
104typedef unsigned int uint32_t;
105typedef unsigned short uint16_t;
106typedef unsigned __int64 uint64_t;
107typedef __int64   int64_t;
108typedef SOCKET sock_t;
109typedef struct _stati64 ns_stat_t;
110#ifndef S_ISDIR
111#define S_ISDIR(x) ((x) & _S_IFDIR)
112#endif
113#else
114#include <errno.h>
115#include <fcntl.h>
116#include <netdb.h>
117#include <pthread.h>
118#include <stdarg.h>
119#include <unistd.h>
120#include <arpa/inet.h>  // For inet_pton() when NS_ENABLE_IPV6 is defined
121#include <netinet/in.h>
122#include <sys/socket.h>
123#include <sys/select.h>
124#define closesocket(x) close(x)
125#define __cdecl
126#define INVALID_SOCKET (-1)
127#define to64(x) strtoll(x, NULL, 10)
128typedef int sock_t;
129typedef struct stat ns_stat_t;
130#endif
131
132#ifdef NS_ENABLE_DEBUG
133#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \
134  fflush(stdout); } while(0)
135#else
136#define DBG(x)
137#endif
138
139#ifndef ARRAY_SIZE
140#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
141#endif
142
143#ifdef NS_ENABLE_SSL
144#ifdef __APPLE__
145#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
146#endif
147#include <openssl/ssl.h>
148#else
149typedef void *SSL;
150typedef void *SSL_CTX;
151#endif
152
153#ifdef __cplusplus
154extern "C" {
155#endif // __cplusplus
156
157union socket_address {
158  struct sockaddr sa;
159  struct sockaddr_in sin;
160#ifdef NS_ENABLE_IPV6
161  struct sockaddr_in6 sin6;
162#else
163  struct sockaddr sin6;
164#endif
165};
166
167// Describes chunk of memory
168struct ns_str {
169  const char *p;
170  size_t len;
171};
172
173// IO buffers interface
174struct iobuf {
175  char *buf;
176  size_t len;
177  size_t size;
178};
179
180void iobuf_init(struct iobuf *, size_t initial_size);
181void iobuf_free(struct iobuf *);
182size_t iobuf_append(struct iobuf *, const void *data, size_t data_size);
183void iobuf_remove(struct iobuf *, size_t data_size);
184void iobuf_resize(struct iobuf *, size_t new_size);
185
186// Callback function (event handler) prototype, must be defined by user.
187// Net skeleton will call event handler, passing events defined above.
188struct ns_connection;
189typedef void (*ns_callback_t)(struct ns_connection *, int event_num, void *evp);
190
191// Events. Meaning of event parameter (evp) is given in the comment.
192#define NS_POLL    0  // Sent to each connection on each call to ns_mgr_poll()
193#define NS_ACCEPT  1  // New connection accept()-ed. union socket_address *addr
194#define NS_CONNECT 2  // connect() succeeded or failed. int *success_status
195#define NS_RECV    3  // Data has benn received. int *num_bytes
196#define NS_SEND    4  // Data has been written to a socket. int *num_bytes
197#define NS_CLOSE   5  // Connection is closed. NULL
198
199
200struct ns_mgr {
201  struct ns_connection *active_connections;
202  const char *hexdump_file;         // Debug hexdump file path
203  sock_t ctl[2];                    // Socketpair for mg_wakeup()
204  void *user_data;                  // User data
205};
206
207
208struct ns_connection {
209  struct ns_connection *next, *prev;  // ns_mgr::active_connections linkage
210  struct ns_connection *listener;     // Set only for accept()-ed connections
211  struct ns_mgr *mgr;
212
213  sock_t sock;                // Socket
214  union socket_address sa;    // Peer address
215  struct iobuf recv_iobuf;    // Received data
216  struct iobuf send_iobuf;    // Data scheduled for sending
217  SSL *ssl;
218  SSL_CTX *ssl_ctx;
219  void *user_data;            // User-specific data
220  void *proto_data;           // Application protocol-specific data
221  time_t last_io_time;        // Timestamp of the last socket IO
222  ns_callback_t callback;     // Event handler function
223
224  unsigned int flags;
225#define NSF_FINISHED_SENDING_DATA   (1 << 0)
226#define NSF_BUFFER_BUT_DONT_SEND    (1 << 1)
227#define NSF_SSL_HANDSHAKE_DONE      (1 << 2)
228#define NSF_CONNECTING              (1 << 3)
229#define NSF_CLOSE_IMMEDIATELY       (1 << 4)
230#define NSF_WANT_READ               (1 << 5)
231#define NSF_WANT_WRITE              (1 << 6)
232#define NSF_LISTENING               (1 << 7)
233#define NSF_UDP                     (1 << 8)
234
235#define NSF_USER_1                  (1 << 20)
236#define NSF_USER_2                  (1 << 21)
237#define NSF_USER_3                  (1 << 22)
238#define NSF_USER_4                  (1 << 23)
239#define NSF_USER_5                  (1 << 24)
240#define NSF_USER_6                  (1 << 25)
241};
242
243void ns_mgr_init(struct ns_mgr *, void *user_data);
244void ns_mgr_free(struct ns_mgr *);
245time_t ns_mgr_poll(struct ns_mgr *, int milli);
246void ns_broadcast(struct ns_mgr *, ns_callback_t, void *, size_t);
247
248struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *);
249struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t,
250                                  ns_callback_t, void *);
251struct ns_connection *ns_bind(struct ns_mgr *, const char *,
252                              ns_callback_t, void *);
253struct ns_connection *ns_connect(struct ns_mgr *, const char *,
254                                 ns_callback_t, void *);
255
256int ns_send(struct ns_connection *, const void *buf, int len);
257int ns_printf(struct ns_connection *, const char *fmt, ...);
258int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap);
259
260// Utility functions
261void *ns_start_thread(void *(*f)(void *), void *p);
262int ns_socketpair(sock_t [2]);
263int ns_socketpair2(sock_t [2], int sock_type);  // SOCK_STREAM or SOCK_DGRAM
264void ns_set_close_on_exec(sock_t);
265void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
266int ns_hexdump(const void *buf, int len, char *dst, int dst_len);
267int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
268int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len);
269
270#ifdef __cplusplus
271}
272#endif // __cplusplus
273
274#endif // NS_SKELETON_HEADER_INCLUDED
275// Copyright (c) 2014 Cesanta Software Limited
276// All rights reserved
277//
278// This software is dual-licensed: you can redistribute it and/or modify
279// it under the terms of the GNU General Public License version 2 as
280// published by the Free Software Foundation. For the terms of this
281// license, see <http://www.gnu.org/licenses/>.
282//
283// You are free to use this software under the terms of the GNU General
284// Public License, but WITHOUT ANY WARRANTY; without even the implied
285// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
286// See the GNU General Public License for more details.
287//
288// Alternatively, you can license this software under a commercial
289// license, as set out in <http://cesanta.com/>.
290//
291// $Date: 2014-09-28 05:04:41 UTC $
292
293
294#ifndef NS_MALLOC
295#define NS_MALLOC malloc
296#endif
297
298#ifndef NS_REALLOC
299#define NS_REALLOC realloc
300#endif
301
302#ifndef NS_FREE
303#define NS_FREE free
304#endif
305
306#ifndef NS_CALLOC
307#define NS_CALLOC calloc
308#endif
309
310#define NS_CTL_MSG_MESSAGE_SIZE     (8 * 1024)
311#define NS_READ_BUFFER_SIZE         2048
312#define NS_UDP_RECEIVE_BUFFER_SIZE  2000
313#define NS_VPRINTF_BUFFER_SIZE      500
314
315struct ctl_msg {
316  ns_callback_t callback;
317  char message[NS_CTL_MSG_MESSAGE_SIZE];
318};
319
320void iobuf_resize(struct iobuf *io, size_t new_size) {
321  char *p;
322  if ((new_size > io->size || (new_size < io->size && new_size >= io->len)) &&
323      (p = (char *) NS_REALLOC(io->buf, new_size)) != NULL) {
324    io->size = new_size;
325    io->buf = p;
326  }
327}
328
329void iobuf_init(struct iobuf *iobuf, size_t initial_size) {
330  iobuf->len = iobuf->size = 0;
331  iobuf->buf = NULL;
332  iobuf_resize(iobuf, initial_size);
333}
334
335void iobuf_free(struct iobuf *iobuf) {
336  if (iobuf != NULL) {
337    if (iobuf->buf != NULL) NS_FREE(iobuf->buf);
338    iobuf_init(iobuf, 0);
339  }
340}
341
342size_t iobuf_append(struct iobuf *io, const void *buf, size_t len) {
343  char *p = NULL;
344
345  assert(io != NULL);
346  assert(io->len <= io->size);
347
348  if (len <= 0) {
349  } else if (io->len + len <= io->size) {
350    memcpy(io->buf + io->len, buf, len);
351    io->len += len;
352  } else if ((p = (char *) NS_REALLOC(io->buf, io->len + len)) != NULL) {
353    io->buf = p;
354    memcpy(io->buf + io->len, buf, len);
355    io->len += len;
356    io->size = io->len;
357  } else {
358    len = 0;
359  }
360
361  return len;
362}
363
364void iobuf_remove(struct iobuf *io, size_t n) {
365  if (n > 0 && n <= io->len) {
366    memmove(io->buf, io->buf + n, io->len - n);
367    io->len -= n;
368  }
369}
370
371static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) {
372  if (nc->flags & NSF_UDP) {
373    long n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin));
374    DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno)));
375    return n < 0 ? 0 : n;
376  } else {
377    return iobuf_append(&nc->send_iobuf, buf, len);
378  }
379}
380
381#ifndef NS_DISABLE_THREADS
382void *ns_start_thread(void *(*f)(void *), void *p) {
383#ifdef _WIN32
384  return (void *) _beginthread((void (__cdecl *)(void *)) f, 0, p);
385#else
386  pthread_t thread_id = (pthread_t) 0;
387  pthread_attr_t attr;
388
389  (void) pthread_attr_init(&attr);
390  (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
391
392#if defined(NS_STACK_SIZE) && NS_STACK_SIZE > 1
393  (void) pthread_attr_setstacksize(&attr, NS_STACK_SIZE);
394#endif
395
396  pthread_create(&thread_id, &attr, f, p);
397  pthread_attr_destroy(&attr);
398
399  return (void *) thread_id;
400#endif
401}
402#endif  // NS_DISABLE_THREADS
403
404static void ns_add_conn(struct ns_mgr *mgr, struct ns_connection *c) {
405  c->next = mgr->active_connections;
406  mgr->active_connections = c;
407  c->prev = NULL;
408  if (c->next != NULL) c->next->prev = c;
409}
410
411static void ns_remove_conn(struct ns_connection *conn) {
412  if (conn->prev == NULL) conn->mgr->active_connections = conn->next;
413  if (conn->prev) conn->prev->next = conn->next;
414  if (conn->next) conn->next->prev = conn->prev;
415}
416
417// Print message to buffer. If buffer is large enough to hold the message,
418// return buffer. If buffer is to small, allocate large enough buffer on heap,
419// and return allocated buffer.
420int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
421  va_list ap_copy;
422  int len;
423
424  va_copy(ap_copy, ap);
425  len = vsnprintf(*buf, size, fmt, ap_copy);
426  va_end(ap_copy);
427
428  if (len < 0) {
429    // eCos and Windows are not standard-compliant and return -1 when
430    // the buffer is too small. Keep allocating larger buffers until we
431    // succeed or out of memory.
432    *buf = NULL;
433    while (len < 0) {
434      if (*buf) NS_FREE(*buf);
435      size *= 2;
436      if ((*buf = (char *) NS_MALLOC(size)) == NULL) break;
437      va_copy(ap_copy, ap);
438      len = vsnprintf(*buf, size, fmt, ap_copy);
439      va_end(ap_copy);
440    }
441  } else if (len > (int) size) {
442    // Standard-compliant code path. Allocate a buffer that is large enough.
443    if ((*buf = (char *) NS_MALLOC(len + 1)) == NULL) {
444      len = -1;
445    } else {
446      va_copy(ap_copy, ap);
447      len = vsnprintf(*buf, len + 1, fmt, ap_copy);
448      va_end(ap_copy);
449    }
450  }
451
452  return len;
453}
454
455int ns_vprintf(struct ns_connection *nc, const char *fmt, va_list ap) {
456  char mem[NS_VPRINTF_BUFFER_SIZE], *buf = mem;
457  int len;
458
459  if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
460    ns_out(nc, buf, len);
461  }
462  if (buf != mem && buf != NULL) {
463    NS_FREE(buf);
464  }
465
466  return len;
467}
468
469int ns_printf(struct ns_connection *conn, const char *fmt, ...) {
470  int len;
471  va_list ap;
472  va_start(ap, fmt);
473  len = ns_vprintf(conn, fmt, ap);
474  va_end(ap);
475  return len;
476}
477
478static void hexdump(struct ns_connection *nc, const char *path,
479                    int num_bytes, int ev) {
480  const struct iobuf *io = ev == NS_SEND ? &nc->send_iobuf : &nc->recv_iobuf;
481  FILE *fp;
482  char *buf, src[60], dst[60];
483  int buf_size = num_bytes * 5 + 100;
484
485  if ((fp = fopen(path, "a")) != NULL) {
486    ns_sock_to_str(nc->sock, src, sizeof(src), 3);
487    ns_sock_to_str(nc->sock, dst, sizeof(dst), 7);
488    fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) time(NULL),
489            nc->user_data, src,
490            ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" :
491            ev == NS_ACCEPT ? "<A" : ev == NS_CONNECT ? "C>" : "XX",
492            dst, num_bytes);
493    if (num_bytes > 0 && (buf = (char *) NS_MALLOC(buf_size)) != NULL) {
494      ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) -
495        (ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size);
496      fprintf(fp, "%s", buf);
497      NS_FREE(buf);
498    }
499    fclose(fp);
500  }
501}
502
503static void ns_call(struct ns_connection *nc, int ev, void *p) {
504  if (nc->mgr->hexdump_file != NULL && ev != NS_POLL) {
505    int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) p : 0;
506    hexdump(nc, nc->mgr->hexdump_file, len, ev);
507  }
508
509  nc->callback(nc, ev, p);
510}
511
512static void ns_destroy_conn(struct ns_connection *conn) {
513  closesocket(conn->sock);
514  iobuf_free(&conn->recv_iobuf);
515  iobuf_free(&conn->send_iobuf);
516#ifdef NS_ENABLE_SSL
517  if (conn->ssl != NULL) {
518    SSL_free(conn->ssl);
519  }
520  if (conn->ssl_ctx != NULL) {
521    SSL_CTX_free(conn->ssl_ctx);
522  }
523#endif
524  NS_FREE(conn);
525}
526
527static void ns_close_conn(struct ns_connection *conn) {
528  DBG(("%p %d", conn, conn->flags));
529  ns_call(conn, NS_CLOSE, NULL);
530  ns_remove_conn(conn);
531  ns_destroy_conn(conn);
532}
533
534void ns_set_close_on_exec(sock_t sock) {
535#ifdef _WIN32
536  (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
537#else
538  fcntl(sock, F_SETFD, FD_CLOEXEC);
539#endif
540}
541
542static void ns_set_non_blocking_mode(sock_t sock) {
543#ifdef _WIN32
544  unsigned long on = 1;
545  ioctlsocket(sock, FIONBIO, &on);
546#else
547  int flags = fcntl(sock, F_GETFL, 0);
548  fcntl(sock, F_SETFL, flags | O_NONBLOCK);
549#endif
550}
551
552#ifndef NS_DISABLE_SOCKETPAIR
553int ns_socketpair2(sock_t sp[2], int sock_type) {
554  union socket_address sa;
555  sock_t sock;
556  socklen_t len = sizeof(sa.sin);
557  int ret = 0;
558
559  sp[0] = sp[1] = INVALID_SOCKET;
560
561  (void) memset(&sa, 0, sizeof(sa));
562  sa.sin.sin_family = AF_INET;
563  sa.sin.sin_port = htons(0);
564  sa.sin.sin_addr.s_addr = htonl(0x7f000001);
565
566  if ((sock = socket(AF_INET, sock_type, 0)) != INVALID_SOCKET &&
567      !bind(sock, &sa.sa, len) &&
568      (sock_type == SOCK_DGRAM || !listen(sock, 1)) &&
569      !getsockname(sock, &sa.sa, &len) &&
570      (sp[0] = socket(AF_INET, sock_type, 0)) != INVALID_SOCKET &&
571      !connect(sp[0], &sa.sa, len) &&
572      (sock_type == SOCK_STREAM ||
573       (!getsockname(sp[0], &sa.sa, &len) && !connect(sock, &sa.sa, len))) &&
574      (sp[1] = (sock_type == SOCK_DGRAM ? sock :
575                accept(sock, &sa.sa, &len))) != INVALID_SOCKET) {
576    ns_set_close_on_exec(sp[0]);
577    ns_set_close_on_exec(sp[1]);
578    ret = 1;
579  } else {
580    if (sp[0] != INVALID_SOCKET) closesocket(sp[0]);
581    if (sp[1] != INVALID_SOCKET) closesocket(sp[1]);
582    sp[0] = sp[1] = INVALID_SOCKET;
583  }
584  if (sock_type != SOCK_DGRAM) closesocket(sock);
585
586  return ret;
587}
588
589int ns_socketpair(sock_t sp[2]) {
590  return ns_socketpair2(sp, SOCK_STREAM);
591}
592#endif  // NS_DISABLE_SOCKETPAIR
593
594// TODO(lsm): use non-blocking resolver
595static int ns_resolve2(const char *host, struct in_addr *ina) {
596  struct hostent *he;
597  if ((he = gethostbyname(host)) == NULL) {
598    DBG(("gethostbyname(%s) failed: %s", host, strerror(errno)));
599  } else {
600    memcpy(ina, he->h_addr_list[0], sizeof(*ina));
601    return 1;
602  }
603  return 0;
604}
605
606// Resolve FDQN "host", store IP address in the "ip".
607// Return > 0 (IP address length) on success.
608int ns_resolve(const char *host, char *buf, size_t n) {
609  struct in_addr ad;
610  return ns_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0;
611}
612
613// Address format: [PROTO://][IP_ADDRESS:]PORT[:CERT][:CA_CERT]
614static int ns_parse_address(const char *str, union socket_address *sa,
615                            int *proto, int *use_ssl, char *cert, char *ca) {
616  unsigned int a, b, c, d, port;
617  int n = 0, len = 0;
618  char host[200];
619#ifdef NS_ENABLE_IPV6
620  char buf[100];
621#endif
622
623  // MacOS needs that. If we do not zero it, subsequent bind() will fail.
624  // Also, all-zeroes in the socket address means binding to all addresses
625  // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
626  memset(sa, 0, sizeof(*sa));
627  sa->sin.sin_family = AF_INET;
628
629  *proto = SOCK_STREAM;
630  *use_ssl = 0;
631  cert[0] = ca[0] = '\0';
632
633  if (memcmp(str, "ssl://", 6) == 0) {
634    str += 6;
635    *use_ssl = 1;
636  } else if (memcmp(str, "udp://", 6) == 0) {
637    str += 6;
638    *proto = SOCK_DGRAM;
639  } else if (memcmp(str, "tcp://", 6) == 0) {
640    str += 6;
641  }
642
643  if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
644    // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
645    sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
646    sa->sin.sin_port = htons((uint16_t) port);
647#ifdef NS_ENABLE_IPV6
648  } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 &&
649             inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
650    // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
651    sa->sin6.sin6_family = AF_INET6;
652    sa->sin6.sin6_port = htons((uint16_t) port);
653#endif
654  } else if (sscanf(str, "%199[^ :]:%u%n", host, &port, &len) == 2) {
655    sa->sin.sin_port = htons((uint16_t) port);
656    ns_resolve2(host, &sa->sin.sin_addr);
657  } else if (sscanf(str, "%u%n", &port, &len) == 1) {
658    // If only port is specified, bind to IPv4, INADDR_ANY
659    sa->sin.sin_port = htons((uint16_t) port);
660  }
661
662  if (*use_ssl && (sscanf(str + len, ":%99[^:]:%99[^:]%n", cert, ca, &n) == 2 ||
663                   sscanf(str + len, ":%99[^:]%n", cert, &n) == 1)) {
664    len += n;
665  }
666
667  return port < 0xffff && str[len] == '\0' ? len : 0;
668}
669
670// 'sa' must be an initialized address to bind to
671static sock_t ns_open_listening_socket(union socket_address *sa, int proto) {
672  socklen_t sa_len = (sa->sa.sa_family == AF_INET) ?
673    sizeof(sa->sin) : sizeof(sa->sin6);
674  sock_t sock = INVALID_SOCKET;
675#ifndef _WIN32
676  int on = 1;
677#endif
678
679  if ((sock = socket(sa->sa.sa_family, proto, 0)) != INVALID_SOCKET &&
680#ifndef _WIN32
681      // SO_RESUSEADDR is not enabled on Windows because the semantics of
682      // SO_REUSEADDR on UNIX and Windows is different. On Windows,
683      // SO_REUSEADDR allows to bind a socket to a port without error even if
684      // the port is already open by another program. This is not the behavior
685      // SO_REUSEADDR was designed for, and leads to hard-to-track failure
686      // scenarios. Therefore, SO_REUSEADDR was disabled on Windows.
687      !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) &&
688#endif
689      !bind(sock, &sa->sa, sa_len) &&
690      (proto == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) {
691    ns_set_non_blocking_mode(sock);
692    // In case port was set to 0, get the real port number
693    (void) getsockname(sock, &sa->sa, &sa_len);
694  } else if (sock != INVALID_SOCKET) {
695    closesocket(sock);
696    sock = INVALID_SOCKET;
697  }
698
699  return sock;
700}
701
702#ifdef NS_ENABLE_SSL
703// Certificate generation script is at
704// https://github.com/cesanta/net_skeleton/blob/master/scripts/gen_certs.sh
705
706static int ns_use_ca_cert(SSL_CTX *ctx, const char *cert) {
707  if (ctx == NULL) {
708    return -1;
709  } else if (cert == NULL || cert[0] == '\0') {
710    return 0;
711  }
712  SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
713  return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? 0 : -2;
714}
715
716static int ns_use_cert(SSL_CTX *ctx, const char *pem_file) {
717  if (ctx == NULL) {
718    return -1;
719  } else if (pem_file == NULL || pem_file[0] == '\0') {
720    return 0;
721  } else if (SSL_CTX_use_certificate_file(ctx, pem_file, 1) == 0 ||
722             SSL_CTX_use_PrivateKey_file(ctx, pem_file, 1) == 0) {
723    return -2;
724  } else {
725    SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
726    SSL_CTX_use_certificate_chain_file(ctx, pem_file);
727    return 0;
728  }
729}
730#endif  // NS_ENABLE_SSL
731
732struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str,
733                              ns_callback_t callback, void *user_data) {
734  union socket_address sa;
735  struct ns_connection *nc = NULL;
736  int use_ssl, proto;
737  char cert[100], ca_cert[100];
738  sock_t sock;
739
740  ns_parse_address(str, &sa, &proto, &use_ssl, cert, ca_cert);
741  if (use_ssl && cert[0] == '\0') return NULL;
742
743  if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) {
744  } else if ((nc = ns_add_sock(srv, sock, callback, NULL)) == NULL) {
745    closesocket(sock);
746  } else {
747    nc->sa = sa;
748    nc->flags |= NSF_LISTENING;
749    nc->user_data = user_data;
750    nc->callback = callback;
751
752    if (proto == SOCK_DGRAM) {
753      nc->flags |= NSF_UDP;
754    }
755
756#ifdef NS_ENABLE_SSL
757    if (use_ssl) {
758      nc->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
759      if (ns_use_cert(nc->ssl_ctx, cert) != 0 ||
760          ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0) {
761        ns_close_conn(nc);
762        nc = NULL;
763      }
764    }
765#endif
766
767    DBG(("%p sock %d/%d ssl %p %p", nc, sock, proto, nc->ssl_ctx, nc->ssl));
768  }
769
770  return nc;
771}
772
773static struct ns_connection *accept_conn(struct ns_connection *ls) {
774  struct ns_connection *c = NULL;
775  union socket_address sa;
776  socklen_t len = sizeof(sa);
777  sock_t sock = INVALID_SOCKET;
778
779  // NOTE(lsm): on Windows, sock is always > FD_SETSIZE
780  if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) {
781  } else if ((c = ns_add_sock(ls->mgr, sock, ls->callback,
782              ls->user_data)) == NULL) {
783    closesocket(sock);
784#ifdef NS_ENABLE_SSL
785  } else if (ls->ssl_ctx != NULL &&
786             ((c->ssl = SSL_new(ls->ssl_ctx)) == NULL ||
787              SSL_set_fd(c->ssl, sock) != 1)) {
788    DBG(("SSL error"));
789    ns_close_conn(c);
790    c = NULL;
791#endif
792  } else {
793    c->listener = ls;
794    c->proto_data = ls->proto_data;
795    ns_call(c, NS_ACCEPT, &sa);
796    DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl));
797  }
798
799  return c;
800}
801
802static int ns_is_error(int n) {
803  return n == 0 ||
804    (n < 0 && errno != EINTR && errno != EINPROGRESS &&
805     errno != EAGAIN && errno != EWOULDBLOCK
806#ifdef _WIN32
807     && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK
808#endif
809    );
810}
811
812void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) {
813  union socket_address sa;
814  socklen_t slen = sizeof(sa);
815
816  if (buf != NULL && len > 0) {
817    buf[0] = '\0';
818    memset(&sa, 0, sizeof(sa));
819    if (flags & 4) {
820      getpeername(sock, &sa.sa, &slen);
821    } else {
822      getsockname(sock, &sa.sa, &slen);
823    }
824    if (flags & 1) {
825#if defined(NS_ENABLE_IPV6)
826      inet_ntop(sa.sa.sa_family, sa.sa.sa_family == AF_INET ?
827                (void *) &sa.sin.sin_addr :
828                (void *) &sa.sin6.sin6_addr, buf, len);
829#elif defined(_WIN32)
830      // Only Windoze Vista (and newer) have inet_ntop()
831      strncpy(buf, inet_ntoa(sa.sin.sin_addr), len);
832#else
833      inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf,(socklen_t)len);
834#endif
835    }
836    if (flags & 2) {
837      snprintf(buf + strlen(buf), len - (strlen(buf) + 1), "%s%d",
838               flags & 1 ? ":" : "", (int) ntohs(sa.sin.sin_port));
839    }
840  }
841}
842
843int ns_hexdump(const void *buf, int len, char *dst, int dst_len) {
844  const unsigned char *p = (const unsigned char *) buf;
845  char ascii[17] = "";
846  int i, idx, n = 0;
847
848  for (i = 0; i < len; i++) {
849    idx = i % 16;
850    if (idx == 0) {
851      if (i > 0) n += snprintf(dst + n, dst_len - n, "  %s\n", ascii);
852      n += snprintf(dst + n, dst_len - n, "%04x ", i);
853    }
854    n += snprintf(dst + n, dst_len - n, " %02x", p[i]);
855    ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i];
856    ascii[idx + 1] = '\0';
857  }
858
859  while (i++ % 16) n += snprintf(dst + n, dst_len - n, "%s", "   ");
860  n += snprintf(dst + n, dst_len - n, "  %s\n\n", ascii);
861
862  return n;
863}
864
865#ifdef NS_ENABLE_SSL
866static int ns_ssl_err(struct ns_connection *conn, int res) {
867  int ssl_err = SSL_get_error(conn->ssl, res);
868  if (ssl_err == SSL_ERROR_WANT_READ) conn->flags |= NSF_WANT_READ;
869  if (ssl_err == SSL_ERROR_WANT_WRITE) conn->flags |= NSF_WANT_WRITE;
870  return ssl_err;
871}
872#endif
873
874static void ns_read_from_socket(struct ns_connection *conn) {
875  char buf[NS_READ_BUFFER_SIZE];
876  int n = 0;
877
878  if (conn->flags & NSF_CONNECTING) {
879    int ok = 1, ret;
880    socklen_t len = sizeof(ok);
881
882    ret = getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &ok, &len);
883    (void) ret;
884#ifdef NS_ENABLE_SSL
885    if (ret == 0 && ok == 0 && conn->ssl != NULL) {
886      int res = SSL_connect(conn->ssl);
887      int ssl_err = ns_ssl_err(conn, res);
888      if (res == 1) {
889        conn->flags |= NSF_SSL_HANDSHAKE_DONE;
890      } else if (ssl_err == SSL_ERROR_WANT_READ ||
891                 ssl_err == SSL_ERROR_WANT_WRITE) {
892        return; // Call us again
893      } else {
894        ok = 1;
895      }
896    }
897#endif
898    conn->flags &= ~NSF_CONNECTING;
899    DBG(("%p ok=%d", conn, ok));
900    if (ok != 0) {
901      conn->flags |= NSF_CLOSE_IMMEDIATELY;
902    }
903    ns_call(conn, NS_CONNECT, &ok);
904    return;
905  }
906
907#ifdef NS_ENABLE_SSL
908  if (conn->ssl != NULL) {
909    if (conn->flags & NSF_SSL_HANDSHAKE_DONE) {
910      // SSL library may have more bytes ready to read then we ask to read.
911      // Therefore, read in a loop until we read everything. Without the loop,
912      // we skip to the next select() cycle which can just timeout.
913      while ((n = SSL_read(conn->ssl, buf, sizeof(buf))) > 0) {
914        DBG(("%p %d <- %d bytes (SSL)", conn, conn->flags, n));
915        iobuf_append(&conn->recv_iobuf, buf, n);
916        ns_call(conn, NS_RECV, &n);
917      }
918      ns_ssl_err(conn, n);
919    } else {
920      int res = SSL_accept(conn->ssl);
921      int ssl_err = ns_ssl_err(conn, res);
922      if (res == 1) {
923        conn->flags |= NSF_SSL_HANDSHAKE_DONE;
924      } else if (ssl_err == SSL_ERROR_WANT_READ ||
925                 ssl_err == SSL_ERROR_WANT_WRITE) {
926        return; // Call us again
927      } else {
928        conn->flags |= NSF_CLOSE_IMMEDIATELY;
929      }
930      return;
931    }
932  } else
933#endif
934  {
935    while ((n = (int) recv(conn->sock, buf, sizeof(buf), 0)) > 0) {
936      DBG(("%p %d <- %d bytes (PLAIN)", conn, conn->flags, n));
937      iobuf_append(&conn->recv_iobuf, buf, n);
938      ns_call(conn, NS_RECV, &n);
939    }
940  }
941
942  if (ns_is_error(n)) {
943    conn->flags |= NSF_CLOSE_IMMEDIATELY;
944  }
945}
946
947static void ns_write_to_socket(struct ns_connection *conn) {
948  struct iobuf *io = &conn->send_iobuf;
949  int n = 0;
950
951#ifdef NS_ENABLE_SSL
952  if (conn->ssl != NULL) {
953    n = SSL_write(conn->ssl, io->buf, io->len);
954    if (n <= 0) {
955      int ssl_err = ns_ssl_err(conn, n);
956      if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) {
957        return; // Call us again
958      } else {
959        conn->flags |= NSF_CLOSE_IMMEDIATELY;
960      }
961    }
962  } else
963#endif
964  { n = (int) send(conn->sock, io->buf, io->len, 0); }
965
966  DBG(("%p %d -> %d bytes", conn, conn->flags, n));
967
968  ns_call(conn, NS_SEND, &n);
969  if (ns_is_error(n)) {
970    conn->flags |= NSF_CLOSE_IMMEDIATELY;
971  } else if (n > 0) {
972    iobuf_remove(io, n);
973  }
974}
975
976int ns_send(struct ns_connection *conn, const void *buf, int len) {
977  return (int) ns_out(conn, buf, len);
978}
979
980static void ns_handle_udp(struct ns_connection *ls) {
981  struct ns_connection nc;
982  char buf[NS_UDP_RECEIVE_BUFFER_SIZE];
983  int n;
984  socklen_t s_len = sizeof(nc.sa);
985
986  memset(&nc, 0, sizeof(nc));
987  n = recvfrom(ls->sock, buf, sizeof(buf), 0, &nc.sa.sa, &s_len);
988  if (n <= 0) {
989    DBG(("%p recvfrom: %s", ls, strerror(errno)));
990  } else {
991    nc.mgr = ls->mgr;
992    nc.recv_iobuf.buf = buf;
993    nc.recv_iobuf.len = nc.recv_iobuf.size = n;
994    nc.sock = ls->sock;
995    nc.callback = ls->callback;
996    nc.user_data = ls->user_data;
997    nc.proto_data = ls->proto_data;
998    nc.mgr = ls->mgr;
999    nc.listener = ls;
1000    nc.flags = NSF_UDP;
1001    DBG(("%p %d bytes received", ls, n));
1002    ns_call(&nc, NS_RECV, &n);
1003  }
1004}
1005
1006static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
1007  if (sock != INVALID_SOCKET) {
1008    FD_SET(sock, set);
1009    if (*max_fd == INVALID_SOCKET || sock > *max_fd) {
1010      *max_fd = sock;
1011    }
1012  }
1013}
1014
1015time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) {
1016  struct ns_connection *conn, *tmp_conn;
1017  struct timeval tv;
1018  fd_set read_set, write_set;
1019  sock_t max_fd = INVALID_SOCKET;
1020  time_t current_time = time(NULL);
1021
1022  FD_ZERO(&read_set);
1023  FD_ZERO(&write_set);
1024  ns_add_to_set(mgr->ctl[1], &read_set, &max_fd);
1025
1026  for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) {
1027    tmp_conn = conn->next;
1028    if (!(conn->flags & (NSF_LISTENING | NSF_CONNECTING))) {
1029      ns_call(conn, NS_POLL, &current_time);
1030    }
1031    if (conn->flags & NSF_CLOSE_IMMEDIATELY) {
1032      ns_close_conn(conn);
1033    } else {
1034      if (!(conn->flags & NSF_WANT_WRITE)) {
1035        //DBG(("%p read_set", conn));
1036        ns_add_to_set(conn->sock, &read_set, &max_fd);
1037      }
1038      if (((conn->flags & NSF_CONNECTING) && !(conn->flags & NSF_WANT_READ)) ||
1039          (conn->send_iobuf.len > 0 && !(conn->flags & NSF_CONNECTING) &&
1040           !(conn->flags & NSF_BUFFER_BUT_DONT_SEND))) {
1041        //DBG(("%p write_set", conn));
1042        ns_add_to_set(conn->sock, &write_set, &max_fd);
1043      }
1044    }
1045  }
1046
1047  tv.tv_sec = milli / 1000;
1048  tv.tv_usec = (milli % 1000) * 1000;
1049
1050  if (select((int) max_fd + 1, &read_set, &write_set, NULL, &tv) > 0) {
1051    // select() might have been waiting for a long time, reset current_time
1052    // now to prevent last_io_time being set to the past.
1053    current_time = time(NULL);
1054
1055    // Read wakeup messages
1056    if (mgr->ctl[1] != INVALID_SOCKET &&
1057        FD_ISSET(mgr->ctl[1], &read_set)) {
1058      struct ctl_msg ctl_msg;
1059      int len = (int) recv(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
1060      send(mgr->ctl[1], ctl_msg.message, 1, 0);
1061      if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) {
1062        struct ns_connection *c;
1063        for (c = ns_next(mgr, NULL); c != NULL; c = ns_next(mgr, c)) {
1064          ctl_msg.callback(c, NS_POLL, ctl_msg.message);
1065        }
1066      }
1067    }
1068
1069    for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) {
1070      tmp_conn = conn->next;
1071      if (FD_ISSET(conn->sock, &read_set)) {
1072        if (conn->flags & NSF_LISTENING) {
1073          if (conn->flags & NSF_UDP) {
1074            ns_handle_udp(conn);
1075          } else {
1076            // We're not looping here, and accepting just one connection at
1077            // a time. The reason is that eCos does not respect non-blocking
1078            // flag on a listening socket and hangs in a loop.
1079            accept_conn(conn);
1080          }
1081        } else {
1082          conn->last_io_time = current_time;
1083          ns_read_from_socket(conn);
1084        }
1085      }
1086
1087      if (FD_ISSET(conn->sock, &write_set)) {
1088        if (conn->flags & NSF_CONNECTING) {
1089          ns_read_from_socket(conn);
1090        } else if (!(conn->flags & NSF_BUFFER_BUT_DONT_SEND)) {
1091          conn->last_io_time = current_time;
1092          ns_write_to_socket(conn);
1093        }
1094      }
1095    }
1096  }
1097
1098  for (conn = mgr->active_connections; conn != NULL; conn = tmp_conn) {
1099    tmp_conn = conn->next;
1100    if ((conn->flags & NSF_CLOSE_IMMEDIATELY) ||
1101        (conn->send_iobuf.len == 0 &&
1102          (conn->flags & NSF_FINISHED_SENDING_DATA))) {
1103      ns_close_conn(conn);
1104    }
1105  }
1106
1107  return current_time;
1108}
1109
1110struct ns_connection *ns_connect(struct ns_mgr *mgr, const char *address,
1111                                 ns_callback_t callback, void *user_data) {
1112  sock_t sock = INVALID_SOCKET;
1113  struct ns_connection *nc = NULL;
1114  union socket_address sa;
1115  char cert[100], ca_cert[100];
1116  int rc, use_ssl, proto;
1117
1118  ns_parse_address(address, &sa, &proto, &use_ssl, cert, ca_cert);
1119  if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) {
1120    return NULL;
1121  }
1122  ns_set_non_blocking_mode(sock);
1123  rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa.sa, sizeof(sa.sin));
1124
1125  if (rc != 0 && ns_is_error(rc)) {
1126    closesocket(sock);
1127    return NULL;
1128  } else if ((nc = ns_add_sock(mgr, sock, callback, user_data)) == NULL) {
1129    closesocket(sock);
1130    return NULL;
1131  }
1132
1133  nc->sa = sa;   // Important, cause UDP conns will use sendto()
1134  nc->flags = (proto == SOCK_DGRAM) ? NSF_UDP : NSF_CONNECTING;
1135
1136#ifdef NS_ENABLE_SSL
1137  if (use_ssl) {
1138    if ((nc->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL ||
1139        ns_use_cert(nc->ssl_ctx, cert) != 0 ||
1140        ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0 ||
1141        (nc->ssl = SSL_new(nc->ssl_ctx)) == NULL) {
1142      ns_close_conn(nc);
1143      return NULL;
1144    } else {
1145      SSL_set_fd(nc->ssl, sock);
1146    }
1147  }
1148#endif
1149
1150  return nc;
1151}
1152
1153struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock,
1154                                  ns_callback_t callback, void *user_data) {
1155  struct ns_connection *conn;
1156  if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) {
1157    memset(conn, 0, sizeof(*conn));
1158    ns_set_non_blocking_mode(sock);
1159    ns_set_close_on_exec(sock);
1160    conn->sock = sock;
1161    conn->user_data = user_data;
1162    conn->callback = callback;
1163    conn->mgr = s;
1164    conn->last_io_time = time(NULL);
1165    ns_add_conn(s, conn);
1166    DBG(("%p %d", conn, sock));
1167  }
1168  return conn;
1169}
1170
1171struct ns_connection *ns_next(struct ns_mgr *s, struct ns_connection *conn) {
1172  return conn == NULL ? s->active_connections : conn->next;
1173}
1174
1175void ns_broadcast(struct ns_mgr *mgr, ns_callback_t cb,void *data, size_t len) {
1176  struct ctl_msg ctl_msg;
1177  if (mgr->ctl[0] != INVALID_SOCKET && data != NULL &&
1178      len < sizeof(ctl_msg.message)) {
1179    ctl_msg.callback = cb;
1180    memcpy(ctl_msg.message, data, len);
1181    send(mgr->ctl[0], (char *) &ctl_msg,
1182         offsetof(struct ctl_msg, message) + len, 0);
1183    recv(mgr->ctl[0], (char *) &len, 1, 0);
1184  }
1185}
1186
1187void ns_mgr_init(struct ns_mgr *s, void *user_data) {
1188  memset(s, 0, sizeof(*s));
1189  s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
1190  s->user_data = user_data;
1191
1192#ifdef _WIN32
1193  { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); }
1194#else
1195  // Ignore SIGPIPE signal, so if client cancels the request, it
1196  // won't kill the whole process.
1197  signal(SIGPIPE, SIG_IGN);
1198#endif
1199
1200#ifndef NS_DISABLE_SOCKETPAIR
1201  do {
1202    ns_socketpair2(s->ctl, SOCK_DGRAM);
1203  } while (s->ctl[0] == INVALID_SOCKET);
1204#endif
1205
1206#ifdef NS_ENABLE_SSL
1207  {static int init_done; if (!init_done) { SSL_library_init(); init_done++; }}
1208#endif
1209}
1210
1211void ns_mgr_free(struct ns_mgr *s) {
1212  struct ns_connection *conn, *tmp_conn;
1213
1214  DBG(("%p", s));
1215  if (s == NULL) return;
1216  // Do one last poll, see https://github.com/cesanta/mongoose/issues/286
1217  ns_mgr_poll(s, 0);
1218
1219  if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]);
1220  if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]);
1221  s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
1222
1223  for (conn = s->active_connections; conn != NULL; conn = tmp_conn) {
1224    tmp_conn = conn->next;
1225    ns_close_conn(conn);
1226  }
1227}
1228// net_skeleton end
1229#endif  // NOEMBED_NET_SKELETON
1230
1231#include <ctype.h>
1232
1233#ifdef _WIN32         //////////////// Windows specific defines and includes
1234#include <io.h>       // For _lseeki64
1235#include <direct.h>   // For _mkdir
1236#ifndef S_ISDIR
1237#define S_ISDIR(x) ((x) & _S_IFDIR)
1238#endif
1239#ifdef stat
1240#undef stat
1241#endif
1242#ifdef lseek
1243#undef lseek
1244#endif
1245#ifdef popen
1246#undef popen
1247#endif
1248#ifdef pclose
1249#undef pclose
1250#endif
1251#define stat(x, y) mg_stat((x), (y))
1252#define fopen(x, y) mg_fopen((x), (y))
1253#define open(x, y, z) mg_open((x), (y), (z))
1254#define close(x) _close(x)
1255#define fileno(x) _fileno(x)
1256#define lseek(x, y, z) _lseeki64((x), (y), (z))
1257#define read(x, y, z) _read((x), (y), (z))
1258#define write(x, y, z) _write((x), (y), (z))
1259#define popen(x, y) _popen((x), (y))
1260#define pclose(x) _pclose(x)
1261#define mkdir(x, y) _mkdir(x)
1262#define rmdir(x) _rmdir(x)
1263#define strdup(x) _strdup(x)
1264#ifndef __func__
1265#define STRX(x) #x
1266#define STR(x) STRX(x)
1267#define __func__ __FILE__ ":" STR(__LINE__)
1268#endif
1269/* MINGW has adopted the MSVC formatting for 64-bit ints as of gcc 4.4 till 4.8*/
1270#if (defined(__MINGW32__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ < 8))) || defined(_MSC_VER)
1271#define INT64_FMT   "I64d"
1272#else
1273#define INT64_FMT   "lld"
1274#endif
1275#define flockfile(x)      ((void) (x))
1276#define funlockfile(x)    ((void) (x))
1277typedef struct _stati64 file_stat_t;
1278typedef HANDLE process_id_t;
1279
1280#else                    ////////////// UNIX specific defines and includes
1281
1282#if !defined(MONGOOSE_NO_FILESYSTEM) &&\
1283    (!defined(MONGOOSE_NO_DAV) || !defined(MONGOOSE_NO_DIRECTORY_LISTING))
1284#include <dirent.h>
1285#endif
1286#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_DL)
1287#include <dlfcn.h>
1288#endif
1289#include <inttypes.h>
1290#include <pwd.h>
1291#if !defined(O_BINARY)
1292#define O_BINARY 0
1293#endif
1294#define INT64_FMT PRId64
1295typedef struct stat file_stat_t;
1296typedef pid_t process_id_t;
1297#endif                  //////// End of platform-specific defines and includes
1298
1299#include "mongoose.h"
1300
1301#define MAX_REQUEST_SIZE 16384
1302#define IOBUF_SIZE 8192
1303#define MAX_PATH_SIZE 8192
1304#define DEFAULT_CGI_PATTERN "**.cgi$|**.pl$|**.php$"
1305#define CGI_ENVIRONMENT_SIZE 8192
1306#define MAX_CGI_ENVIR_VARS 64
1307#define ENV_EXPORT_TO_CGI "MONGOOSE_CGI"
1308#define PASSWORDS_FILE_NAME ".htpasswd"
1309
1310#ifndef MONGOOSE_USE_WEBSOCKET_PING_INTERVAL
1311#define MONGOOSE_USE_WEBSOCKET_PING_INTERVAL 5
1312#endif
1313
1314// Extra HTTP headers to send in every static file reply
1315#if !defined(MONGOOSE_USE_EXTRA_HTTP_HEADERS)
1316#define MONGOOSE_USE_EXTRA_HTTP_HEADERS ""
1317#endif
1318
1319#ifndef MONGOOSE_POST_SIZE_LIMIT
1320#define MONGOOSE_POST_SIZE_LIMIT 0
1321#endif
1322
1323#ifndef MONGOOSE_IDLE_TIMEOUT_SECONDS
1324#define MONGOOSE_IDLE_TIMEOUT_SECONDS 300
1325#endif
1326
1327#if defined(NS_DISABLE_SOCKETPAIR) && !defined(MONGOOSE_NO_CGI)
1328#define MONGOOSE_NO_CGI
1329#endif
1330
1331#ifdef MONGOOSE_NO_FILESYSTEM
1332#define MONGOOSE_NO_AUTH
1333#if !defined(MONGOOSE_NO_CGI)
1334#define MONGOOSE_NO_CGI
1335#endif
1336#define MONGOOSE_NO_DAV
1337#define MONGOOSE_NO_DIRECTORY_LISTING
1338#define MONGOOSE_NO_LOGGING
1339#define MONGOOSE_NO_SSI
1340#define MONGOOSE_NO_DL
1341#endif
1342
1343struct vec {
1344  const char *ptr;
1345  int len;
1346};
1347
1348// For directory listing and WevDAV support
1349struct dir_entry {
1350  struct connection *conn;
1351  char *file_name;
1352  file_stat_t st;
1353};
1354
1355// NOTE(lsm): this enum shoulds be in sync with the config_options.
1356enum {
1357  ACCESS_CONTROL_LIST,
1358#ifndef MONGOOSE_NO_FILESYSTEM
1359  ACCESS_LOG_FILE,
1360#ifndef MONGOOSE_NO_AUTH
1361  AUTH_DOMAIN,
1362#endif
1363#ifndef MONGOOSE_NO_CGI
1364  CGI_INTERPRETER,
1365  CGI_PATTERN,
1366#endif
1367  DAV_AUTH_FILE,
1368  DOCUMENT_ROOT,
1369#ifndef MONGOOSE_NO_DIRECTORY_LISTING
1370  ENABLE_DIRECTORY_LISTING,
1371#endif
1372#endif
1373  ENABLE_PROXY,
1374  EXTRA_MIME_TYPES,
1375#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH)
1376  GLOBAL_AUTH_FILE,
1377#endif
1378#ifndef MONGOOSE_NO_FILESYSTEM
1379  HIDE_FILES_PATTERN,
1380  HEXDUMP_FILE,
1381  INDEX_FILES,
1382#endif
1383  LISTENING_PORT,
1384#ifndef _WIN32
1385  RUN_AS_USER,
1386#endif
1387#ifndef MONGOOSE_NO_SSI
1388  SSI_PATTERN,
1389#endif
1390  URL_REWRITES,
1391  NUM_OPTIONS
1392};
1393
1394static const char *static_config_options[] = {
1395  "access_control_list", NULL,
1396#ifndef MONGOOSE_NO_FILESYSTEM
1397  "access_log_file", NULL,
1398#ifndef MONGOOSE_NO_AUTH
1399  "auth_domain", "mydomain.com",
1400#endif
1401#ifndef MONGOOSE_NO_CGI
1402  "cgi_interpreter", NULL,
1403  "cgi_pattern", DEFAULT_CGI_PATTERN,
1404#endif
1405  "dav_auth_file", NULL,
1406  "document_root",  NULL,
1407#ifndef MONGOOSE_NO_DIRECTORY_LISTING
1408  "enable_directory_listing", "yes",
1409#endif
1410#endif
1411  "enable_proxy", NULL,
1412  "extra_mime_types", NULL,
1413#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH)
1414  "global_auth_file", NULL,
1415#endif
1416#ifndef MONGOOSE_NO_FILESYSTEM
1417  "hide_files_patterns", NULL,
1418  "hexdump_file", NULL,
1419  "index_files","index.html,index.htm,index.shtml,index.cgi,index.php",
1420#endif
1421  "listening_port", NULL,
1422#ifndef _WIN32
1423  "run_as_user", NULL,
1424#endif
1425#ifndef MONGOOSE_NO_SSI
1426  "ssi_pattern", "**.shtml$|**.shtm$",
1427#endif
1428  "url_rewrites", NULL,
1429  NULL
1430};
1431
1432struct mg_server {
1433  struct ns_mgr ns_mgr;
1434  union socket_address lsa;   // Listening socket address
1435  mg_handler_t event_handler;
1436  char *config_options[NUM_OPTIONS];
1437};
1438
1439// Local endpoint representation
1440union endpoint {
1441  int fd;                     // Opened regular local file
1442  struct ns_connection *nc;   // CGI or proxy->target connection
1443};
1444
1445enum endpoint_type {
1446 EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT, EP_CLIENT, EP_PROXY
1447};
1448
1449#define MG_HEADERS_SENT NSF_USER_1
1450#define MG_LONG_RUNNING NSF_USER_2
1451#define MG_CGI_CONN NSF_USER_3
1452#define MG_PROXY_CONN NSF_USER_4
1453#define MG_PROXY_DONT_PARSE NSF_USER_5
1454
1455struct connection {
1456  struct ns_connection *ns_conn;  // NOTE(lsm): main.c depends on this order
1457  struct mg_connection mg_conn;
1458  struct mg_server *server;
1459  union endpoint endpoint;
1460  enum endpoint_type endpoint_type;
1461  char *path_info;
1462  char *request;
1463  int64_t num_bytes_recv; // Total number of bytes received
1464  int64_t cl;             // Reply content length, for Range support
1465  int request_len;  // Request length, including last \r\n after last header
1466};
1467
1468#define MG_CONN_2_CONN(c) ((struct connection *) ((char *) (c) - \
1469  offsetof(struct connection, mg_conn)))
1470
1471static void open_local_endpoint(struct connection *conn, int skip_user);
1472static void close_local_endpoint(struct connection *conn);
1473static void mg_ev_handler(struct ns_connection *nc, int ev, void *p);
1474
1475static const struct {
1476  const char *extension;
1477  size_t ext_len;
1478  const char *mime_type;
1479} static_builtin_mime_types[] = {
1480  {".html", 5, "text/html"},
1481  {".htm", 4, "text/html"},
1482  {".shtm", 5, "text/html"},
1483  {".shtml", 6, "text/html"},
1484  {".css", 4, "text/css"},
1485  {".js",  3, "application/javascript"},
1486  {".ico", 4, "image/x-icon"},
1487  {".gif", 4, "image/gif"},
1488  {".jpg", 4, "image/jpeg"},
1489  {".jpeg", 5, "image/jpeg"},
1490  {".png", 4, "image/png"},
1491  {".svg", 4, "image/svg+xml"},
1492  {".txt", 4, "text/plain"},
1493  {".torrent", 8, "application/x-bittorrent"},
1494  {".wav", 4, "audio/x-wav"},
1495  {".mp3", 4, "audio/x-mp3"},
1496  {".mid", 4, "audio/mid"},
1497  {".m3u", 4, "audio/x-mpegurl"},
1498  {".ogg", 4, "application/ogg"},
1499  {".ram", 4, "audio/x-pn-realaudio"},
1500  {".xml", 4, "text/xml"},
1501  {".json",  5, "application/json"},
1502  {".xslt", 5, "application/xml"},
1503  {".xsl", 4, "application/xml"},
1504  {".ra",  3, "audio/x-pn-realaudio"},
1505  {".doc", 4, "application/msword"},
1506  {".exe", 4, "application/octet-stream"},
1507  {".zip", 4, "application/x-zip-compressed"},
1508  {".xls", 4, "application/excel"},
1509  {".tgz", 4, "application/x-tar-gz"},
1510  {".tar", 4, "application/x-tar"},
1511  {".gz",  3, "application/x-gunzip"},
1512  {".arj", 4, "application/x-arj-compressed"},
1513  {".rar", 4, "application/x-rar-compressed"},
1514  {".rtf", 4, "application/rtf"},
1515  {".pdf", 4, "application/pdf"},
1516  {".swf", 4, "application/x-shockwave-flash"},
1517  {".mpg", 4, "video/mpeg"},
1518  {".webm", 5, "video/webm"},
1519  {".mpeg", 5, "video/mpeg"},
1520  {".mov", 4, "video/quicktime"},
1521  {".mp4", 4, "video/mp4"},
1522  {".m4v", 4, "video/x-m4v"},
1523  {".asf", 4, "video/x-ms-asf"},
1524  {".avi", 4, "video/x-msvideo"},
1525  {".bmp", 4, "image/bmp"},
1526  {".ttf", 4, "application/x-font-ttf"},
1527  {NULL,  0, NULL}
1528};
1529
1530#ifndef MONGOOSE_NO_THREADS
1531void *mg_start_thread(void *(*f)(void *), void *p) {
1532  return ns_start_thread(f, p);
1533}
1534#endif  // MONGOOSE_NO_THREADS
1535
1536#ifndef MONGOOSE_NO_MMAP
1537#ifdef _WIN32
1538static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
1539                  int offset) {
1540  HANDLE fh = (HANDLE) _get_osfhandle(fd);
1541  HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
1542  void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
1543  CloseHandle(mh);
1544  return p;
1545}
1546#define munmap(x, y)  UnmapViewOfFile(x)
1547#define MAP_FAILED NULL
1548#define MAP_PRIVATE 0
1549#define PROT_READ 0
1550#else
1551#include <sys/mman.h>
1552#endif
1553
1554void *mg_mmap(FILE *fp, size_t size) {
1555  void *p = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
1556  return p == MAP_FAILED ? NULL : p;
1557}
1558
1559void mg_munmap(void *p, size_t size) {
1560  munmap(p, size);
1561}
1562#endif  // MONGOOSE_NO_MMAP
1563
1564#if defined(_WIN32) && !defined(MONGOOSE_NO_FILESYSTEM)
1565// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
1566// wbuf and wbuf_len is a target buffer and its length.
1567static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
1568  char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p;
1569
1570  strncpy(buf, path, sizeof(buf));
1571  buf[sizeof(buf) - 1] = '\0';
1572
1573  // Trim trailing slashes. Leave backslash for paths like "X:\"
1574  p = buf + strlen(buf) - 1;
1575  while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
1576
1577  // Convert to Unicode and back. If doubly-converted string does not
1578  // match the original, something is fishy, reject.
1579  memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
1580  MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
1581  WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
1582                      NULL, NULL);
1583  if (strcmp(buf, buf2) != 0) {
1584    wbuf[0] = L'\0';
1585  }
1586}
1587
1588static int mg_stat(const char *path, file_stat_t *st) {
1589  wchar_t wpath[MAX_PATH_SIZE];
1590  to_wchar(path, wpath, ARRAY_SIZE(wpath));
1591  DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st)));
1592  return _wstati64(wpath, st);
1593}
1594
1595static FILE *mg_fopen(const char *path, const char *mode) {
1596  wchar_t wpath[MAX_PATH_SIZE], wmode[10];
1597  to_wchar(path, wpath, ARRAY_SIZE(wpath));
1598  to_wchar(mode, wmode, ARRAY_SIZE(wmode));
1599  return _wfopen(wpath, wmode);
1600}
1601
1602static int mg_open(const char *path, int flag, int mode) {
1603  wchar_t wpath[MAX_PATH_SIZE];
1604  to_wchar(path, wpath, ARRAY_SIZE(wpath));
1605  return _wopen(wpath, flag, mode);
1606}
1607#endif // _WIN32 && !MONGOOSE_NO_FILESYSTEM
1608
1609// A helper function for traversing a comma separated list of values.
1610// It returns a list pointer shifted to the next value, or NULL if the end
1611// of the list found.
1612// Value is stored in val vector. If value has form "x=y", then eq_val
1613// vector is initialized to point to the "y" part, and val vector length
1614// is adjusted to point only to "x".
1615static const char *next_option(const char *list, struct vec *val,
1616                               struct vec *eq_val) {
1617  if (list == NULL || *list == '\0') {
1618    // End of the list
1619    list = NULL;
1620  } else {
1621    val->ptr = list;
1622    if ((list = strchr(val->ptr, ',')) != NULL) {
1623      // Comma found. Store length and shift the list ptr
1624      val->len = list - val->ptr;
1625      list++;
1626    } else {
1627      // This value is the last one
1628      list = val->ptr + strlen(val->ptr);
1629      val->len = list - val->ptr;
1630    }
1631
1632    if (eq_val != NULL) {
1633      // Value has form "x=y", adjust pointers and lengths
1634      // so that val points to "x", and eq_val points to "y".
1635      eq_val->len = 0;
1636      eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len);
1637      if (eq_val->ptr != NULL) {
1638        eq_val->ptr++;  // Skip over '=' character
1639        eq_val->len = val->ptr + val->len - eq_val->ptr;
1640        val->len = (eq_val->ptr - val->ptr) - 1;
1641      }
1642    }
1643  }
1644
1645  return list;
1646}
1647
1648// Like snprintf(), but never returns negative value, or a value
1649// that is larger than a supplied buffer.
1650static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap) {
1651  int n;
1652  if (buflen < 1) return 0;
1653  n = vsnprintf(buf, buflen, fmt, ap);
1654  if (n < 0) {
1655    n = 0;
1656  } else if (n >= (int) buflen) {
1657    n = (int) buflen - 1;
1658  }
1659  buf[n] = '\0';
1660  return n;
1661}
1662
1663static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) {
1664  va_list ap;
1665  int n;
1666  va_start(ap, fmt);
1667  n = mg_vsnprintf(buf, buflen, fmt, ap);
1668  va_end(ap);
1669  return n;
1670}
1671
1672// Check whether full request is buffered. Return:
1673//   -1  if request is malformed
1674//    0  if request is not yet fully buffered
1675//   >0  actual request length, including last \r\n\r\n
1676static int get_request_len(const char *s, int buf_len) {
1677  const unsigned char *buf = (unsigned char *) s;
1678  int i;
1679
1680  for (i = 0; i < buf_len; i++) {
1681    // Control characters are not allowed but >=128 are.
1682    // Abort scan as soon as one malformed character is found.
1683    if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) {
1684      return -1;
1685    } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
1686      return i + 2;
1687    } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
1688               buf[i + 2] == '\n') {
1689      return i + 3;
1690    }
1691  }
1692
1693  return 0;
1694}
1695
1696// Skip the characters until one of the delimiters characters found.
1697// 0-terminate resulting word. Skip the rest of the delimiters if any.
1698// Advance pointer to buffer to the next word. Return found 0-terminated word.
1699static char *skip(char **buf, const char *delimiters) {
1700  char *p, *begin_word, *end_word, *end_delimiters;
1701
1702  begin_word = *buf;
1703  end_word = begin_word + strcspn(begin_word, delimiters);
1704  end_delimiters = end_word + strspn(end_word, delimiters);
1705
1706  for (p = end_word; p < end_delimiters; p++) {
1707    *p = '\0';
1708  }
1709
1710  *buf = end_delimiters;
1711
1712  return begin_word;
1713}
1714
1715// Parse HTTP headers from the given buffer, advance buffer to the point
1716// where parsing stopped.
1717static void parse_http_headers(char **buf, struct mg_connection *ri) {
1718  size_t i;
1719
1720  for (i = 0; i < ARRAY_SIZE(ri->http_headers); i++) {
1721    ri->http_headers[i].name = skip(buf, ": ");
1722    ri->http_headers[i].value = skip(buf, "\r\n");
1723    if (ri->http_headers[i].name[0] == '\0')
1724      break;
1725    ri->num_headers = i + 1;
1726  }
1727}
1728
1729static const char *status_code_to_str(int status_code) {
1730  switch (status_code) {
1731
1732    case 100: return "Continue";
1733    case 101: return "Switching Protocols";
1734    case 102: return "Processing";
1735
1736    case 200: return "OK";
1737    case 201: return "Created";
1738    case 202: return "Accepted";
1739    case 203: return "Non-Authoritative Information";
1740    case 204: return "No Content";
1741    case 205: return "Reset Content";
1742    case 206: return "Partial Content";
1743    case 207: return "Multi-Status";
1744    case 208: return "Already Reported";
1745    case 226: return "IM Used";
1746
1747    case 300: return "Multiple Choices";
1748    case 301: return "Moved Permanently";
1749    case 302: return "Found";
1750    case 303: return "See Other";
1751    case 304: return "Not Modified";
1752    case 305: return "Use Proxy";
1753    case 306: return "Switch Proxy";
1754    case 307: return "Temporary Redirect";
1755    case 308: return "Permanent Redirect";
1756
1757    case 400: return "Bad Request";
1758    case 401: return "Unauthorized";
1759    case 402: return "Payment Required";
1760    case 403: return "Forbidden";
1761    case 404: return "Not Found";
1762    case 405: return "Method Not Allowed";
1763    case 406: return "Not Acceptable";
1764    case 407: return "Proxy Authentication Required";
1765    case 408: return "Request Timeout";
1766    case 409: return "Conflict";
1767    case 410: return "Gone";
1768    case 411: return "Length Required";
1769    case 412: return "Precondition Failed";
1770    case 413: return "Payload Too Large";
1771    case 414: return "URI Too Long";
1772    case 415: return "Unsupported Media Type";
1773    case 416: return "Requested Range Not Satisfiable";
1774    case 417: return "Expectation Failed";
1775    case 418: return "I\'m a teapot";
1776    case 422: return "Unprocessable Entity";
1777    case 423: return "Locked";
1778    case 424: return "Failed Dependency";
1779    case 426: return "Upgrade Required";
1780    case 428: return "Precondition Required";
1781    case 429: return "Too Many Requests";
1782    case 431: return "Request Header Fields Too Large";
1783    case 451: return "Unavailable For Legal Reasons";
1784
1785    case 500: return "Internal Server Error";
1786    case 501: return "Not Implemented";
1787    case 502: return "Bad Gateway";
1788    case 503: return "Service Unavailable";
1789    case 504: return "Gateway Timeout";
1790    case 505: return "HTTP Version Not Supported";
1791    case 506: return "Variant Also Negotiates";
1792    case 507: return "Insufficient Storage";
1793    case 508: return "Loop Detected";
1794    case 510: return "Not Extended";
1795    case 511: return "Network Authentication Required";
1796
1797    default:  return "Server Error";
1798  }
1799}
1800
1801static int call_user(struct connection *conn, enum mg_event ev) {
1802  return conn != NULL && conn->server != NULL &&
1803    conn->server->event_handler != NULL ?
1804    conn->server->event_handler(&conn->mg_conn, ev) : MG_FALSE;
1805}
1806
1807static void send_http_error(struct connection *conn, int code,
1808                            const char *fmt, ...) {
1809  const char *message = status_code_to_str(code);
1810  const char *rewrites = conn->server->config_options[URL_REWRITES];
1811  char headers[200], body[200];
1812  struct vec a, b;
1813  va_list ap;
1814  int body_len, headers_len, match_code;
1815
1816  conn->mg_conn.status_code = code;
1817
1818  // Invoke error handler if it is set
1819  if (call_user(conn, MG_HTTP_ERROR) == MG_TRUE) {
1820    close_local_endpoint(conn);
1821    return;
1822  }
1823
1824  // Handle error code rewrites
1825  while ((rewrites = next_option(rewrites, &a, &b)) != NULL) {
1826    if ((match_code = atoi(a.ptr)) > 0 && match_code == code) {
1827      struct mg_connection *c = &conn->mg_conn;
1828      c->status_code = 302;
1829      mg_printf(c, "HTTP/1.1 %d Moved\r\n"
1830                "Location: %.*s?code=%d&orig_uri=%s&query_string=%s\r\n\r\n",
1831                c->status_code, b.len, b.ptr, code, c->uri,
1832                c->query_string == NULL ? "" : c->query_string);
1833      close_local_endpoint(conn);
1834      return;
1835    }
1836  }
1837
1838  body_len = mg_snprintf(body, sizeof(body), "%d %s\n", code, message);
1839  if (fmt != NULL) {
1840    va_start(ap, fmt);
1841    body_len += mg_vsnprintf(body + body_len, sizeof(body) - body_len, fmt, ap);
1842    va_end(ap);
1843  }
1844  if ((code >= 300 && code <= 399) || code == 204) {
1845    // 3xx errors do not have body
1846    body_len = 0;
1847  }
1848  headers_len = mg_snprintf(headers, sizeof(headers),
1849                            "HTTP/1.1 %d %s\r\nContent-Length: %d\r\n"
1850                            "Content-Type: text/plain\r\n\r\n",
1851                            code, message, body_len);
1852  ns_send(conn->ns_conn, headers, headers_len);
1853  ns_send(conn->ns_conn, body, body_len);
1854  close_local_endpoint(conn);  // This will write to the log file
1855}
1856
1857static void write_chunk(struct connection *conn, const char *buf, int len) {
1858  char chunk_size[50];
1859  int n = mg_snprintf(chunk_size, sizeof(chunk_size), "%X\r\n", len);
1860  ns_send(conn->ns_conn, chunk_size, n);
1861  ns_send(conn->ns_conn, buf, len);
1862  ns_send(conn->ns_conn, "\r\n", 2);
1863}
1864
1865size_t mg_printf(struct mg_connection *conn, const char *fmt, ...) {
1866  struct connection *c = MG_CONN_2_CONN(conn);
1867  va_list ap;
1868
1869  va_start(ap, fmt);
1870  ns_vprintf(c->ns_conn, fmt, ap);
1871  va_end(ap);
1872
1873  return c->ns_conn->send_iobuf.len;
1874}
1875
1876static void ns_forward(struct ns_connection *from, struct ns_connection *to) {
1877  DBG(("%p -> %p %lu bytes", from, to, (unsigned long)from->recv_iobuf.len));
1878  ns_send(to, from->recv_iobuf.buf, from->recv_iobuf.len);
1879  iobuf_remove(&from->recv_iobuf, from->recv_iobuf.len);
1880}
1881
1882#ifndef MONGOOSE_NO_CGI
1883#ifdef _WIN32
1884struct threadparam {
1885  sock_t s;
1886  HANDLE hPipe;
1887};
1888
1889static int wait_until_ready(sock_t sock, int for_read) {
1890  fd_set set;
1891  FD_ZERO(&set);
1892  FD_SET(sock, &set);
1893  select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0);
1894  return 1;
1895}
1896
1897static void *push_to_stdin(void *arg) {
1898  struct threadparam *tp = (struct threadparam *)arg;
1899  int n, sent, stop = 0;
1900  DWORD k;
1901  char buf[IOBUF_SIZE];
1902
1903  while (!stop && wait_until_ready(tp->s, 1) &&
1904         (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) {
1905    if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue;
1906    for (sent = 0; !stop && sent < n; sent += k) {
1907      if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1;
1908    }
1909  }
1910  DBG(("%s", "FORWARED EVERYTHING TO CGI"));
1911  CloseHandle(tp->hPipe);
1912  NS_FREE(tp);
1913  _endthread();
1914  return NULL;
1915}
1916
1917static void *pull_from_stdout(void *arg) {
1918  struct threadparam *tp = (struct threadparam *)arg;
1919  int k = 0, stop = 0;
1920  DWORD n, sent;
1921  char buf[IOBUF_SIZE];
1922
1923  while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
1924    for (sent = 0; !stop && sent < n; sent += k) {
1925      if (wait_until_ready(tp->s, 0) &&
1926          (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) stop = 1;
1927    }
1928  }
1929  DBG(("%s", "EOF FROM CGI"));
1930  CloseHandle(tp->hPipe);
1931  shutdown(tp->s, 2);  // Without this, IO thread may get truncated data
1932  closesocket(tp->s);
1933  NS_FREE(tp);
1934  _endthread();
1935  return NULL;
1936}
1937
1938static void spawn_stdio_thread(sock_t sock, HANDLE hPipe,
1939                               void *(*func)(void *)) {
1940  struct threadparam *tp = (struct threadparam *)NS_MALLOC(sizeof(*tp));
1941  if (tp != NULL) {
1942    tp->s = sock;
1943    tp->hPipe = hPipe;
1944    mg_start_thread(func, tp);
1945  }
1946}
1947
1948static void abs_path(const char *utf8_path, char *abs_path, size_t len) {
1949  wchar_t buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE];
1950  to_wchar(utf8_path, buf, ARRAY_SIZE(buf));
1951  GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL);
1952  WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0);
1953}
1954
1955static process_id_t start_process(char *interp, const char *cmd,
1956                                  const char *env, const char *envp[],
1957                                  const char *dir, sock_t sock) {
1958  STARTUPINFOW si;
1959  PROCESS_INFORMATION pi;
1960  HANDLE a[2], b[2], me = GetCurrentProcess();
1961  wchar_t wcmd[MAX_PATH_SIZE], full_dir[MAX_PATH_SIZE];
1962  char buf[MAX_PATH_SIZE], buf4[MAX_PATH_SIZE], buf5[MAX_PATH_SIZE],
1963       cmdline[MAX_PATH_SIZE], *p;
1964  DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
1965  FILE *fp;
1966
1967  memset(&si, 0, sizeof(si));
1968  memset(&pi, 0, sizeof(pi));
1969
1970  si.cb = sizeof(si);
1971  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1972  si.wShowWindow = SW_HIDE;
1973  si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1974
1975  CreatePipe(&a[0], &a[1], NULL, 0);
1976  CreatePipe(&b[0], &b[1], NULL, 0);
1977  DuplicateHandle(me, a[0], me, &si.hStdInput, 0, TRUE, flags);
1978  DuplicateHandle(me, b[1], me, &si.hStdOutput, 0, TRUE, flags);
1979
1980  if (interp == NULL && (fp = fopen(cmd, "r")) != NULL) {
1981    buf[0] = buf[1] = '\0';
1982    fgets(buf, sizeof(buf), fp);
1983    buf[sizeof(buf) - 1] = '\0';
1984    if (buf[0] == '#' && buf[1] == '!') {
1985      interp = buf + 2;
1986      for (p = interp + strlen(interp) - 1;
1987           isspace(* (uint8_t *) p) && p > interp; p--) *p = '\0';
1988    }
1989    fclose(fp);
1990  }
1991
1992  if (interp != NULL) {
1993    abs_path(interp, buf4, ARRAY_SIZE(buf4));
1994    interp = buf4;
1995  }
1996  abs_path(dir, buf5, ARRAY_SIZE(buf5));
1997  to_wchar(dir, full_dir, ARRAY_SIZE(full_dir));
1998  mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\"",
1999              interp ? interp : "", interp ? " " : "", cmd);
2000  to_wchar(cmdline, wcmd, ARRAY_SIZE(wcmd));
2001
2002  if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP,
2003                     (void *) env, full_dir, &si, &pi) != 0) {
2004    spawn_stdio_thread(sock, a[1], push_to_stdin);
2005    spawn_stdio_thread(sock, b[0], pull_from_stdout);
2006  } else {
2007    CloseHandle(a[1]);
2008    CloseHandle(b[0]);
2009    closesocket(sock);
2010  }
2011  DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess));
2012
2013  // Not closing a[0] and b[1] because we've used DUPLICATE_CLOSE_SOURCE
2014  CloseHandle(si.hStdOutput);
2015  CloseHandle(si.hStdInput);
2016  //CloseHandle(pi.hThread);
2017  //CloseHandle(pi.hProcess);
2018
2019  return pi.hProcess;
2020}
2021#else
2022static process_id_t start_process(const char *interp, const char *cmd,
2023                                  const char *env, const char *envp[],
2024                                  const char *dir, sock_t sock) {
2025  char buf[500];
2026  process_id_t pid = fork();
2027  (void) env;
2028
2029  if (pid == 0) {
2030    (void) chdir(dir);
2031    (void) dup2(sock, 0);
2032    (void) dup2(sock, 1);
2033    closesocket(sock);
2034
2035    // After exec, all signal handlers are restored to their default values,
2036    // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
2037    // implementation, SIGCHLD's handler will leave unchanged after exec
2038    // if it was set to be ignored. Restore it to default action.
2039    signal(SIGCHLD, SIG_DFL);
2040
2041    if (interp == NULL) {
2042      execle(cmd, cmd, (char *) 0, envp); // Using (char *) 0 to avoid warning
2043    } else {
2044      execle(interp, interp, cmd, (char *) 0, envp);
2045    }
2046    snprintf(buf, sizeof(buf), "Status: 500\r\n\r\n"
2047             "500 Server Error: %s%s%s: %s", interp == NULL ? "" : interp,
2048             interp == NULL ? "" : " ", cmd, strerror(errno));
2049    send(1, buf, strlen(buf), 0);
2050    exit(EXIT_FAILURE);  // exec call failed
2051  }
2052
2053  return pid;
2054}
2055#endif  // _WIN32
2056
2057// This structure helps to create an environment for the spawned CGI program.
2058// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
2059// last element must be NULL.
2060// However, on Windows there is a requirement that all these VARIABLE=VALUE\0
2061// strings must reside in a contiguous buffer. The end of the buffer is
2062// marked by two '\0' characters.
2063// We satisfy both worlds: we create an envp array (which is vars), all
2064// entries are actually pointers inside buf.
2065struct cgi_env_block {
2066  struct mg_connection *conn;
2067  char buf[CGI_ENVIRONMENT_SIZE];       // Environment buffer
2068  const char *vars[MAX_CGI_ENVIR_VARS]; // char *envp[]
2069  int len;                              // Space taken
2070  int nvars;                            // Number of variables in envp[]
2071};
2072
2073// Append VARIABLE=VALUE\0 string to the buffer, and add a respective
2074// pointer into the vars array.
2075static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
2076  int n, space;
2077  char *added;
2078  va_list ap;
2079
2080  // Calculate how much space is left in the buffer
2081  space = sizeof(block->buf) - block->len - 2;
2082  assert(space >= 0);
2083
2084  // Make a pointer to the free space int the buffer
2085  added = block->buf + block->len;
2086
2087  // Copy VARIABLE=VALUE\0 string into the free space
2088  va_start(ap, fmt);
2089  n = mg_vsnprintf(added, (size_t) space, fmt, ap);
2090  va_end(ap);
2091
2092  // Make sure we do not overflow buffer and the envp array
2093  if (n > 0 && n + 1 < space &&
2094      block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
2095    // Append a pointer to the added string into the envp array
2096    block->vars[block->nvars++] = added;
2097    // Bump up used length counter. Include \0 terminator
2098    block->len += n + 1;
2099  }
2100
2101  return added;
2102}
2103
2104static void addenv2(struct cgi_env_block *blk, const char *name) {
2105  const char *s;
2106  if ((s = getenv(name)) != NULL) addenv(blk, "%s=%s", name, s);
2107}
2108
2109static void prepare_cgi_environment(struct connection *conn,
2110                                    const char *prog,
2111                                    struct cgi_env_block *blk) {
2112  struct mg_connection *ri = &conn->mg_conn;
2113  const char *s, *slash;
2114  char *p, **opts = conn->server->config_options;
2115  int  i;
2116
2117  blk->len = blk->nvars = 0;
2118  blk->conn = ri;
2119
2120  if ((s = getenv("SERVER_NAME")) != NULL) {
2121    addenv(blk, "SERVER_NAME=%s", s);
2122  } else {
2123    addenv(blk, "SERVER_NAME=%s", ri->local_ip);
2124  }
2125  addenv(blk, "SERVER_ROOT=%s", opts[DOCUMENT_ROOT]);
2126  addenv(blk, "DOCUMENT_ROOT=%s", opts[DOCUMENT_ROOT]);
2127  addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MONGOOSE_VERSION);
2128
2129  // Prepare the environment block
2130  addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
2131  addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
2132  addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
2133
2134  // TODO(lsm): fix this for IPv6 case
2135  //addenv(blk, "SERVER_PORT=%d", ri->remote_port);
2136
2137  addenv(blk, "REQUEST_METHOD=%s", ri->request_method);
2138  addenv(blk, "REMOTE_ADDR=%s", ri->remote_ip);
2139  addenv(blk, "REMOTE_PORT=%d", ri->remote_port);
2140  addenv(blk, "REQUEST_URI=%s%s%s", ri->uri,
2141         ri->query_string == NULL ? "" : "?",
2142         ri->query_string == NULL ? "" : ri->query_string);
2143
2144  // SCRIPT_NAME
2145  if (conn->path_info != NULL) {
2146    addenv(blk, "SCRIPT_NAME=%.*s",
2147           (int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri);
2148    addenv(blk, "PATH_INFO=%s", conn->path_info);
2149  } else {
2150    s = strrchr(prog, '/');
2151    slash = strrchr(ri->uri, '/');
2152    addenv(blk, "SCRIPT_NAME=%.*s%s",
2153           slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri,
2154           s == NULL ? prog : s);
2155  }
2156
2157  addenv(blk, "SCRIPT_FILENAME=%s", prog);
2158  addenv(blk, "PATH_TRANSLATED=%s", prog);
2159  addenv(blk, "HTTPS=%s", conn->ns_conn->ssl != NULL ? "on" : "off");
2160
2161  if ((s = mg_get_header(ri, "Content-Type")) != NULL)
2162    addenv(blk, "CONTENT_TYPE=%s", s);
2163
2164  if (ri->query_string != NULL)
2165    addenv(blk, "QUERY_STRING=%s", ri->query_string);
2166
2167  if ((s = mg_get_header(ri, "Content-Length")) != NULL)
2168    addenv(blk, "CONTENT_LENGTH=%s", s);
2169
2170  addenv2(blk, "PATH");
2171  addenv2(blk, "TMP");
2172  addenv2(blk, "TEMP");
2173  addenv2(blk, "TMPDIR");
2174  addenv2(blk, "PERLLIB");
2175  addenv2(blk, ENV_EXPORT_TO_CGI);
2176
2177#if defined(_WIN32)
2178  addenv2(blk, "COMSPEC");
2179  addenv2(blk, "SYSTEMROOT");
2180  addenv2(blk, "SystemDrive");
2181  addenv2(blk, "ProgramFiles");
2182  addenv2(blk, "ProgramFiles(x86)");
2183  addenv2(blk, "CommonProgramFiles(x86)");
2184#else
2185  addenv2(blk, "LD_LIBRARY_PATH");
2186#endif // _WIN32
2187
2188  // Add all headers as HTTP_* variables
2189  for (i = 0; i < ri->num_headers; i++) {
2190    p = addenv(blk, "HTTP_%s=%s",
2191        ri->http_headers[i].name, ri->http_headers[i].value);
2192
2193    // Convert variable name into uppercase, and change - to _
2194    for (; *p != '=' && *p != '\0'; p++) {
2195      if (*p == '-')
2196        *p = '_';
2197      *p = (char) toupper(* (unsigned char *) p);
2198    }
2199  }
2200
2201  blk->vars[blk->nvars++] = NULL;
2202  blk->buf[blk->len++] = '\0';
2203
2204  assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
2205  assert(blk->len > 0);
2206  assert(blk->len < (int) sizeof(blk->buf));
2207}
2208
2209static const char cgi_status[] = "HTTP/1.1 200 OK\r\n";
2210
2211static void open_cgi_endpoint(struct connection *conn, const char *prog) {
2212  struct cgi_env_block blk;
2213  char dir[MAX_PATH_SIZE];
2214  const char *p;
2215  sock_t fds[2];
2216
2217  prepare_cgi_environment(conn, prog, &blk);
2218  // CGI must be executed in its own directory. 'dir' must point to the
2219  // directory containing executable program, 'p' must point to the
2220  // executable program name relative to 'dir'.
2221  if ((p = strrchr(prog, '/')) == NULL) {
2222    mg_snprintf(dir, sizeof(dir), "%s", ".");
2223  } else {
2224    mg_snprintf(dir, sizeof(dir), "%.*s", (int) (p - prog), prog);
2225  }
2226
2227  // Try to create socketpair in a loop until success. ns_socketpair()
2228  // can be interrupted by a signal and fail.
2229  // TODO(lsm): use sigaction to restart interrupted syscall
2230  do {
2231    ns_socketpair(fds);
2232  } while (fds[0] == INVALID_SOCKET);
2233
2234  if (start_process(conn->server->config_options[CGI_INTERPRETER],
2235                    prog, blk.buf, blk.vars, dir, fds[1]) != 0) {
2236    conn->endpoint_type = EP_CGI;
2237    conn->endpoint.nc = ns_add_sock(&conn->server->ns_mgr, fds[0],
2238                                    mg_ev_handler, conn);
2239    conn->endpoint.nc->flags |= MG_CGI_CONN;
2240    ns_send(conn->ns_conn, cgi_status, sizeof(cgi_status) - 1);
2241    conn->mg_conn.status_code = 200;
2242    conn->ns_conn->flags |= NSF_BUFFER_BUT_DONT_SEND;
2243    // Pass POST data to the CGI process
2244    conn->endpoint.nc->send_iobuf = conn->ns_conn->recv_iobuf;
2245    iobuf_init(&conn->ns_conn->recv_iobuf, 0);
2246  } else {
2247    closesocket(fds[0]);
2248    send_http_error(conn, 500, "start_process(%s) failed", prog);
2249  }
2250
2251#ifndef _WIN32
2252  closesocket(fds[1]);  // On Windows, CGI stdio thread closes that socket
2253#endif
2254}
2255
2256static void on_cgi_data(struct ns_connection *nc) {
2257  struct connection *conn = (struct connection *) nc->user_data;
2258  const char *status = "500";
2259  struct mg_connection c;
2260
2261  if (!conn) return;
2262
2263  // Copy CGI data from CGI socket to the client send buffer
2264  ns_forward(nc, conn->ns_conn);
2265
2266  // If reply has not been parsed yet, parse it
2267  if (conn->ns_conn->flags & NSF_BUFFER_BUT_DONT_SEND) {
2268    struct iobuf *io = &conn->ns_conn->send_iobuf;
2269    int s_len = sizeof(cgi_status) - 1;
2270    int len = get_request_len(io->buf + s_len, io->len - s_len);
2271    char buf[MAX_REQUEST_SIZE], *s = buf;
2272
2273    if (len == 0) return;
2274
2275    if (len < 0 || len > (int) sizeof(buf)) {
2276      len = io->len;
2277      iobuf_remove(io, io->len);
2278      send_http_error(conn, 500, "CGI program sent malformed headers: [%.*s]",
2279        len, io->buf);
2280    } else {
2281      memset(&c, 0, sizeof(c));
2282      memcpy(buf, io->buf + s_len, len);
2283      buf[len - 1] = '\0';
2284      parse_http_headers(&s, &c);
2285      if (mg_get_header(&c, "Location") != NULL) {
2286        status = "302";
2287      } else if ((status = (char *) mg_get_header(&c, "Status")) == NULL) {
2288        status = "200";
2289      }
2290      memcpy(io->buf + 9, status, 3);
2291      conn->mg_conn.status_code = atoi(status);
2292    }
2293    conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
2294  }
2295}
2296#endif  // !MONGOOSE_NO_CGI
2297
2298static char *mg_strdup(const char *str) {
2299  char *copy = (char *) NS_MALLOC(strlen(str) + 1);
2300  if (copy != NULL) {
2301    strcpy(copy, str);
2302  }
2303  return copy;
2304}
2305
2306static int isbyte(int n) {
2307  return n >= 0 && n <= 255;
2308}
2309
2310static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
2311  int n, a, b, c, d, slash = 32, len = 0;
2312
2313  if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
2314      sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
2315      isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) &&
2316      slash >= 0 && slash < 33) {
2317    len = n;
2318    *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d;
2319    *mask = slash ? 0xffffffffU << (32 - slash) : 0;
2320  }
2321
2322  return len;
2323}
2324
2325// Verify given socket address against the ACL.
2326// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
2327static int check_acl(const char *acl, uint32_t remote_ip) {
2328  int allowed, flag;
2329  uint32_t net, mask;
2330  struct vec vec;
2331
2332  // If any ACL is set, deny by default
2333  allowed = acl == NULL ? '+' : '-';
2334
2335  while ((acl = next_option(acl, &vec, NULL)) != NULL) {
2336    flag = vec.ptr[0];
2337    if ((flag != '+' && flag != '-') ||
2338        parse_net(&vec.ptr[1], &net, &mask) == 0) {
2339      return -1;
2340    }
2341
2342    if (net == (remote_ip & mask)) {
2343      allowed = flag;
2344    }
2345  }
2346
2347  return allowed == '+';
2348}
2349
2350// Protect against directory disclosure attack by removing '..',
2351// excessive '/' and '\' characters
2352static void remove_double_dots_and_double_slashes(char *s) {
2353  char *p = s;
2354
2355  while (*s != '\0') {
2356    *p++ = *s++;
2357    if (s[-1] == '/' || s[-1] == '\\') {
2358      // Skip all following slashes, backslashes and double-dots
2359      while (s[0] != '\0') {
2360        if (s[0] == '/' || s[0] == '\\') { s++; }
2361        else if (s[0] == '.' && s[1] == '.') { s += 2; }
2362        else { break; }
2363      }
2364    }
2365  }
2366  *p = '\0';
2367}
2368
2369int mg_url_decode(const char *src, int src_len, char *dst,
2370                  int dst_len, int is_form_url_encoded) {
2371  int i, j, a, b;
2372#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
2373
2374  for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
2375    if (src[i] == '%' && i < src_len - 2 &&
2376        isxdigit(* (const unsigned char *) (src + i + 1)) &&
2377        isxdigit(* (const unsigned char *) (src + i + 2))) {
2378      a = tolower(* (const unsigned char *) (src + i + 1));
2379      b = tolower(* (const unsigned char *) (src + i + 2));
2380      dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
2381      i += 2;
2382    } else if (is_form_url_encoded && src[i] == '+') {
2383      dst[j] = ' ';
2384    } else {
2385      dst[j] = src[i];
2386    }
2387  }
2388
2389  dst[j] = '\0'; // Null-terminate the destination
2390
2391  return i >= src_len ? j : -1;
2392}
2393
2394static int is_valid_http_method(const char *s) {
2395  return !strcmp(s, "GET") || !strcmp(s, "POST") || !strcmp(s, "HEAD") ||
2396    !strcmp(s, "CONNECT") || !strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
2397    !strcmp(s, "OPTIONS") || !strcmp(s, "PROPFIND") || !strcmp(s, "MKCOL");
2398}
2399
2400// Parse HTTP request, fill in mg_request structure.
2401// This function modifies the buffer by NUL-terminating
2402// HTTP request components, header names and header values.
2403// Note that len must point to the last \n of HTTP headers.
2404static int parse_http_message(char *buf, int len, struct mg_connection *ri) {
2405  int is_request, n;
2406
2407  // Reset the connection. Make sure that we don't touch fields that are
2408  // set elsewhere: remote_ip, remote_port, server_param
2409  ri->request_method = ri->uri = ri->http_version = ri->query_string = NULL;
2410  ri->num_headers = ri->status_code = ri->is_websocket = ri->content_len = 0;
2411
2412  buf[len - 1] = '\0';
2413
2414  // RFC says that all initial whitespaces should be ingored
2415  while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
2416    buf++;
2417  }
2418  ri->request_method = skip(&buf, " ");
2419  ri->uri = skip(&buf, " ");
2420  ri->http_version = skip(&buf, "\r\n");
2421
2422  // HTTP message could be either HTTP request or HTTP response, e.g.
2423  // "GET / HTTP/1.0 ...." or  "HTTP/1.0 200 OK ..."
2424  is_request = is_valid_http_method(ri->request_method);
2425  if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) ||
2426      (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) {
2427    len = -1;
2428  } else {
2429    if (is_request) {
2430      ri->http_version += 5;
2431    } else {
2432      ri->status_code = atoi(ri->uri);
2433    }
2434    parse_http_headers(&buf, ri);
2435
2436    if ((ri->query_string = strchr(ri->uri, '?')) != NULL) {
2437      *(char *) ri->query_string++ = '\0';
2438    }
2439    n = (int) strlen(ri->uri);
2440    mg_url_decode(ri->uri, n, (char *) ri->uri, n + 1, 0);
2441    if (*ri->uri == '/' || *ri->uri == '.') {
2442      remove_double_dots_and_double_slashes((char *) ri->uri);
2443    }
2444  }
2445
2446  return len;
2447}
2448
2449static int lowercase(const char *s) {
2450  return tolower(* (const unsigned char *) s);
2451}
2452
2453static int mg_strcasecmp(const char *s1, const char *s2) {
2454  int diff;
2455
2456  do {
2457    diff = lowercase(s1++) - lowercase(s2++);
2458  } while (diff == 0 && s1[-1] != '\0');
2459
2460  return diff;
2461}
2462
2463static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
2464  int diff = 0;
2465
2466  if (len > 0)
2467    do {
2468      diff = lowercase(s1++) - lowercase(s2++);
2469    } while (diff == 0 && s1[-1] != '\0' && --len > 0);
2470
2471  return diff;
2472}
2473
2474// Return HTTP header value, or NULL if not found.
2475const char *mg_get_header(const struct mg_connection *ri, const char *s) {
2476  int i;
2477
2478  for (i = 0; i < ri->num_headers; i++)
2479    if (!mg_strcasecmp(s, ri->http_headers[i].name))
2480      return ri->http_headers[i].value;
2481
2482  return NULL;
2483}
2484
2485// Perform case-insensitive match of string against pattern
2486int mg_match_prefix(const char *pattern, int pattern_len, const char *str) {
2487  const char *or_str;
2488  int len, res, i = 0, j = 0;
2489
2490  if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) {
2491    res = mg_match_prefix(pattern, or_str - pattern, str);
2492    return res > 0 ? res : mg_match_prefix(or_str + 1,
2493      (pattern + pattern_len) - (or_str + 1), str);
2494  }
2495
2496  for (; i < pattern_len; i++, j++) {
2497    if (pattern[i] == '?' && str[j] != '\0') {
2498      continue;
2499    } else if (pattern[i] == '$') {
2500      return str[j] == '\0' ? j : -1;
2501    } else if (pattern[i] == '*') {
2502      i++;
2503      if (pattern[i] == '*') {
2504        i++;
2505        len = (int) strlen(str + j);
2506      } else {
2507        len = (int) strcspn(str + j, "/");
2508      }
2509      if (i == pattern_len) {
2510        return j + len;
2511      }
2512      do {
2513        res = mg_match_prefix(pattern + i, pattern_len - i, str + j + len);
2514      } while (res == -1 && len-- > 0);
2515      return res == -1 ? -1 : j + res + len;
2516    } else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
2517      return -1;
2518    }
2519  }
2520  return j;
2521}
2522
2523// This function prints HTML pages, and expands "{{something}}" blocks
2524// inside HTML by calling appropriate callback functions.
2525// Note that {{@path/to/file}} construct outputs embedded file's contents,
2526// which provides SSI-like functionality.
2527void mg_template(struct mg_connection *conn, const char *s,
2528                 struct mg_expansion *expansions) {
2529  int i, j, pos = 0, inside_marker = 0;
2530
2531  for (i = 0; s[i] != '\0'; i++) {
2532    if (inside_marker == 0 && !memcmp(&s[i], "{{", 2)) {
2533      if (i > pos) {
2534        mg_send_data(conn, &s[pos], i - pos);
2535      }
2536      pos = i;
2537      inside_marker = 1;
2538    }
2539    if (inside_marker == 1 && !memcmp(&s[i], "}}", 2)) {
2540      for (j = 0; expansions[j].keyword != NULL; j++) {
2541        const char *kw = expansions[j].keyword;
2542        if ((int) strlen(kw) == i - (pos + 2) &&
2543            memcmp(kw, &s[pos + 2], i - (pos + 2)) == 0) {
2544          expansions[j].handler(conn);
2545          pos = i + 2;
2546          break;
2547        }
2548      }
2549      inside_marker = 0;
2550    }
2551  }
2552  if (i > pos) {
2553    mg_send_data(conn, &s[pos], i - pos);
2554  }
2555}
2556
2557#ifndef MONGOOSE_NO_FILESYSTEM
2558static int must_hide_file(struct connection *conn, const char *path) {
2559  const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
2560  const char *pattern = conn->server->config_options[HIDE_FILES_PATTERN];
2561  return mg_match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
2562    (pattern != NULL && mg_match_prefix(pattern, strlen(pattern), path) > 0);
2563}
2564
2565// Return 1 if real file has been found, 0 otherwise
2566static int convert_uri_to_file_name(struct connection *conn, char *buf,
2567                                    size_t buf_len, file_stat_t *st) {
2568  struct vec a, b;
2569  const char *rewrites = conn->server->config_options[URL_REWRITES];
2570  const char *root = conn->server->config_options[DOCUMENT_ROOT];
2571#ifndef MONGOOSE_NO_CGI
2572  const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
2573  char *p;
2574#endif
2575  const char *uri = conn->mg_conn.uri;
2576  const char *domain = mg_get_header(&conn->mg_conn, "Host");
2577  int match_len, root_len = root == NULL ? 0 : strlen(root);
2578
2579  // Perform virtual hosting rewrites
2580  if (rewrites != NULL && domain != NULL) {
2581    const char *colon = strchr(domain, ':');
2582    int domain_len = colon == NULL ? (int) strlen(domain) : colon - domain;
2583
2584    while ((rewrites = next_option(rewrites, &a, &b)) != NULL) {
2585      if (a.len > 1 && a.ptr[0] == '@' && a.len == domain_len + 1 &&
2586          mg_strncasecmp(a.ptr + 1, domain, domain_len) == 0) {
2587        root = b.ptr;
2588        root_len = b.len;
2589        break;
2590      }
2591    }
2592  }
2593
2594  // No filesystem access
2595  if (root == NULL || root_len == 0) return 0;
2596
2597  // Handle URL rewrites
2598  mg_snprintf(buf, buf_len, "%.*s%s", root_len, root, uri);
2599  rewrites = conn->server->config_options[URL_REWRITES];  // Re-initialize!
2600  while ((rewrites = next_option(rewrites, &a, &b)) != NULL) {
2601    if ((match_len = mg_match_prefix(a.ptr, a.len, uri)) > 0) {
2602      mg_snprintf(buf, buf_len, "%.*s%s", (int) b.len, b.ptr, uri + match_len);
2603      break;
2604    }
2605  }
2606
2607  if (stat(buf, st) == 0) return 1;
2608
2609#ifndef MONGOOSE_NO_CGI
2610  // Support PATH_INFO for CGI scripts.
2611  for (p = buf + strlen(root) + 2; *p != '\0'; p++) {
2612    if (*p == '/') {
2613      *p = '\0';
2614      if (mg_match_prefix(cgi_pat, strlen(cgi_pat), buf) > 0 &&
2615          !stat(buf, st)) {
2616      DBG(("!!!! [%s]", buf));
2617        *p = '/';
2618        conn->path_info = mg_strdup(p);
2619        *p = '\0';
2620        return 1;
2621      }
2622      *p = '/';
2623    }
2624  }
2625#endif
2626
2627  return 0;
2628}
2629#endif  // MONGOOSE_NO_FILESYSTEM
2630
2631static int should_keep_alive(const struct mg_connection *conn) {
2632  struct connection *c = MG_CONN_2_CONN(conn);
2633  const char *method = conn->request_method;
2634  const char *http_version = conn->http_version;
2635  const char *header = mg_get_header(conn, "Connection");
2636  return method != NULL &&
2637    (!strcmp(method, "GET") || c->endpoint_type == EP_USER) &&
2638    ((header != NULL && !mg_strcasecmp(header, "keep-alive")) ||
2639     (header == NULL && http_version && !strcmp(http_version, "1.1")));
2640}
2641
2642size_t mg_write(struct mg_connection *c, const void *buf, int len) {
2643  struct connection *conn = MG_CONN_2_CONN(c);
2644  ns_send(conn->ns_conn, buf, len);
2645  return conn->ns_conn->send_iobuf.len;
2646}
2647
2648void mg_send_status(struct mg_connection *c, int status) {
2649  if (c->status_code == 0) {
2650    c->status_code = status;
2651    mg_printf(c, "HTTP/1.1 %d %s\r\n", status, status_code_to_str(status));
2652  }
2653}
2654
2655void mg_send_header(struct mg_connection *c, const char *name, const char *v) {
2656  if (c->status_code == 0) {
2657    c->status_code = 200;
2658    mg_printf(c, "HTTP/1.1 %d %s\r\n", 200, status_code_to_str(200));
2659  }
2660  mg_printf(c, "%s: %s\r\n", name, v);
2661}
2662
2663static void terminate_headers(struct mg_connection *c) {
2664  struct connection *conn = MG_CONN_2_CONN(c);
2665  if (!(conn->ns_conn->flags & MG_HEADERS_SENT)) {
2666    mg_send_header(c, "Transfer-Encoding", "chunked");
2667    mg_write(c, "\r\n", 2);
2668    conn->ns_conn->flags |= MG_HEADERS_SENT;
2669  }
2670}
2671
2672size_t mg_send_data(struct mg_connection *c, const void *data, int data_len) {
2673  struct connection *conn = MG_CONN_2_CONN(c);
2674  terminate_headers(c);
2675  write_chunk(MG_CONN_2_CONN(c), (const char *) data, data_len);
2676  return conn->ns_conn->send_iobuf.len;
2677}
2678
2679size_t mg_printf_data(struct mg_connection *c, const char *fmt, ...) {
2680  struct connection *conn = MG_CONN_2_CONN(c);
2681  va_list ap;
2682  int len;
2683  char mem[IOBUF_SIZE], *buf = mem;
2684
2685  terminate_headers(c);
2686
2687  va_start(ap, fmt);
2688  len = ns_avprintf(&buf, sizeof(mem), fmt, ap);
2689  va_end(ap);
2690
2691  if (len >= 0) {
2692    write_chunk((struct connection *) conn, buf, len);
2693  }
2694  if (buf != mem && buf != NULL) {
2695    NS_FREE(buf);
2696  }
2697  return conn->ns_conn->send_iobuf.len;
2698}
2699
2700#if !defined(MONGOOSE_NO_WEBSOCKET) || !defined(MONGOOSE_NO_AUTH)
2701static int is_big_endian(void) {
2702  static const int n = 1;
2703  return ((char *) &n)[0] == 0;
2704}
2705#endif
2706
2707#ifndef MONGOOSE_NO_WEBSOCKET
2708// START OF SHA-1 code
2709// Copyright(c) By Steve Reid <steve@edmweb.com>
2710#define SHA1HANDSOFF
2711#if defined(__sun)
2712#include "solarisfixes.h"
2713#endif
2714
2715union char64long16 { unsigned char c[64]; uint32_t l[16]; };
2716
2717#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
2718
2719static uint32_t blk0(union char64long16 *block, int i) {
2720  // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN
2721  if (!is_big_endian()) {
2722    block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
2723      (rol(block->l[i], 8) & 0x00FF00FF);
2724  }
2725  return block->l[i];
2726}
2727
2728/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */
2729#undef blk
2730#undef R0
2731#undef R1
2732#undef R2
2733#undef R3
2734#undef R4
2735
2736#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
2737    ^block->l[(i+2)&15]^block->l[i&15],1))
2738#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
2739#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
2740#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
2741#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
2742#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
2743
2744typedef struct {
2745    uint32_t state[5];
2746    uint32_t count[2];
2747    unsigned char buffer[64];
2748} SHA1_CTX;
2749
2750static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) {
2751  uint32_t a, b, c, d, e;
2752  union char64long16 block[1];
2753
2754  memcpy(block, buffer, 64);
2755  a = state[0];
2756  b = state[1];
2757  c = state[2];
2758  d = state[3];
2759  e = state[4];
2760  R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
2761  R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
2762  R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
2763  R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
2764  R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
2765  R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
2766  R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
2767  R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
2768  R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
2769  R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
2770  R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
2771  R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
2772  R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
2773  R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
2774  R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
2775  R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
2776  R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
2777  R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
2778  R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
2779  R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
2780  state[0] += a;
2781  state[1] += b;
2782  state[2] += c;
2783  state[3] += d;
2784  state[4] += e;
2785  // Erase working structures. The order of operations is important,
2786  // used to ensure that compiler doesn't optimize those out.
2787  memset(block, 0, sizeof(block));
2788  a = b = c = d = e = 0;
2789  (void) a; (void) b; (void) c; (void) d; (void) e;
2790}
2791
2792static void SHA1Init(SHA1_CTX *context) {
2793  context->state[0] = 0x67452301;
2794  context->state[1] = 0xEFCDAB89;
2795  context->state[2] = 0x98BADCFE;
2796  context->state[3] = 0x10325476;
2797  context->state[4] = 0xC3D2E1F0;
2798  context->count[0] = context->count[1] = 0;
2799}
2800
2801static void SHA1Update(SHA1_CTX *context, const unsigned char *data,
2802                       uint32_t len) {
2803  uint32_t i, j;
2804
2805  j = context->count[0];
2806  if ((context->count[0] += len << 3) < j)
2807    context->count[1]++;
2808  context->count[1] += (len>>29);
2809  j = (j >> 3) & 63;
2810  if ((j + len) > 63) {
2811    memcpy(&context->buffer[j], data, (i = 64-j));
2812    SHA1Transform(context->state, context->buffer);
2813    for ( ; i + 63 < len; i += 64) {
2814      SHA1Transform(context->state, &data[i]);
2815    }
2816    j = 0;
2817  }
2818  else i = 0;
2819  memcpy(&context->buffer[j], &data[i], len - i);
2820}
2821
2822static void SHA1Final(unsigned char digest[20], SHA1_CTX *context) {
2823  unsigned i;
2824  unsigned char finalcount[8], c;
2825
2826  for (i = 0; i < 8; i++) {
2827    finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
2828                                     >> ((3-(i & 3)) * 8) ) & 255);
2829  }
2830  c = 0200;
2831  SHA1Update(context, &c, 1);
2832  while ((context->count[0] & 504) != 448) {
2833    c = 0000;
2834    SHA1Update(context, &c, 1);
2835  }
2836  SHA1Update(context, finalcount, 8);
2837  for (i = 0; i < 20; i++) {
2838    digest[i] = (unsigned char)
2839      ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
2840  }
2841  memset(context, '\0', sizeof(*context));
2842  memset(&finalcount, '\0', sizeof(finalcount));
2843}
2844// END OF SHA1 CODE
2845
2846static void base64_encode(const unsigned char *src, int src_len, char *dst) {
2847  static const char *b64 =
2848    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2849  int i, j, a, b, c;
2850
2851  for (i = j = 0; i < src_len; i += 3) {
2852    a = src[i];
2853    b = i + 1 >= src_len ? 0 : src[i + 1];
2854    c = i + 2 >= src_len ? 0 : src[i + 2];
2855
2856    dst[j++] = b64[a >> 2];
2857    dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
2858    if (i + 1 < src_len) {
2859      dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
2860    }
2861    if (i + 2 < src_len) {
2862      dst[j++] = b64[c & 63];
2863    }
2864  }
2865  while (j % 4 != 0) {
2866    dst[j++] = '=';
2867  }
2868  dst[j++] = '\0';
2869}
2870
2871static void send_websocket_handshake(struct mg_connection *conn,
2872                                     const char *key) {
2873  static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
2874  char buf[500], sha[20], b64_sha[sizeof(sha) * 2];
2875  SHA1_CTX sha_ctx;
2876
2877  mg_snprintf(buf, sizeof(buf), "%s%s", key, magic);
2878  SHA1Init(&sha_ctx);
2879  SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf));
2880  SHA1Final((unsigned char *) sha, &sha_ctx);
2881  base64_encode((unsigned char *) sha, sizeof(sha), b64_sha);
2882  mg_snprintf(buf, sizeof(buf), "%s%s%s",
2883              "HTTP/1.1 101 Switching Protocols\r\n"
2884              "Upgrade: websocket\r\n"
2885              "Connection: Upgrade\r\n"
2886              "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
2887
2888  mg_write(conn, buf, strlen(buf));
2889}
2890
2891static int deliver_websocket_frame(struct connection *conn) {
2892  // Having buf unsigned char * is important, as it is used below in arithmetic
2893  unsigned char *buf = (unsigned char *) conn->ns_conn->recv_iobuf.buf;
2894  int i, len, buf_len = conn->ns_conn->recv_iobuf.len, frame_len = 0,
2895      mask_len = 0, header_len = 0, data_len = 0, buffered = 0;
2896
2897  if (buf_len >= 2) {
2898    len = buf[1] & 127;
2899    mask_len = buf[1] & 128 ? 4 : 0;
2900    if (len < 126 && buf_len >= mask_len) {
2901      data_len = len;
2902      header_len = 2 + mask_len;
2903    } else if (len == 126 && buf_len >= 4 + mask_len) {
2904      header_len = 4 + mask_len;
2905      data_len = ((((int) buf[2]) << 8) + buf[3]);
2906    } else if (buf_len >= 10 + mask_len) {
2907      header_len = 10 + mask_len;
2908      data_len = (int) (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) +
2909        htonl(* (uint32_t *) &buf[6]);
2910    }
2911  }
2912
2913  frame_len = header_len + data_len;
2914  buffered = frame_len > 0 && frame_len <= buf_len;
2915
2916  if (buffered) {
2917    conn->mg_conn.content_len = data_len;
2918    conn->mg_conn.content = (char *) buf + header_len;
2919    conn->mg_conn.wsbits = buf[0];
2920
2921    // Apply mask if necessary
2922    if (mask_len > 0) {
2923      for (i = 0; i < data_len; i++) {
2924        buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4];
2925      }
2926    }
2927
2928    // Call the handler and remove frame from the iobuf
2929    if (call_user(conn, MG_REQUEST) == MG_FALSE) {
2930      conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
2931    }
2932    iobuf_remove(&conn->ns_conn->recv_iobuf, frame_len);
2933  }
2934
2935  return buffered;
2936}
2937
2938size_t mg_websocket_write(struct mg_connection *conn, int opcode,
2939                       const char *data, size_t data_len) {
2940    unsigned char mem[4192], *copy = mem;
2941    size_t copy_len = 0;
2942
2943    if (data_len + 10 > sizeof(mem) &&
2944        (copy = (unsigned char *) NS_MALLOC(data_len + 10)) == NULL) {
2945      return 0;
2946    }
2947
2948    copy[0] = 0x80 + (opcode & 0x0f);
2949
2950    // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2
2951    if (data_len < 126) {
2952      // Inline 7-bit length field
2953      copy[1] = data_len;
2954      memcpy(copy + 2, data, data_len);
2955      copy_len = 2 + data_len;
2956    } else if (data_len <= 0xFFFF) {
2957      // 16-bit length field
2958      copy[1] = 126;
2959      * (uint16_t *) (copy + 2) = (uint16_t) htons((uint16_t) data_len);
2960      memcpy(copy + 4, data, data_len);
2961      copy_len = 4 + data_len;
2962    } else {
2963      // 64-bit length field
2964      copy[1] = 127;
2965      const uint32_t hi = htonl((uint32_t) ((uint64_t) data_len >> 32));
2966      const uint32_t lo = htonl(data_len & 0xffffffff);
2967      memcpy(copy+2,&hi,sizeof(hi));
2968      memcpy(copy+6,&lo,sizeof(lo));
2969      memcpy(copy + 10, data, data_len);
2970      copy_len = 10 + data_len;
2971    }
2972
2973    if (copy_len > 0) {
2974      mg_write(conn, copy, copy_len);
2975    }
2976    if (copy != mem) {
2977      NS_FREE(copy);
2978    }
2979
2980    // If we send closing frame, schedule a connection to be closed after
2981    // data is drained to the client.
2982    if (opcode == WEBSOCKET_OPCODE_CONNECTION_CLOSE) {
2983      MG_CONN_2_CONN(conn)->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
2984    }
2985
2986    return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len;
2987}
2988
2989size_t mg_websocket_printf(struct mg_connection *conn, int opcode,
2990                           const char *fmt, ...) {
2991  char mem[4192], *buf = mem;
2992  va_list ap;
2993  int len;
2994
2995  va_start(ap, fmt);
2996  if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
2997    mg_websocket_write(conn, opcode, buf, len);
2998  }
2999  va_end(ap);
3000
3001  if (buf != mem && buf != NULL) {
3002    NS_FREE(buf);
3003  }
3004
3005  return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len;
3006}
3007
3008static void send_websocket_handshake_if_requested(struct mg_connection *conn) {
3009  const char *ver = mg_get_header(conn, "Sec-WebSocket-Version"),
3010        *key = mg_get_header(conn, "Sec-WebSocket-Key");
3011  if (ver != NULL && key != NULL) {
3012    conn->is_websocket = 1;
3013    if (call_user(MG_CONN_2_CONN(conn), MG_WS_HANDSHAKE) == MG_FALSE) {
3014      send_websocket_handshake(conn, key);
3015    }
3016    call_user(MG_CONN_2_CONN(conn), MG_WS_CONNECT);
3017  }
3018}
3019
3020static void ping_idle_websocket_connection(struct connection *conn, time_t t) {
3021  if (t - conn->ns_conn->last_io_time > MONGOOSE_USE_WEBSOCKET_PING_INTERVAL) {
3022    mg_websocket_write(&conn->mg_conn, WEBSOCKET_OPCODE_PING, "", 0);
3023  }
3024}
3025#else
3026#define ping_idle_websocket_connection(conn, t)
3027#endif // !MONGOOSE_NO_WEBSOCKET
3028
3029static void write_terminating_chunk(struct connection *conn) {
3030  mg_write(&conn->mg_conn, "0\r\n\r\n", 5);
3031}
3032
3033static int call_request_handler(struct connection *conn) {
3034  int result;
3035  conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf;
3036  if ((result = call_user(conn, MG_REQUEST)) == MG_TRUE) {
3037    if (conn->ns_conn->flags & MG_HEADERS_SENT) {
3038      write_terminating_chunk(conn);
3039    }
3040    close_local_endpoint(conn);
3041  }
3042  return result;
3043}
3044
3045const char *mg_get_mime_type(const char *path, const char *default_mime_type) {
3046  const char *ext;
3047  size_t i, path_len;
3048
3049  path_len = strlen(path);
3050
3051  for (i = 0; static_builtin_mime_types[i].extension != NULL; i++) {
3052    ext = path + (path_len - static_builtin_mime_types[i].ext_len);
3053    if (path_len > static_builtin_mime_types[i].ext_len &&
3054        mg_strcasecmp(ext, static_builtin_mime_types[i].extension) == 0) {
3055      return static_builtin_mime_types[i].mime_type;
3056    }
3057  }
3058
3059  return default_mime_type;
3060}
3061
3062#ifndef MONGOOSE_NO_FILESYSTEM
3063// Convert month to the month number. Return -1 on error, or month number
3064static int get_month_index(const char *s) {
3065  static const char *month_names[] = {
3066    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
3067    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
3068  };
3069  int i;
3070
3071  for (i = 0; i < (int) ARRAY_SIZE(month_names); i++)
3072    if (!strcmp(s, month_names[i]))
3073      return i;
3074
3075  return -1;
3076}
3077
3078static int num_leap_years(int year) {
3079  return year / 4 - year / 100 + year / 400;
3080}
3081
3082// Parse UTC date-time string, and return the corresponding time_t value.
3083static time_t parse_date_string(const char *datetime) {
3084  static const unsigned short days_before_month[] = {
3085    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
3086  };
3087  char month_str[32];
3088  int second, minute, hour, day, month, year, leap_days, days;
3089  time_t result = (time_t) 0;
3090
3091  if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
3092               &day, month_str, &year, &hour, &minute, &second) == 6) ||
3093       (sscanf(datetime, "%d %3s %d %d:%d:%d",
3094               &day, month_str, &year, &hour, &minute, &second) == 6) ||
3095       (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
3096               &day, month_str, &year, &hour, &minute, &second) == 6) ||
3097       (sscanf(datetime, "%d-%3s-%d %d:%d:%d",
3098               &day, month_str, &year, &hour, &minute, &second) == 6)) &&
3099      year > 1970 &&
3100      (month = get_month_index(month_str)) != -1) {
3101    leap_days = num_leap_years(year) - num_leap_years(1970);
3102    year -= 1970;
3103    days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
3104    result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
3105  }
3106
3107  return result;
3108}
3109
3110// Look at the "path" extension and figure what mime type it has.
3111// Store mime type in the vector.
3112static void get_mime_type(const struct mg_server *server, const char *path,
3113                          struct vec *vec) {
3114  struct vec ext_vec, mime_vec;
3115  const char *list, *ext;
3116  size_t path_len;
3117
3118  path_len = strlen(path);
3119
3120  // Scan user-defined mime types first, in case user wants to
3121  // override default mime types.
3122  list = server->config_options[EXTRA_MIME_TYPES];
3123  while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
3124    // ext now points to the path suffix
3125    ext = path + path_len - ext_vec.len;
3126    if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
3127      *vec = mime_vec;
3128      return;
3129    }
3130  }
3131
3132  vec->ptr = mg_get_mime_type(path, "text/plain");
3133  vec->len = strlen(vec->ptr);
3134}
3135
3136static const char *suggest_connection_header(const struct mg_connection *conn) {
3137  return should_keep_alive(conn) ? "keep-alive" : "close";
3138}
3139
3140static void construct_etag(char *buf, size_t buf_len, const file_stat_t *st) {
3141  mg_snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
3142              (unsigned long) st->st_mtime, (int64_t) st->st_size);
3143}
3144
3145// Return True if we should reply 304 Not Modified.
3146static int is_not_modified(const struct connection *conn,
3147                           const file_stat_t *stp) {
3148  char etag[64];
3149  const char *ims = mg_get_header(&conn->mg_conn, "If-Modified-Since");
3150  const char *inm = mg_get_header(&conn->mg_conn, "If-None-Match");
3151  construct_etag(etag, sizeof(etag), stp);
3152  return (inm != NULL && !mg_strcasecmp(etag, inm)) ||
3153    (ims != NULL && stp->st_mtime <= parse_date_string(ims));
3154}
3155
3156// For given directory path, substitute it to valid index file.
3157// Return 0 if index file has been found, -1 if not found.
3158// If the file is found, it's stats is returned in stp.
3159static int find_index_file(struct connection *conn, char *path,
3160                           size_t path_len, file_stat_t *stp) {
3161  const char *list = conn->server->config_options[INDEX_FILES];
3162  file_stat_t st;
3163  struct vec filename_vec;
3164  size_t n = strlen(path), found = 0;
3165
3166  // The 'path' given to us points to the directory. Remove all trailing
3167  // directory separator characters from the end of the path, and
3168  // then append single directory separator character.
3169  while (n > 0 && path[n - 1] == '/') {
3170    n--;
3171  }
3172  path[n] = '/';
3173
3174  // Traverse index files list. For each entry, append it to the given
3175  // path and see if the file exists. If it exists, break the loop
3176  while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
3177
3178    // Ignore too long entries that may overflow path buffer
3179    if (filename_vec.len > (int) (path_len - (n + 2)))
3180      continue;
3181
3182    // Prepare full path to the index file
3183    strncpy(path + n + 1, filename_vec.ptr, filename_vec.len);
3184    path[n + 1 + filename_vec.len] = '\0';
3185
3186    //DBG(("[%s]", path));
3187
3188    // Does it exist?
3189    if (!stat(path, &st)) {
3190      // Yes it does, break the loop
3191      *stp = st;
3192      found = 1;
3193      break;
3194    }
3195  }
3196
3197  // If no index file exists, restore directory path
3198  if (!found) {
3199    path[n] = '\0';
3200  }
3201
3202  return found;
3203}
3204
3205static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
3206  return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
3207}
3208
3209static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
3210  strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
3211}
3212
3213static void open_file_endpoint(struct connection *conn, const char *path,
3214                               file_stat_t *st, const char *extra_headers) {
3215  char date[64], lm[64], etag[64], range[64], headers[1000];
3216  const char *msg = "OK", *hdr;
3217  time_t curtime = time(NULL);
3218  int64_t r1, r2;
3219  struct vec mime_vec;
3220  int n;
3221
3222  conn->endpoint_type = EP_FILE;
3223  ns_set_close_on_exec(conn->endpoint.fd);
3224  conn->mg_conn.status_code = 200;
3225
3226  get_mime_type(conn->server, path, &mime_vec);
3227  conn->cl = st->st_size;
3228  range[0] = '\0';
3229
3230  // If Range: header specified, act accordingly
3231  r1 = r2 = 0;
3232  hdr = mg_get_header(&conn->mg_conn, "Range");
3233  if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
3234      r1 >= 0 && r2 >= 0) {
3235    conn->mg_conn.status_code = 206;
3236    conn->cl = n == 2 ? (r2 > conn->cl ? conn->cl : r2) - r1 + 1: conn->cl - r1;
3237    mg_snprintf(range, sizeof(range), "Content-Range: bytes "
3238                "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n",
3239                r1, r1 + conn->cl - 1, (int64_t) st->st_size);
3240    msg = "Partial Content";
3241    lseek(conn->endpoint.fd, r1, SEEK_SET);
3242  }
3243
3244  // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
3245  // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
3246  gmt_time_string(date, sizeof(date), &curtime);
3247  gmt_time_string(lm, sizeof(lm), &st->st_mtime);
3248  construct_etag(etag, sizeof(etag), st);
3249
3250  n = mg_snprintf(headers, sizeof(headers),
3251                  "HTTP/1.1 %d %s\r\n"
3252                  "Date: %s\r\n"
3253                  "Last-Modified: %s\r\n"
3254                  "Etag: %s\r\n"
3255                  "Content-Type: %.*s\r\n"
3256                  "Content-Length: %" INT64_FMT "\r\n"
3257                  "Connection: %s\r\n"
3258                  "Accept-Ranges: bytes\r\n"
3259                  "%s%s%s\r\n",
3260                  conn->mg_conn.status_code, msg, date, lm, etag,
3261                  (int) mime_vec.len, mime_vec.ptr, conn->cl,
3262                  suggest_connection_header(&conn->mg_conn),
3263                  range, extra_headers == NULL ? "" : extra_headers,
3264                  MONGOOSE_USE_EXTRA_HTTP_HEADERS);
3265  ns_send(conn->ns_conn, headers, n);
3266
3267  if (!strcmp(conn->mg_conn.request_method, "HEAD")) {
3268    conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
3269    close(conn->endpoint.fd);
3270    conn->endpoint_type = EP_NONE;
3271  }
3272}
3273
3274void mg_send_file_data(struct mg_connection *c, int fd) {
3275  struct connection *conn = MG_CONN_2_CONN(c);
3276  conn->endpoint_type = EP_FILE;
3277  conn->endpoint.fd = fd;
3278  ns_set_close_on_exec(conn->endpoint.fd);
3279}
3280#endif  // MONGOOSE_NO_FILESYSTEM
3281
3282static void call_request_handler_if_data_is_buffered(struct connection *conn) {
3283#ifndef MONGOOSE_NO_WEBSOCKET
3284  if (conn->mg_conn.is_websocket) {
3285    do { } while (deliver_websocket_frame(conn));
3286  } else
3287#endif
3288  if (conn->num_bytes_recv >= (conn->cl + conn->request_len) &&
3289      call_request_handler(conn) == MG_FALSE) {
3290    open_local_endpoint(conn, 1);
3291  }
3292}
3293
3294#if !defined(MONGOOSE_NO_DIRECTORY_LISTING) || !defined(MONGOOSE_NO_DAV)
3295
3296#ifdef _WIN32
3297struct dirent {
3298  char d_name[MAX_PATH_SIZE];
3299};
3300
3301typedef struct DIR {
3302  HANDLE   handle;
3303  WIN32_FIND_DATAW info;
3304  struct dirent result;
3305} DIR;
3306
3307// Implementation of POSIX opendir/closedir/readdir for Windows.
3308static DIR *opendir(const char *name) {
3309  DIR *dir = NULL;
3310  wchar_t wpath[MAX_PATH_SIZE];
3311  DWORD attrs;
3312
3313  if (name == NULL) {
3314    SetLastError(ERROR_BAD_ARGUMENTS);
3315  } else if ((dir = (DIR *) NS_MALLOC(sizeof(*dir))) == NULL) {
3316    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3317  } else {
3318    to_wchar(name, wpath, ARRAY_SIZE(wpath));
3319    attrs = GetFileAttributesW(wpath);
3320    if (attrs != 0xFFFFFFFF &&
3321        ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
3322      (void) wcscat(wpath, L"\\*");
3323      dir->handle = FindFirstFileW(wpath, &dir->info);
3324      dir->result.d_name[0] = '\0';
3325    } else {
3326      NS_FREE(dir);
3327      dir = NULL;
3328    }
3329  }
3330
3331  return dir;
3332}
3333
3334static int closedir(DIR *dir) {
3335  int result = 0;
3336
3337  if (dir != NULL) {
3338    if (dir->handle != INVALID_HANDLE_VALUE)
3339      result = FindClose(dir->handle) ? 0 : -1;
3340
3341    NS_FREE(dir);
3342  } else {
3343    result = -1;
3344    SetLastError(ERROR_BAD_ARGUMENTS);
3345  }
3346
3347  return result;
3348}
3349
3350static struct dirent *readdir(DIR *dir) {
3351  struct dirent *result = 0;
3352
3353  if (dir) {
3354    if (dir->handle != INVALID_HANDLE_VALUE) {
3355      result = &dir->result;
3356      (void) WideCharToMultiByte(CP_UTF8, 0,
3357          dir->info.cFileName, -1, result->d_name,
3358          sizeof(result->d_name), NULL, NULL);
3359
3360      if (!FindNextFileW(dir->handle, &dir->info)) {
3361        (void) FindClose(dir->handle);
3362        dir->handle = INVALID_HANDLE_VALUE;
3363      }
3364
3365    } else {
3366      SetLastError(ERROR_FILE_NOT_FOUND);
3367    }
3368  } else {
3369    SetLastError(ERROR_BAD_ARGUMENTS);
3370  }
3371
3372  return result;
3373}
3374#endif // _WIN32  POSIX opendir/closedir/readdir implementation
3375
3376static int scan_directory(struct connection *conn, const char *dir,
3377                          struct dir_entry **arr) {
3378  char path[MAX_PATH_SIZE];
3379  struct dir_entry *p;
3380  struct dirent *dp;
3381  int arr_size = 0, arr_ind = 0, inc = 100;
3382  DIR *dirp;
3383
3384  *arr = NULL;
3385  if ((dirp = (opendir(dir))) == NULL) return 0;
3386
3387  while ((dp = readdir(dirp)) != NULL) {
3388    // Do not show current dir and hidden files
3389    if (!strcmp(dp->d_name, ".") ||
3390        !strcmp(dp->d_name, "..") ||
3391        must_hide_file(conn, dp->d_name)) {
3392      continue;
3393    }
3394    mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
3395
3396    // Resize the array if nesessary
3397    if (arr_ind >= arr_size) {
3398      if ((p = (struct dir_entry *)
3399           NS_REALLOC(*arr, (inc + arr_size) * sizeof(**arr))) != NULL) {
3400        // Memset new chunk to zero, otherwize st_mtime will have garbage which
3401        // can make strftime() segfault, see
3402        // http://code.google.com/p/mongoose/issues/detail?id=79
3403        memset(p + arr_size, 0, sizeof(**arr) * inc);
3404
3405        *arr = p;
3406        arr_size += inc;
3407      }
3408    }
3409
3410    if (arr_ind < arr_size) {
3411      (*arr)[arr_ind].conn = conn;
3412      (*arr)[arr_ind].file_name = strdup(dp->d_name);
3413      stat(path, &(*arr)[arr_ind].st);
3414      arr_ind++;
3415    }
3416  }
3417  closedir(dirp);
3418
3419  return arr_ind;
3420}
3421
3422int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len) {
3423  static const char *dont_escape = "._-$,;~()";
3424  static const char *hex = "0123456789abcdef";
3425  size_t i = 0, j = 0;
3426
3427  for (i = j = 0; dst_len > 0 && i < s_len && j + 2 < dst_len - 1; i++, j++) {
3428    if (isalnum(* (const unsigned char *) (src + i)) ||
3429        strchr(dont_escape, * (const unsigned char *) (src + i)) != NULL) {
3430      dst[j] = src[i];
3431    } else if (j + 3 < dst_len) {
3432      dst[j] = '%';
3433      dst[j + 1] = hex[(* (const unsigned char *) (src + i)) >> 4];
3434      dst[j + 2] = hex[(* (const unsigned char *) (src + i)) & 0xf];
3435      j += 2;
3436    }
3437  }
3438
3439  dst[j] = '\0';
3440  return j;
3441}
3442#endif  // !NO_DIRECTORY_LISTING || !MONGOOSE_NO_DAV
3443
3444#ifndef MONGOOSE_NO_DIRECTORY_LISTING
3445
3446static void print_dir_entry(const struct dir_entry *de) {
3447  char size[64], mod[64], href[MAX_PATH_SIZE * 3];
3448  int64_t fsize = de->st.st_size;
3449  int is_dir = S_ISDIR(de->st.st_mode);
3450  const char *slash = is_dir ? "/" : "";
3451
3452  if (is_dir) {
3453    mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]");
3454  } else {
3455     // We use (signed) cast below because MSVC 6 compiler cannot
3456     // convert unsigned __int64 to double.
3457    if (fsize < 1024) {
3458      mg_snprintf(size, sizeof(size), "%d", (int) fsize);
3459    } else if (fsize < 0x100000) {
3460      mg_snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0);
3461    } else if (fsize < 0x40000000) {
3462      mg_snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576);
3463    } else {
3464      mg_snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824);
3465    }
3466  }
3467  strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.st_mtime));
3468  mg_url_encode(de->file_name, strlen(de->file_name), href, sizeof(href));
3469  mg_printf_data(&de->conn->mg_conn,
3470                  "<tr><td><a href=\"%s%s\">%s%s</a></td>"
3471                  "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
3472                  href, slash, de->file_name, slash, mod, size);
3473}
3474
3475// Sort directory entries by size, or name, or modification time.
3476// On windows, __cdecl specification is needed in case if project is built
3477// with __stdcall convention. qsort always requires __cdels callback.
3478static int __cdecl compare_dir_entries(const void *p1, const void *p2) {
3479  const struct dir_entry *a = (const struct dir_entry *) p1,
3480        *b = (const struct dir_entry *) p2;
3481  const char *qs = a->conn->mg_conn.query_string ?
3482    a->conn->mg_conn.query_string : "na";
3483  int cmp_result = 0;
3484
3485  if (S_ISDIR(a->st.st_mode) && !S_ISDIR(b->st.st_mode)) {
3486    return -1;  // Always put directories on top
3487  } else if (!S_ISDIR(a->st.st_mode) && S_ISDIR(b->st.st_mode)) {
3488    return 1;   // Always put directories on top
3489  } else if (*qs == 'n') {
3490    cmp_result = strcmp(a->file_name, b->file_name);
3491  } else if (*qs == 's') {
3492    cmp_result = a->st.st_size == b->st.st_size ? 0 :
3493      a->st.st_size > b->st.st_size ? 1 : -1;
3494  } else if (*qs == 'd') {
3495    cmp_result = a->st.st_mtime == b->st.st_mtime ? 0 :
3496      a->st.st_mtime > b->st.st_mtime ? 1 : -1;
3497  }
3498
3499  return qs[1] == 'd' ? -cmp_result : cmp_result;
3500}
3501
3502static void send_directory_listing(struct connection *conn, const char *dir) {
3503  struct dir_entry *arr = NULL;
3504  int i, num_entries, sort_direction = conn->mg_conn.query_string != NULL &&
3505    conn->mg_conn.query_string[1] == 'd' ? 'a' : 'd';
3506
3507  mg_send_header(&conn->mg_conn, "Transfer-Encoding", "chunked");
3508  mg_send_header(&conn->mg_conn, "Content-Type", "text/html; charset=utf-8");
3509
3510  mg_printf_data(&conn->mg_conn,
3511              "<html><head><title>Index of %s</title>"
3512              "<style>th {text-align: left;}</style></head>"
3513              "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
3514              "<tr><th><a href=\"?n%c\">Name</a></th>"
3515              "<th><a href=\"?d%c\">Modified</a></th>"
3516              "<th><a href=\"?s%c\">Size</a></th></tr>"
3517              "<tr><td colspan=\"3\"><hr></td></tr>",
3518              conn->mg_conn.uri, conn->mg_conn.uri,
3519              sort_direction, sort_direction, sort_direction);
3520
3521  num_entries = scan_directory(conn, dir, &arr);
3522  qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries);
3523  for (i = 0; i < num_entries; i++) {
3524    print_dir_entry(&arr[i]);
3525    NS_FREE(arr[i].file_name);
3526  }
3527  NS_FREE(arr);
3528
3529  write_terminating_chunk(conn);
3530  close_local_endpoint(conn);
3531}
3532#endif  // MONGOOSE_NO_DIRECTORY_LISTING
3533
3534#ifndef MONGOOSE_NO_DAV
3535static void print_props(struct connection *conn, const char *uri,
3536                        file_stat_t *stp) {
3537  char mtime[64];
3538
3539  gmt_time_string(mtime, sizeof(mtime), &stp->st_mtime);
3540  mg_printf(&conn->mg_conn,
3541      "<d:response>"
3542       "<d:href>%s</d:href>"
3543       "<d:propstat>"
3544        "<d:prop>"
3545         "<d:resourcetype>%s</d:resourcetype>"
3546         "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
3547         "<d:getlastmodified>%s</d:getlastmodified>"
3548        "</d:prop>"
3549        "<d:status>HTTP/1.1 200 OK</d:status>"
3550       "</d:propstat>"
3551      "</d:response>\n",
3552      uri, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "",
3553      (int64_t) stp->st_size, mtime);
3554}
3555
3556static void handle_propfind(struct connection *conn, const char *path,
3557                            file_stat_t *stp, int exists) {
3558  static const char header[] = "HTTP/1.1 207 Multi-Status\r\n"
3559    "Connection: close\r\n"
3560    "Content-Type: text/xml; charset=utf-8\r\n\r\n"
3561    "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
3562    "<d:multistatus xmlns:d='DAV:'>\n";
3563  static const char footer[] = "</d:multistatus>";
3564  const char *depth = mg_get_header(&conn->mg_conn, "Depth");
3565#ifdef MONGOOSE_NO_DIRECTORY_LISTING
3566  const char *list_dir = "no";
3567#else
3568  const char *list_dir = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
3569#endif
3570
3571  conn->mg_conn.status_code = 207;
3572
3573  // Print properties for the requested resource itself
3574  if (!exists) {
3575    conn->mg_conn.status_code = 404;
3576    mg_printf(&conn->mg_conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n");
3577  } else if (S_ISDIR(stp->st_mode) && mg_strcasecmp(list_dir, "yes") != 0) {
3578    conn->mg_conn.status_code = 403;
3579    mg_printf(&conn->mg_conn, "%s",
3580              "HTTP/1.1 403 Directory Listing Denied\r\n\r\n");
3581  } else {
3582    ns_send(conn->ns_conn, header, sizeof(header) - 1);
3583    print_props(conn, conn->mg_conn.uri, stp);
3584
3585    if (S_ISDIR(stp->st_mode) &&
3586             (depth == NULL || strcmp(depth, "0") != 0)) {
3587      struct dir_entry *arr = NULL;
3588      int i, num_entries = scan_directory(conn, path, &arr);
3589
3590      for (i = 0; i < num_entries; i++) {
3591        char buf[MAX_PATH_SIZE * 3];
3592        struct dir_entry *de = &arr[i];
3593        mg_url_encode(de->file_name, strlen(de->file_name), buf, sizeof(buf));
3594        print_props(conn, buf, &de->st);
3595        NS_FREE(de->file_name);
3596      }
3597      NS_FREE(arr);
3598    }
3599    ns_send(conn->ns_conn, footer, sizeof(footer) - 1);
3600  }
3601
3602  close_local_endpoint(conn);
3603}
3604
3605static void handle_mkcol(struct connection *conn, const char *path) {
3606  int status_code = 500;
3607
3608  if (conn->mg_conn.content_len > 0) {
3609    status_code = 415;
3610  } else if (!mkdir(path, 0755)) {
3611    status_code = 201;
3612  } else if (errno == EEXIST) {
3613    status_code = 405;
3614  } else if (errno == EACCES) {
3615    status_code = 403;
3616  } else if (errno == ENOENT) {
3617    status_code = 409;
3618  }
3619  send_http_error(conn, status_code, NULL);
3620}
3621
3622static int remove_directory(const char *dir) {
3623  char path[MAX_PATH_SIZE];
3624  struct dirent *dp;
3625  file_stat_t st;
3626  DIR *dirp;
3627
3628  if ((dirp = opendir(dir)) == NULL) return 0;
3629
3630  while ((dp = readdir(dirp)) != NULL) {
3631    if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
3632    mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
3633    stat(path, &st);
3634    if (S_ISDIR(st.st_mode)) {
3635      remove_directory(path);
3636    } else {
3637      remove(path);
3638    }
3639  }
3640  closedir(dirp);
3641  rmdir(dir);
3642
3643  return 1;
3644}
3645
3646static void handle_delete(struct connection *conn, const char *path) {
3647  file_stat_t st;
3648
3649  if (stat(path, &st) != 0) {
3650    send_http_error(conn, 404, NULL);
3651  } else if (S_ISDIR(st.st_mode)) {
3652    remove_directory(path);
3653    send_http_error(conn, 204, NULL);
3654  } else if (remove(path) == 0) {
3655    send_http_error(conn, 204, NULL);
3656  } else {
3657    send_http_error(conn, 423, NULL);
3658  }
3659}
3660
3661// For a given PUT path, create all intermediate subdirectories
3662// for given path. Return 0 if the path itself is a directory,
3663// or -1 on error, 1 if OK.
3664static int put_dir(const char *path) {
3665  char buf[MAX_PATH_SIZE];
3666  const char *s, *p;
3667  file_stat_t st;
3668
3669  // Create intermediate directories if they do not exist
3670  for (s = p = path + 1; (p = strchr(s, '/')) != NULL; s = ++p) {
3671    if (p - path >= (int) sizeof(buf)) return -1; // Buffer overflow
3672    memcpy(buf, path, p - path);
3673    buf[p - path] = '\0';
3674    if (stat(buf, &st) != 0 && mkdir(buf, 0755) != 0) return -1;
3675    if (p[1] == '\0') return 0;  // Path is a directory itself
3676  }
3677
3678  return 1;
3679}
3680
3681static void handle_put(struct connection *conn, const char *path) {
3682  file_stat_t st;
3683  const char *range, *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
3684  int64_t r1, r2;
3685  int rc;
3686
3687  conn->mg_conn.status_code = !stat(path, &st) ? 200 : 201;
3688  if ((rc = put_dir(path)) == 0) {
3689    mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\n\r\n",
3690              conn->mg_conn.status_code);
3691    close_local_endpoint(conn);
3692  } else if (rc == -1) {
3693    send_http_error(conn, 500, "put_dir: %s", strerror(errno));
3694  } else if (cl_hdr == NULL) {
3695    send_http_error(conn, 411, NULL);
3696  } else if ((conn->endpoint.fd =
3697              open(path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644)) < 0) {
3698    send_http_error(conn, 500, "open(%s): %s", path, strerror(errno));
3699  } else {
3700    DBG(("PUT [%s] %lu", path, (unsigned long) conn->ns_conn->recv_iobuf.len));
3701    conn->endpoint_type = EP_PUT;
3702    ns_set_close_on_exec(conn->endpoint.fd);
3703    range = mg_get_header(&conn->mg_conn, "Content-Range");
3704    conn->cl = to64(cl_hdr);
3705    r1 = r2 = 0;
3706    if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
3707      conn->mg_conn.status_code = 206;
3708      lseek(conn->endpoint.fd, r1, SEEK_SET);
3709      conn->cl = r2 > r1 ? r2 - r1 + 1: conn->cl - r1;
3710    }
3711    mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n",
3712              conn->mg_conn.status_code);
3713  }
3714}
3715
3716static void forward_put_data(struct connection *conn) {
3717  struct iobuf *io = &conn->ns_conn->recv_iobuf;
3718  size_t k = conn->cl < (int64_t) io->len ? conn->cl : (int64_t) io->len;   // To write
3719  int n = write(conn->endpoint.fd, io->buf, k);   // Write them!
3720  if (n > 0) {
3721    iobuf_remove(io, n);
3722    conn->cl -= n;
3723  }
3724  if (conn->cl <= 0) {
3725    close_local_endpoint(conn);
3726  }
3727}
3728#endif //  MONGOOSE_NO_DAV
3729
3730static void send_options(struct connection *conn) {
3731  conn->mg_conn.status_code = 200;
3732  mg_printf(&conn->mg_conn, "%s",
3733            "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, PUT, "
3734            "DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n");
3735  close_local_endpoint(conn);
3736}
3737
3738#ifndef MONGOOSE_NO_AUTH
3739void mg_send_digest_auth_request(struct mg_connection *c) {
3740  struct connection *conn = MG_CONN_2_CONN(c);
3741  c->status_code = 401;
3742  mg_printf(c,
3743            "HTTP/1.1 401 Unauthorized\r\n"
3744            "WWW-Authenticate: Digest qop=\"auth\", "
3745            "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
3746            conn->server->config_options[AUTH_DOMAIN],
3747            (unsigned long) time(NULL));
3748  close_local_endpoint(conn);
3749}
3750
3751// Use the global passwords file, if specified by auth_gpass option,
3752// or search for .htpasswd in the requested directory.
3753static FILE *open_auth_file(struct connection *conn, const char *path,
3754                            int is_directory) {
3755  char name[MAX_PATH_SIZE];
3756  const char *p, *gpass = conn->server->config_options[GLOBAL_AUTH_FILE];
3757  FILE *fp = NULL;
3758
3759  if (gpass != NULL) {
3760    // Use global passwords file
3761    fp = fopen(gpass, "r");
3762  } else if (is_directory) {
3763    mg_snprintf(name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME);
3764    fp = fopen(name, "r");
3765  } else {
3766    // Try to find .htpasswd in requested directory.
3767    if ((p = strrchr(path, '/')) == NULL) p = path;
3768    mg_snprintf(name, sizeof(name), "%.*s%c%s",
3769                (int) (p - path), path, '/', PASSWORDS_FILE_NAME);
3770    fp = fopen(name, "r");
3771  }
3772
3773  return fp;
3774}
3775
3776#if !defined(HAVE_MD5) && !defined(MONGOOSE_NO_AUTH)
3777/*
3778 * This code implements the MD5 message-digest algorithm.
3779 * The algorithm is due to Ron Rivest.   This code was
3780 * written by Colin Plumb in 1993, no copyright is claimed.
3781 * This code is in the public domain; do with it what you wish.
3782 *
3783 * Equivalent code is available from RSA Data Security, Inc.
3784 * This code has been tested against that, and is equivalent,
3785 * except that you don't need to include two pages of legalese
3786 * with every copy.
3787 *
3788 * To compute the message digest of a chunk of bytes, declare an
3789 * MD5Context structure, pass it to MD5Init, call MD5Update as
3790 * needed on buffers full of bytes, and then call MD5Final, which
3791 * will fill a supplied 16-byte array with the digest.
3792 */
3793
3794typedef struct MD5Context {
3795  uint32_t buf[4];
3796  uint32_t bits[2];
3797  unsigned char in[64];
3798} MD5_CTX;
3799
3800static void byteReverse(unsigned char *buf, unsigned longs) {
3801  uint32_t t;
3802
3803  // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
3804  if (is_big_endian()) {
3805    do {
3806      t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
3807        ((unsigned) buf[1] << 8 | buf[0]);
3808      * (uint32_t *) buf = t;
3809      buf += 4;
3810    } while (--longs);
3811  }
3812}
3813
3814#define F1(x, y, z) (z ^ (x & (y ^ z)))
3815#define F2(x, y, z) F1(z, x, y)
3816#define F3(x, y, z) (x ^ y ^ z)
3817#define F4(x, y, z) (y ^ (x | ~z))
3818
3819#define MD5STEP(f, w, x, y, z, data, s) \
3820  ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
3821
3822// Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
3823// initialization constants.
3824static void MD5Init(MD5_CTX *ctx) {
3825  ctx->buf[0] = 0x67452301;
3826  ctx->buf[1] = 0xefcdab89;
3827  ctx->buf[2] = 0x98badcfe;
3828  ctx->buf[3] = 0x10325476;
3829
3830  ctx->bits[0] = 0;
3831  ctx->bits[1] = 0;
3832}
3833
3834static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
3835  register uint32_t a, b, c, d;
3836
3837  a = buf[0];
3838  b = buf[1];
3839  c = buf[2];
3840  d = buf[3];
3841
3842  MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
3843  MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
3844  MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
3845  MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
3846  MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
3847  MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
3848  MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
3849  MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
3850  MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
3851  MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
3852  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
3853  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
3854  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
3855  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
3856  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
3857  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
3858
3859  MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
3860  MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
3861  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
3862  MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
3863  MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
3864  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
3865  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
3866  MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
3867  MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
3868  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
3869  MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
3870  MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
3871  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
3872  MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
3873  MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
3874  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
3875
3876  MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
3877  MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
3878  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
3879  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
3880  MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
3881  MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
3882  MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
3883  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
3884  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
3885  MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
3886  MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
3887  MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
3888  MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
3889  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
3890  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
3891  MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
3892
3893  MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
3894  MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
3895  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
3896  MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
3897  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
3898  MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
3899  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
3900  MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
3901  MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
3902  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
3903  MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
3904  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
3905  MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
3906  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
3907  MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
3908  MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
3909
3910  buf[0] += a;
3911  buf[1] += b;
3912  buf[2] += c;
3913  buf[3] += d;
3914}
3915
3916static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
3917  uint32_t t;
3918
3919  t = ctx->bits[0];
3920  if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
3921    ctx->bits[1]++;
3922  ctx->bits[1] += len >> 29;
3923
3924  t = (t >> 3) & 0x3f;
3925
3926  if (t) {
3927    unsigned char *p = (unsigned char *) ctx->in + t;
3928
3929    t = 64 - t;
3930    if (len < t) {
3931      memcpy(p, buf, len);
3932      return;
3933    }
3934    memcpy(p, buf, t);
3935    byteReverse(ctx->in, 16);
3936    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
3937    buf += t;
3938    len -= t;
3939  }
3940
3941  while (len >= 64) {
3942    memcpy(ctx->in, buf, 64);
3943    byteReverse(ctx->in, 16);
3944    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
3945    buf += 64;
3946    len -= 64;
3947  }
3948
3949  memcpy(ctx->in, buf, len);
3950}
3951
3952static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
3953  unsigned count;
3954  unsigned char *p;
3955  uint32_t *a;
3956
3957  count = (ctx->bits[0] >> 3) & 0x3F;
3958
3959  p = ctx->in + count;
3960  *p++ = 0x80;
3961  count = 64 - 1 - count;
3962  if (count < 8) {
3963    memset(p, 0, count);
3964    byteReverse(ctx->in, 16);
3965    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
3966    memset(ctx->in, 0, 56);
3967  } else {
3968    memset(p, 0, count - 8);
3969  }
3970  byteReverse(ctx->in, 14);
3971
3972  a = (uint32_t *)ctx->in;
3973  a[14] = ctx->bits[0];
3974  a[15] = ctx->bits[1];
3975
3976  MD5Transform(ctx->buf, (uint32_t *) ctx->in);
3977  byteReverse((unsigned char *) ctx->buf, 4);
3978  memcpy(digest, ctx->buf, 16);
3979  memset((char *) ctx, 0, sizeof(*ctx));
3980}
3981#endif // !HAVE_MD5
3982
3983
3984
3985// Stringify binary data. Output buffer must be twice as big as input,
3986// because each byte takes 2 bytes in string representation
3987static void bin2str(char *to, const unsigned char *p, size_t len) {
3988  static const char *hex = "0123456789abcdef";
3989
3990  for (; len--; p++) {
3991    *to++ = hex[p[0] >> 4];
3992    *to++ = hex[p[0] & 0x0f];
3993  }
3994  *to = '\0';
3995}
3996
3997// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
3998char *mg_md5(char buf[33], ...) {
3999  unsigned char hash[16];
4000  const char *p;
4001  va_list ap;
4002  MD5_CTX ctx;
4003
4004  MD5Init(&ctx);
4005
4006  va_start(ap, buf);
4007  while ((p = va_arg(ap, const char *)) != NULL) {
4008    MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
4009  }
4010  va_end(ap);
4011
4012  MD5Final(hash, &ctx);
4013  bin2str(buf, hash, sizeof(hash));
4014  return buf;
4015}
4016
4017// Check the user's password, return 1 if OK
4018static int check_password(const char *method, const char *ha1, const char *uri,
4019                          const char *nonce, const char *nc, const char *cnonce,
4020                          const char *qop, const char *response) {
4021  char ha2[32 + 1], expected_response[32 + 1];
4022
4023#if 0
4024  // Check for authentication timeout
4025  if ((unsigned long) time(NULL) - (unsigned long) to64(nonce) > 3600 * 2) {
4026    return 0;
4027  }
4028#endif
4029
4030  mg_md5(ha2, method, ":", uri, NULL);
4031  mg_md5(expected_response, ha1, ":", nonce, ":", nc,
4032      ":", cnonce, ":", qop, ":", ha2, NULL);
4033
4034  return mg_strcasecmp(response, expected_response) == 0 ? MG_TRUE : MG_FALSE;
4035}
4036
4037
4038// Authorize against the opened passwords file. Return 1 if authorized.
4039int mg_authorize_digest(struct mg_connection *c, FILE *fp) {
4040  struct connection *conn = MG_CONN_2_CONN(c);
4041  const char *hdr;
4042  char line[256], f_user[256], ha1[256], f_domain[256], user[100], nonce[100],
4043       uri[MAX_REQUEST_SIZE], cnonce[100], resp[100], qop[100], nc[100];
4044
4045  if (c == NULL || fp == NULL) return 0;
4046  if ((hdr = mg_get_header(c, "Authorization")) == NULL ||
4047      mg_strncasecmp(hdr, "Digest ", 7) != 0) return 0;
4048  if (!mg_parse_header(hdr, "username", user, sizeof(user))) return 0;
4049  if (!mg_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce))) return 0;
4050  if (!mg_parse_header(hdr, "response", resp, sizeof(resp))) return 0;
4051  if (!mg_parse_header(hdr, "uri", uri, sizeof(uri))) return 0;
4052  if (!mg_parse_header(hdr, "qop", qop, sizeof(qop))) return 0;
4053  if (!mg_parse_header(hdr, "nc", nc, sizeof(nc))) return 0;
4054  if (!mg_parse_header(hdr, "nonce", nonce, sizeof(nonce))) return 0;
4055
4056  while (fgets(line, sizeof(line), fp) != NULL) {
4057    if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) == 3 &&
4058        !strcmp(user, f_user) &&
4059        // NOTE(lsm): due to a bug in MSIE, we do not compare URIs
4060        !strcmp(conn->server->config_options[AUTH_DOMAIN], f_domain))
4061      return check_password(c->request_method, ha1, uri,
4062                            nonce, nc, cnonce, qop, resp);
4063  }
4064  return MG_FALSE;
4065}
4066
4067
4068// Return 1 if request is authorised, 0 otherwise.
4069static int is_authorized(struct connection *conn, const char *path,
4070                         int is_directory) {
4071  FILE *fp;
4072  int authorized = MG_TRUE;
4073
4074  if ((fp = open_auth_file(conn, path, is_directory)) != NULL) {
4075    authorized = mg_authorize_digest(&conn->mg_conn, fp);
4076    fclose(fp);
4077  }
4078
4079  return authorized;
4080}
4081
4082static int is_authorized_for_dav(struct connection *conn) {
4083  const char *auth_file = conn->server->config_options[DAV_AUTH_FILE];
4084  const char *method = conn->mg_conn.request_method;
4085  FILE *fp;
4086  int authorized = MG_FALSE;
4087
4088  // If dav_auth_file is not set, allow non-authorized PROPFIND
4089  if (method != NULL && !strcmp(method, "PROPFIND") && auth_file == NULL) {
4090    authorized = MG_TRUE;
4091  } else if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
4092    authorized = mg_authorize_digest(&conn->mg_conn, fp);
4093    fclose(fp);
4094  }
4095
4096  return authorized;
4097}
4098
4099static int is_dav_request(const struct connection *conn) {
4100  const char *s = conn->mg_conn.request_method;
4101  return !strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
4102    !strcmp(s, "MKCOL") || !strcmp(s, "PROPFIND");
4103}
4104#endif // MONGOOSE_NO_AUTH
4105
4106static int parse_header(const char *str, int str_len, const char *var_name,
4107                        char *buf, size_t buf_size) {
4108  int ch = ' ', ch1 = ',', len = 0, n = strlen(var_name);
4109  const char *p, *end = str + str_len, *s = NULL;
4110
4111  if (buf != NULL && buf_size > 0) buf[0] = '\0';
4112
4113  // Find where variable starts
4114  for (s = str; s != NULL && s + n < end; s++) {
4115    if ((s == str || s[-1] == ch || s[-1] == ch1) && s[n] == '=' &&
4116        !memcmp(s, var_name, n)) break;
4117  }
4118
4119  if (s != NULL && &s[n + 1] < end) {
4120    s += n + 1;
4121    if (*s == '"' || *s == '\'') ch = ch1 = *s++;
4122    p = s;
4123    while (p < end && p[0] != ch && p[0] != ch1 && len < (int) buf_size) {
4124      if (ch == ch1 && p[0] == '\\' && p[1] == ch) p++;
4125      buf[len++] = *p++;
4126    }
4127    if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
4128      len = 0;
4129    } else {
4130      if (len > 0 && s[len - 1] == ',') len--;
4131      if (len > 0 && s[len - 1] == ';') len--;
4132      buf[len] = '\0';
4133    }
4134  }
4135
4136  return len;
4137}
4138
4139int mg_parse_header(const char *s, const char *var_name, char *buf,
4140                    size_t buf_size) {
4141  return parse_header(s, s == NULL ? 0 : strlen(s), var_name, buf, buf_size);
4142}
4143
4144#ifndef MONGOOSE_NO_SSI
4145static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
4146
4147static void send_file_data(struct mg_connection *conn, FILE *fp) {
4148  char buf[IOBUF_SIZE];
4149  int n;
4150  while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
4151    mg_write(conn, buf, n);
4152  }
4153}
4154
4155static void do_ssi_include(struct mg_connection *conn, const char *ssi,
4156                           char *tag, int include_level) {
4157  char file_name[IOBUF_SIZE], path[MAX_PATH_SIZE], *p;
4158  char **opts = (MG_CONN_2_CONN(conn))->server->config_options;
4159  FILE *fp;
4160
4161  // sscanf() is safe here, since send_ssi_file() also uses buffer
4162  // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
4163  if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
4164    // File name is relative to the webserver root
4165    mg_snprintf(path, sizeof(path), "%s%c%s",
4166                opts[DOCUMENT_ROOT], '/', file_name);
4167  } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) {
4168    // File name is relative to the webserver working directory
4169    // or it is absolute system path
4170    mg_snprintf(path, sizeof(path), "%s", file_name);
4171  } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 ||
4172             sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
4173    // File name is relative to the currect document
4174    mg_snprintf(path, sizeof(path), "%s", ssi);
4175    if ((p = strrchr(path, '/')) != NULL) {
4176      p[1] = '\0';
4177    }
4178    mg_snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s",
4179                file_name);
4180  } else {
4181    mg_printf(conn, "Bad SSI #include: [%s]", tag);
4182    return;
4183  }
4184
4185  if ((fp = fopen(path, "rb")) == NULL) {
4186    mg_printf(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
4187              tag, path, strerror(errno));
4188  } else {
4189    ns_set_close_on_exec(fileno(fp));
4190    if (mg_match_prefix(opts[SSI_PATTERN], strlen(opts[SSI_PATTERN]),
4191        path) > 0) {
4192      send_ssi_file(conn, path, fp, include_level + 1);
4193    } else {
4194      send_file_data(conn, fp);
4195    }
4196    fclose(fp);
4197  }
4198}
4199
4200#ifndef MONGOOSE_NO_POPEN
4201static void do_ssi_exec(struct mg_connection *conn, char *tag) {
4202  char cmd[IOBUF_SIZE];
4203  FILE *fp;
4204
4205  if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
4206    mg_printf(conn, "Bad SSI #exec: [%s]", tag);
4207  } else if ((fp = popen(cmd, "r")) == NULL) {
4208    mg_printf(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(errno));
4209  } else {
4210    send_file_data(conn, fp);
4211    pclose(fp);
4212  }
4213}
4214#endif // !MONGOOSE_NO_POPEN
4215
4216static void send_ssi_file(struct mg_connection *conn, const char *path,
4217                          FILE *fp, int include_level) {
4218  char buf[IOBUF_SIZE];
4219  int ch, offset, len, in_ssi_tag;
4220
4221  if (include_level > 10) {
4222    mg_printf(conn, "SSI #include level is too deep (%s)", path);
4223    return;
4224  }
4225
4226  in_ssi_tag = len = offset = 0;
4227  while ((ch = fgetc(fp)) != EOF) {
4228    if (in_ssi_tag && ch == '>') {
4229      in_ssi_tag = 0;
4230      buf[len++] = (char) ch;
4231      buf[len] = '\0';
4232      assert(len <= (int) sizeof(buf));
4233      if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
4234        // Not an SSI tag, pass it
4235        (void) mg_write(conn, buf, (size_t) len);
4236      } else {
4237        if (!memcmp(buf + 5, "include", 7)) {
4238          do_ssi_include(conn, path, buf + 12, include_level);
4239#if !defined(MONGOOSE_NO_POPEN)
4240        } else if (!memcmp(buf + 5, "exec", 4)) {
4241          do_ssi_exec(conn, buf + 9);
4242#endif // !NO_POPEN
4243        } else {
4244          mg_printf(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
4245        }
4246      }
4247      len = 0;
4248    } else if (in_ssi_tag) {
4249      if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
4250        // Not an SSI tag
4251        in_ssi_tag = 0;
4252      } else if (len == (int) sizeof(buf) - 2) {
4253        mg_printf(conn, "%s: SSI tag is too large", path);
4254        len = 0;
4255      }
4256      buf[len++] = ch & 0xff;
4257    } else if (ch == '<') {
4258      in_ssi_tag = 1;
4259      if (len > 0) {
4260        mg_write(conn, buf, (size_t) len);
4261      }
4262      len = 0;
4263      buf[len++] = ch & 0xff;
4264    } else {
4265      buf[len++] = ch & 0xff;
4266      if (len == (int) sizeof(buf)) {
4267        mg_write(conn, buf, (size_t) len);
4268        len = 0;
4269      }
4270    }
4271  }
4272
4273  // Send the rest of buffered data
4274  if (len > 0) {
4275    mg_write(conn, buf, (size_t) len);
4276  }
4277}
4278
4279static void handle_ssi_request(struct connection *conn, const char *path) {
4280  FILE *fp;
4281  struct vec mime_vec;
4282
4283  if ((fp = fopen(path, "rb")) == NULL) {
4284    send_http_error(conn, 500, "fopen(%s): %s", path, strerror(errno));
4285  } else {
4286    ns_set_close_on_exec(fileno(fp));
4287    get_mime_type(conn->server, path, &mime_vec);
4288    conn->mg_conn.status_code = 200;
4289    mg_printf(&conn->mg_conn,
4290              "HTTP/1.1 %d OK\r\n"
4291              "Content-Type: %.*s\r\n"
4292              "Connection: close\r\n\r\n",
4293              conn->mg_conn.status_code, (int) mime_vec.len, mime_vec.ptr);
4294    send_ssi_file(&conn->mg_conn, path, fp, 0);
4295    fclose(fp);
4296    close_local_endpoint(conn);
4297  }
4298}
4299#endif
4300
4301static void proxy_request(struct ns_connection *pc, struct mg_connection *c) {
4302  int i, sent_close_header = 0;
4303
4304  ns_printf(pc, "%s %s%s%s HTTP/%s\r\n", c->request_method, c->uri,
4305            c->query_string ? "?" : "",
4306            c->query_string ? c->query_string : "",
4307            c->http_version);
4308  for (i = 0; i < c->num_headers; i++) {
4309    if (mg_strcasecmp(c->http_headers[i].name, "Connection") == 0) {
4310      // Force connection close, cause we don't parse proxy replies
4311      // therefore we don't know message boundaries
4312      ns_printf(pc, "%s: %s\r\n", "Connection", "close");
4313      sent_close_header = 1;
4314    } else {
4315      ns_printf(pc, "%s: %s\r\n", c->http_headers[i].name,
4316                c->http_headers[i].value);
4317    }
4318  }
4319  if (!sent_close_header) {
4320    ns_printf(pc, "%s: %s\r\n", "Connection", "close");
4321  }
4322  ns_printf(pc, "%s", "\r\n");
4323  ns_send(pc, c->content, c->content_len);
4324
4325}
4326
4327#ifdef NS_ENABLE_SSL
4328int mg_terminate_ssl(struct mg_connection *c, const char *cert) {
4329  static const char ok[] = "HTTP/1.0 200 OK\r\n\r\n";
4330  struct connection *conn = MG_CONN_2_CONN(c);
4331  SSL_CTX *ctx;
4332
4333  DBG(("%p MITM", conn));
4334  if ((ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) return 0;
4335
4336  SSL_CTX_use_certificate_file(ctx, cert, 1);
4337  SSL_CTX_use_PrivateKey_file(ctx, cert, 1);
4338  SSL_CTX_use_certificate_chain_file(ctx, cert);
4339
4340  // When clear-text reply is pushed to client, switch to SSL mode.
4341  // TODO(lsm): check for send() failure
4342  send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0);
4343  //DBG(("%p %lu %d SEND", c, (unsigned long) sizeof(ok) - 1, n));
4344  conn->ns_conn->send_iobuf.len = 0;
4345  conn->endpoint_type = EP_USER;  // To keep-alive in close_local_endpoint()
4346  close_local_endpoint(conn);     // Clean up current CONNECT request
4347  if ((conn->ns_conn->ssl = SSL_new(ctx)) != NULL) {
4348    SSL_set_fd(conn->ns_conn->ssl, conn->ns_conn->sock);
4349  }
4350  SSL_CTX_free(ctx);
4351  return 1;
4352}
4353#endif
4354
4355int mg_forward(struct mg_connection *c, const char *addr) {
4356  static const char ok[] = "HTTP/1.1 200 OK\r\n\r\n";
4357  struct connection *conn = MG_CONN_2_CONN(c);
4358  struct ns_connection *pc;
4359
4360  if ((pc = ns_connect(&conn->server->ns_mgr, addr,
4361      mg_ev_handler, conn)) == NULL) {
4362    conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
4363    return 0;
4364  }
4365
4366  // Interlink two connections
4367  pc->flags |= MG_PROXY_CONN;
4368  conn->endpoint_type = EP_PROXY;
4369  conn->endpoint.nc = pc;
4370  DBG(("%p [%s] [%s] -> %p %p", conn, c->uri, addr, pc, conn->ns_conn->ssl));
4371
4372  if (strcmp(c->request_method, "CONNECT") == 0) {
4373    // For CONNECT request, reply with 200 OK. Tunnel is established.
4374    // TODO(lsm): check for send() failure
4375    (void) send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0);
4376  } else {
4377    // Strip "http://host:port" part from the URI
4378    if (memcmp(c->uri, "http://", 7) == 0) c->uri += 7;
4379    while (*c->uri != '\0' && *c->uri != '/') c->uri++;
4380    proxy_request(pc, c);
4381  }
4382  return 1;
4383}
4384
4385static void proxify_connection(struct connection *conn) {
4386  char proto[10], host[500], cert[500], addr[1000];
4387  unsigned short port = 80;
4388  struct mg_connection *c = &conn->mg_conn;
4389  int n = 0;
4390  const char *url = c->uri;
4391
4392  proto[0] = host[0] = cert[0] = '\0';
4393  if (sscanf(url, "%499[^: ]:%hu%n", host, &port, &n) != 2 &&
4394      sscanf(url, "%9[a-z]://%499[^: ]:%hu%n", proto, host, &port, &n) != 3 &&
4395      sscanf(url, "%9[a-z]://%499[^/ ]%n", proto, host, &n) != 2) {
4396    n = 0;
4397  }
4398
4399  snprintf(addr, sizeof(addr), "%s://%s:%hu",
4400           conn->ns_conn->ssl != NULL ? "ssl" : "tcp", host, port);
4401  if (n <= 0 || !mg_forward(c, addr)) {
4402    conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
4403  }
4404}
4405
4406#ifndef MONGOOSE_NO_FILESYSTEM
4407void mg_send_file_internal(struct mg_connection *c, const char *file_name,
4408                           file_stat_t *st, int exists,
4409                           const char *extra_headers) {
4410  struct connection *conn = MG_CONN_2_CONN(c);
4411  char path[MAX_PATH_SIZE];
4412  const int is_directory = S_ISDIR(st->st_mode);
4413#ifndef MONGOOSE_NO_CGI
4414  const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
4415#else
4416  const char *cgi_pat = DEFAULT_CGI_PATTERN;
4417#endif
4418#ifndef MONGOOSE_NO_DIRECTORY_LISTING
4419  const char *dir_lst = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
4420#else
4421  const char *dir_lst = "yes";
4422#endif
4423
4424  mg_snprintf(path, sizeof(path), "%s", file_name);
4425
4426  if (!exists || must_hide_file(conn, path)) {
4427    send_http_error(conn, 404, NULL);
4428  } else if (is_directory &&
4429             conn->mg_conn.uri[strlen(conn->mg_conn.uri) - 1] != '/') {
4430    conn->mg_conn.status_code = 301;
4431    mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n"
4432              "Location: %s/\r\n\r\n", conn->mg_conn.uri);
4433    close_local_endpoint(conn);
4434  } else if (is_directory && !find_index_file(conn, path, sizeof(path), st)) {
4435    if (!mg_strcasecmp(dir_lst, "yes")) {
4436#ifndef MONGOOSE_NO_DIRECTORY_LISTING
4437      send_directory_listing(conn, path);
4438#else
4439      send_http_error(conn, 501, NULL);
4440#endif
4441    } else {
4442      send_http_error(conn, 403, NULL);
4443    }
4444  } else if (mg_match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
4445#if !defined(MONGOOSE_NO_CGI)
4446    open_cgi_endpoint(conn, path);
4447#else
4448    send_http_error(conn, 501, NULL);
4449#endif // !MONGOOSE_NO_CGI
4450#ifndef MONGOOSE_NO_SSI
4451  } else if (mg_match_prefix(conn->server->config_options[SSI_PATTERN],
4452                             strlen(conn->server->config_options[SSI_PATTERN]),
4453                             path) > 0) {
4454    handle_ssi_request(conn, path);
4455#endif
4456  } else if (is_not_modified(conn, st)) {
4457    send_http_error(conn, 304, NULL);
4458  } else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY, 0)) != -1) {
4459    // O_BINARY is required for Windows, otherwise in default text mode
4460    // two bytes \r\n will be read as one.
4461    open_file_endpoint(conn, path, st, extra_headers);
4462  } else {
4463    send_http_error(conn, 404, NULL);
4464  }
4465}
4466void mg_send_file(struct mg_connection *c, const char *file_name,
4467                  const char *extra_headers) {
4468  file_stat_t st;
4469  const int exists = stat(file_name, &st) == 0;
4470  mg_send_file_internal(c, file_name, &st, exists, extra_headers);
4471}
4472#endif  // !MONGOOSE_NO_FILESYSTEM
4473
4474static void open_local_endpoint(struct connection *conn, int skip_user) {
4475#ifndef MONGOOSE_NO_FILESYSTEM
4476  char path[MAX_PATH_SIZE];
4477  file_stat_t st;
4478  int exists = 0;
4479#endif
4480
4481  // If EP_USER was set in a prev call, reset it
4482  conn->endpoint_type = EP_NONE;
4483
4484#ifndef MONGOOSE_NO_AUTH
4485  if (conn->server->event_handler && call_user(conn, MG_AUTH) == MG_FALSE) {
4486    mg_send_digest_auth_request(&conn->mg_conn);
4487    return;
4488  }
4489#endif
4490
4491  // Call URI handler if one is registered for this URI
4492  if (skip_user == 0 && conn->server->event_handler != NULL) {
4493    conn->endpoint_type = EP_USER;
4494#if MONGOOSE_POST_SIZE_LIMIT > 1
4495    {
4496      const char *cl = mg_get_header(&conn->mg_conn, "Content-Length");
4497      if ((strcmp(conn->mg_conn.request_method, "POST") == 0 ||
4498           strcmp(conn->mg_conn.request_method, "PUT") == 0) &&
4499          (cl == NULL || to64(cl) > MONGOOSE_POST_SIZE_LIMIT)) {
4500        send_http_error(conn, 500, "POST size > %lu",
4501                        (unsigned long) MONGOOSE_POST_SIZE_LIMIT);
4502      }
4503    }
4504#endif
4505    return;
4506  }
4507
4508  if (strcmp(conn->mg_conn.request_method, "CONNECT") == 0 ||
4509      mg_strncasecmp(conn->mg_conn.uri, "http", 4) == 0) {
4510    const char *enp = conn->server->config_options[ENABLE_PROXY];
4511    if (enp == NULL || strcmp(enp, "yes") != 0) {
4512      send_http_error(conn, 405, NULL);
4513    } else {
4514      proxify_connection(conn);
4515    }
4516    return;
4517  }
4518
4519  if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
4520    send_options(conn);
4521    return;
4522  }
4523
4524#ifdef MONGOOSE_NO_FILESYSTEM
4525  send_http_error(conn, 404, NULL);
4526#else
4527  exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
4528
4529  if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
4530    send_options(conn);
4531  } else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) {
4532    send_http_error(conn, 404, NULL);
4533#ifndef MONGOOSE_NO_AUTH
4534  } else if ((!is_dav_request(conn) && !is_authorized(conn, path,
4535               exists && S_ISDIR(st.st_mode))) ||
4536             (is_dav_request(conn) && !is_authorized_for_dav(conn))) {
4537    mg_send_digest_auth_request(&conn->mg_conn);
4538    close_local_endpoint(conn);
4539#endif
4540#ifndef MONGOOSE_NO_DAV
4541  } else if (must_hide_file(conn, path)) {
4542    send_http_error(conn, 404, NULL);
4543  } else if (!strcmp(conn->mg_conn.request_method, "PROPFIND")) {
4544    handle_propfind(conn, path, &st, exists);
4545  } else if (!strcmp(conn->mg_conn.request_method, "MKCOL")) {
4546    handle_mkcol(conn, path);
4547  } else if (!strcmp(conn->mg_conn.request_method, "DELETE")) {
4548    handle_delete(conn, path);
4549  } else if (!strcmp(conn->mg_conn.request_method, "PUT")) {
4550    handle_put(conn, path);
4551#endif
4552  } else {
4553    mg_send_file_internal(&conn->mg_conn, path, &st, exists, NULL);
4554  }
4555#endif  // MONGOOSE_NO_FILESYSTEM
4556}
4557
4558static void send_continue_if_expected(struct connection *conn) {
4559  static const char expect_response[] = "HTTP/1.1 100 Continue\r\n\r\n";
4560  const char *expect_hdr = mg_get_header(&conn->mg_conn, "Expect");
4561
4562  if (expect_hdr != NULL && !mg_strcasecmp(expect_hdr, "100-continue")) {
4563    ns_send(conn->ns_conn, expect_response, sizeof(expect_response) - 1);
4564  }
4565}
4566
4567// Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
4568static int is_valid_uri(const char *uri) {
4569  unsigned short n;
4570  return uri[0] == '/' ||
4571    strcmp(uri, "*") == 0 ||            // OPTIONS method can use asterisk URI
4572    mg_strncasecmp(uri, "http", 4) == 0 || // Naive check for the absolute URI
4573    sscanf(uri, "%*[^ :]:%hu", &n) > 0; // CONNECT method can use host:port
4574}
4575
4576static void try_parse(struct connection *conn) {
4577  struct iobuf *io = &conn->ns_conn->recv_iobuf;
4578
4579  if (conn->request_len == 0 &&
4580      (conn->request_len = get_request_len(io->buf, io->len)) > 0) {
4581    // If request is buffered in, remove it from the iobuf. This is because
4582    // iobuf could be reallocated, and pointers in parsed request could
4583    // become invalid.
4584    conn->request = (char *) NS_MALLOC(conn->request_len);
4585    memcpy(conn->request, io->buf, conn->request_len);
4586    //DBG(("%p [%.*s]", conn, conn->request_len, conn->request));
4587    iobuf_remove(io, conn->request_len);
4588    conn->request_len = parse_http_message(conn->request, conn->request_len,
4589                                           &conn->mg_conn);
4590    if (conn->request_len > 0) {
4591      const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
4592      conn->cl = cl_hdr == NULL ? 0 : to64(cl_hdr);
4593      conn->mg_conn.content_len = (size_t) conn->cl;
4594    }
4595  }
4596}
4597
4598static void do_proxy(struct connection *conn) {
4599  if (0 && conn->request_len == 0) {
4600    try_parse(conn);
4601    DBG(("%p parsing -> %d", conn, conn->request_len));
4602    if (conn->request_len > 0 && call_user(conn, MG_REQUEST) == MG_FALSE) {
4603      proxy_request(conn->endpoint.nc, &conn->mg_conn);
4604    } else if (conn->request_len < 0) {
4605      ns_forward(conn->ns_conn, conn->endpoint.nc);
4606    }
4607  } else {
4608    DBG(("%p forwarding", conn));
4609    ns_forward(conn->ns_conn, conn->endpoint.nc);
4610  }
4611}
4612
4613static void on_recv_data(struct connection *conn) {
4614  struct iobuf *io = &conn->ns_conn->recv_iobuf;
4615  int n;
4616
4617  if (conn->endpoint_type == EP_PROXY) {
4618    if (conn->endpoint.nc != NULL) do_proxy(conn);
4619    return;
4620  }
4621
4622  try_parse(conn);
4623  DBG(("%p %d %lu %d", conn, conn->request_len, (unsigned long)io->len,
4624       conn->ns_conn->flags));
4625  if (conn->request_len < 0 ||
4626      (conn->request_len > 0 && !is_valid_uri(conn->mg_conn.uri))) {
4627    send_http_error(conn, 400, NULL);
4628  } else if (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE) {
4629    send_http_error(conn, 413, NULL);
4630  } else if (conn->request_len > 0 &&
4631             strcmp(conn->mg_conn.http_version, "1.0") != 0 &&
4632             strcmp(conn->mg_conn.http_version, "1.1") != 0) {
4633    send_http_error(conn, 505, NULL);
4634  } else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) {
4635#ifndef MONGOOSE_NO_WEBSOCKET
4636    send_websocket_handshake_if_requested(&conn->mg_conn);
4637#endif
4638    send_continue_if_expected(conn);
4639    open_local_endpoint(conn, 0);
4640  }
4641
4642#ifndef MONGOOSE_NO_CGI
4643  if (conn->endpoint_type == EP_CGI && conn->endpoint.nc != NULL) {
4644    ns_forward(conn->ns_conn, conn->endpoint.nc);
4645  }
4646#endif
4647  if (conn->endpoint_type == EP_USER) {
4648    conn->mg_conn.content = io->buf;
4649    conn->mg_conn.content_len = io->len;
4650    n = call_user(conn, MG_RECV);
4651    if (n < 0) {
4652      conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
4653    } else if ((size_t) n <= io->len) {
4654      iobuf_remove(io, n);
4655    }
4656    call_request_handler_if_data_is_buffered(conn);
4657  }
4658#ifndef MONGOOSE_NO_DAV
4659  if (conn->endpoint_type == EP_PUT && io->len > 0) {
4660    forward_put_data(conn);
4661  }
4662#endif
4663}
4664
4665static void call_http_client_handler(struct connection *conn) {
4666  //conn->mg_conn.status_code = code;
4667  // For responses without Content-Lengh, use the whole buffer
4668  if (conn->cl == 0) {
4669    conn->mg_conn.content_len = conn->ns_conn->recv_iobuf.len;
4670  }
4671  conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf;
4672  if (call_user(conn, MG_REPLY) == MG_FALSE) {
4673    conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
4674  }
4675  iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len);
4676  conn->mg_conn.status_code = 0;
4677  conn->cl = conn->num_bytes_recv = conn->request_len = 0;
4678  NS_FREE(conn->request);
4679  conn->request = NULL;
4680}
4681
4682static void process_response(struct connection *conn) {
4683  struct iobuf *io = &conn->ns_conn->recv_iobuf;
4684
4685  try_parse(conn);
4686  DBG(("%p %d %lu", conn, conn->request_len, (unsigned long)io->len));
4687  if (conn->request_len < 0 ||
4688      (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE)) {
4689    call_http_client_handler(conn);
4690  } else if ((int64_t) io->len >= conn->cl) {
4691    call_http_client_handler(conn);
4692  }
4693}
4694
4695struct mg_connection *mg_connect(struct mg_server *server, const char *addr) {
4696  struct ns_connection *nsconn;
4697  struct connection *conn;
4698
4699  nsconn = ns_connect(&server->ns_mgr, addr, mg_ev_handler, NULL);
4700  if (nsconn == NULL) return 0;
4701
4702  if ((conn = (struct connection *) NS_CALLOC(1, sizeof(*conn))) == NULL) {
4703    nsconn->flags |= NSF_CLOSE_IMMEDIATELY;
4704    return 0;
4705  }
4706
4707  // Interlink two structs
4708  conn->ns_conn = nsconn;
4709  nsconn->user_data = conn;
4710
4711  conn->server = server;
4712  conn->endpoint_type = EP_CLIENT;
4713  //conn->handler = handler;
4714  conn->mg_conn.server_param = server->ns_mgr.user_data;
4715  conn->ns_conn->flags = NSF_CONNECTING;
4716
4717  return &conn->mg_conn;
4718}
4719
4720#ifndef MONGOOSE_NO_LOGGING
4721static void log_header(const struct mg_connection *conn, const char *header,
4722                       FILE *fp) {
4723  const char *header_value;
4724
4725  if ((header_value = mg_get_header(conn, header)) == NULL) {
4726    (void) fprintf(fp, "%s", " -");
4727  } else {
4728    (void) fprintf(fp, " \"%s\"", header_value);
4729  }
4730}
4731
4732static void log_access(const struct connection *conn, const char *path) {
4733  const struct mg_connection *c = &conn->mg_conn;
4734  FILE *fp = (path == NULL) ?  NULL : fopen(path, "a+");
4735  char date[64], user[100];
4736  time_t now;
4737
4738  if (fp == NULL) return;
4739  now = time(NULL);
4740  strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", localtime(&now));
4741
4742  flockfile(fp);
4743  mg_parse_header(mg_get_header(&conn->mg_conn, "Authorization"), "username",
4744                  user, sizeof(user));
4745  fprintf(fp, "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d 0",
4746          c->remote_ip, user[0] == '\0' ? "-" : user, date,
4747          c->request_method ? c->request_method : "-",
4748          c->uri ? c->uri : "-", c->query_string ? "?" : "",
4749          c->query_string ? c->query_string : "",
4750          c->http_version, c->status_code);
4751  log_header(c, "Referer", fp);
4752  log_header(c, "User-Agent", fp);
4753  fputc('\n', fp);
4754  fflush(fp);
4755
4756  funlockfile(fp);
4757  fclose(fp);
4758}
4759#endif
4760
4761static void close_local_endpoint(struct connection *conn) {
4762  struct mg_connection *c = &conn->mg_conn;
4763  // Must be done before free()
4764  int keep_alive = should_keep_alive(&conn->mg_conn) &&
4765    (conn->endpoint_type == EP_FILE || conn->endpoint_type == EP_USER);
4766  DBG(("%p %d %d %d", conn, conn->endpoint_type, keep_alive,
4767       conn->ns_conn->flags));
4768
4769  switch (conn->endpoint_type) {
4770    case EP_PUT:
4771    case EP_FILE:
4772      close(conn->endpoint.fd);
4773      break;
4774    case EP_CGI:
4775    case EP_PROXY:
4776      if (conn->endpoint.nc != NULL) {
4777        DBG(("%p %p %p :-)", conn, conn->ns_conn, conn->endpoint.nc));
4778        conn->endpoint.nc->flags |= NSF_CLOSE_IMMEDIATELY;
4779        conn->endpoint.nc->user_data = NULL;
4780      }
4781      break;
4782    default: break;
4783  }
4784
4785#ifndef MONGOOSE_NO_LOGGING
4786  if (c->status_code > 0 && conn->endpoint_type != EP_CLIENT &&
4787      c->status_code != 400) {
4788    log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
4789  }
4790#endif
4791
4792  // Gobble possible POST data sent to the URI handler
4793  iobuf_free(&conn->ns_conn->recv_iobuf);
4794  NS_FREE(conn->request);
4795  NS_FREE(conn->path_info);
4796  conn->endpoint.nc = NULL;
4797  conn->request = conn->path_info = NULL;
4798
4799  conn->endpoint_type = EP_NONE;
4800  conn->cl = conn->num_bytes_recv = conn->request_len = 0;
4801  conn->ns_conn->flags &= ~(NSF_FINISHED_SENDING_DATA |
4802                            NSF_BUFFER_BUT_DONT_SEND | NSF_CLOSE_IMMEDIATELY |
4803                            MG_HEADERS_SENT | MG_LONG_RUNNING);
4804
4805  // Do not memset() the whole structure, as some of the fields
4806  // (IP addresses & ports, server_param) must survive. Nullify the rest.
4807  c->request_method = c->uri = c->http_version = c->query_string = NULL;
4808  c->num_headers = c->status_code = c->is_websocket = c->content_len = 0;
4809  c->connection_param = c->callback_param = NULL;
4810
4811  if (keep_alive) {
4812    on_recv_data(conn);  // Can call us recursively if pipelining is used
4813  } else {
4814    conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len == 0 ?
4815      NSF_CLOSE_IMMEDIATELY : NSF_FINISHED_SENDING_DATA;
4816  }
4817}
4818
4819static void transfer_file_data(struct connection *conn) {
4820  char buf[IOBUF_SIZE];
4821  int n;
4822
4823  // If output buffer is too big, don't send anything. Wait until
4824  // mongoose drains already buffered data to the client.
4825  if (conn->ns_conn->send_iobuf.len > sizeof(buf) * 2) return;
4826
4827  // Do not send anyt
4828  n = read(conn->endpoint.fd, buf, conn->cl < (int64_t) sizeof(buf) ?
4829           (int) conn->cl : (int) sizeof(buf));
4830
4831  if (n <= 0) {
4832    close_local_endpoint(conn);
4833  } else if (n > 0) {
4834    conn->cl -= n;
4835    ns_send(conn->ns_conn, buf, n);
4836    if (conn->cl <= 0) {
4837      close_local_endpoint(conn);
4838    }
4839  }
4840}
4841
4842int mg_poll_server(struct mg_server *server, int milliseconds) {
4843  return ns_mgr_poll(&server->ns_mgr, milliseconds);
4844}
4845
4846void mg_destroy_server(struct mg_server **server) {
4847  if (server != NULL && *server != NULL) {
4848    struct mg_server *s = *server;
4849    int i;
4850
4851    ns_mgr_free(&s->ns_mgr);
4852    for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) {
4853      NS_FREE(s->config_options[i]);  // It is OK to free(NULL)
4854    }
4855    NS_FREE(s);
4856    *server = NULL;
4857  }
4858}
4859
4860struct mg_connection *mg_next(struct mg_server *s, struct mg_connection *c) {
4861  struct ns_connection *nc = ns_next(&s->ns_mgr, c == NULL ? NULL :
4862                                     MG_CONN_2_CONN(c)->ns_conn);
4863  if (nc != NULL && nc->user_data != NULL) {
4864    return & ((struct connection *) nc->user_data)->mg_conn;
4865  } else {
4866    return NULL;
4867  }
4868}
4869
4870static int get_var(const char *data, size_t data_len, const char *name,
4871                   char *dst, size_t dst_len) {
4872  const char *p, *e, *s;
4873  size_t name_len;
4874  int len;
4875
4876  if (dst == NULL || dst_len == 0) {
4877    len = -2;
4878  } else if (data == NULL || name == NULL || data_len == 0) {
4879    len = -1;
4880    dst[0] = '\0';
4881  } else {
4882    name_len = strlen(name);
4883    e = data + data_len;
4884    len = -1;
4885    dst[0] = '\0';
4886
4887    // data is "var1=val1&var2=val2...". Find variable first
4888    for (p = data; p + name_len < e; p++) {
4889      if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
4890          !mg_strncasecmp(name, p, name_len)) {
4891
4892        // Point p to variable value
4893        p += name_len + 1;
4894
4895        // Point s to the end of the value
4896        s = (const char *) memchr(p, '&', (size_t)(e - p));
4897        if (s == NULL) {
4898          s = e;
4899        }
4900        assert(s >= p);
4901
4902        // Decode variable into destination buffer
4903        len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
4904
4905        // Redirect error code from -1 to -2 (destination buffer too small).
4906        if (len == -1) {
4907          len = -2;
4908        }
4909        break;
4910      }
4911    }
4912  }
4913
4914  return len;
4915}
4916
4917int mg_get_var(const struct mg_connection *conn, const char *name,
4918               char *dst, size_t dst_len) {
4919  int len = get_var(conn->query_string, conn->query_string == NULL ? 0 :
4920                    strlen(conn->query_string), name, dst, dst_len);
4921  if (len < 0) {
4922    len = get_var(conn->content, conn->content_len, name, dst, dst_len);
4923  }
4924  return len;
4925}
4926
4927static int get_line_len(const char *buf, int buf_len) {
4928  int len = 0;
4929  while (len < buf_len && buf[len] != '\n') len++;
4930  return buf[len] == '\n' ? len + 1: -1;
4931}
4932
4933int mg_parse_multipart(const char *buf, int buf_len,
4934                       char *var_name, int var_name_len,
4935                       char *file_name, int file_name_len,
4936                       const char **data, int *data_len) {
4937  static const char cd[] = "Content-Disposition: ";
4938  //struct mg_connection c;
4939  int hl, bl, n, ll, pos, cdl = sizeof(cd) - 1;
4940  //char *p;
4941
4942  if (buf == NULL || buf_len <= 0) return 0;
4943  if ((hl = get_request_len(buf, buf_len)) <= 0) return 0;
4944  if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0;
4945
4946  // Get boundary length
4947  bl = get_line_len(buf, buf_len);
4948
4949  // Loop through headers, fetch variable name and file name
4950  var_name[0] = file_name[0] = '\0';
4951  for (n = bl; (ll = get_line_len(buf + n, hl - n)) > 0; n += ll) {
4952    if (mg_strncasecmp(cd, buf + n, cdl) == 0) {
4953      parse_header(buf + n + cdl, ll - (cdl + 2), "name",
4954                   var_name, var_name_len);
4955      parse_header(buf + n + cdl, ll - (cdl + 2), "filename",
4956                   file_name, file_name_len);
4957    }
4958  }
4959
4960  // Scan body, search for terminating boundary
4961  for (pos = hl; pos + (bl - 2) < buf_len; pos++) {
4962    if (buf[pos] == '-' && !memcmp(buf, &buf[pos], bl - 2)) {
4963      if (data_len != NULL) *data_len = (pos - 2) - hl;
4964      if (data != NULL) *data = buf + hl;
4965      return pos;
4966    }
4967  }
4968
4969  return 0;
4970}
4971
4972const char **mg_get_valid_option_names(void) {
4973  return static_config_options;
4974}
4975
4976void mg_copy_listeners(struct mg_server *s, struct mg_server *to) {
4977  struct ns_connection *c;
4978  for (c = ns_next(&s->ns_mgr, NULL); c != NULL; c = ns_next(&s->ns_mgr, c)) {
4979    struct ns_connection *tmp;
4980    if ((c->flags & NSF_LISTENING) &&
4981        (tmp = (struct ns_connection *) NS_MALLOC(sizeof(*tmp))) != NULL) {
4982      memcpy(tmp, c, sizeof(*tmp));
4983      tmp->mgr = &to->ns_mgr;
4984      ns_add_conn(tmp->mgr, tmp);
4985    }
4986  }
4987}
4988
4989static int get_option_index(const char *name) {
4990  int i;
4991
4992  for (i = 0; static_config_options[i * 2] != NULL; i++) {
4993    if (strcmp(static_config_options[i * 2], name) == 0) {
4994      return i;
4995    }
4996  }
4997  return -1;
4998}
4999
5000static void set_default_option_values(char **opts) {
5001  const char *value, **all_opts = mg_get_valid_option_names();
5002  int i;
5003
5004  for (i = 0; all_opts[i * 2] != NULL; i++) {
5005    value = all_opts[i * 2 + 1];
5006    if (opts[i] == NULL && value != NULL) {
5007      opts[i] = mg_strdup(value);
5008    }
5009  }
5010}
5011
5012const char *mg_set_option(struct mg_server *server, const char *name,
5013                          const char *value) {
5014  int ind = get_option_index(name);
5015  const char *error_msg = NULL;
5016  char **v = NULL;
5017
5018  if (ind < 0) return  "No such option";
5019  v = &server->config_options[ind];
5020
5021  // Return success immediately if setting to the same value
5022  if ((*v == NULL && value == NULL) ||
5023      (value != NULL && *v != NULL && !strcmp(value, *v))) {
5024    return NULL;
5025  }
5026
5027  if (*v != NULL) {
5028    NS_FREE(*v);
5029    *v = NULL;
5030  }
5031
5032  if (value == NULL || value[0] == '\0') return NULL;
5033
5034  *v = mg_strdup(value);
5035  DBG(("%s [%s]", name, *v));
5036
5037  if (ind == LISTENING_PORT) {
5038    struct vec vec;
5039    while ((value = next_option(value, &vec, NULL)) != NULL) {
5040      struct ns_connection *c = ns_bind(&server->ns_mgr, vec.ptr,
5041                                        mg_ev_handler, NULL);
5042      if (c== NULL) {
5043        error_msg = "Cannot bind to port";
5044        break;
5045      } else {
5046        char buf[100];
5047        ns_sock_to_str(c->sock, buf, sizeof(buf), 2);
5048        NS_FREE(*v);
5049        *v = mg_strdup(buf);
5050      }
5051    }
5052#ifndef MONGOOSE_NO_FILESYSTEM
5053  } else if (ind == HEXDUMP_FILE) {
5054    server->ns_mgr.hexdump_file = *v;
5055#endif
5056#if !defined(_WIN32) && !defined(MONGOOSE_NO_USER)
5057  } else if (ind == RUN_AS_USER) {
5058    struct passwd *pw;
5059    if ((pw = getpwnam(value)) == NULL) {
5060      error_msg = "Unknown user";
5061    } else if (setgid(pw->pw_gid) != 0) {
5062      error_msg = "setgid() failed";
5063    } else if (setuid(pw->pw_uid) != 0) {
5064      error_msg = "setuid() failed";
5065    }
5066#endif
5067  }
5068
5069  return error_msg;
5070}
5071
5072static void set_ips(struct ns_connection *nc, int is_rem) {
5073  struct connection *conn = (struct connection *) nc->user_data;
5074  struct mg_connection *c = &conn->mg_conn;
5075  char buf[100];
5076
5077  ns_sock_to_str(nc->sock, buf, sizeof(buf), is_rem ? 7 : 3);
5078  sscanf(buf, "%47[^:]:%hu",
5079         is_rem ? c->remote_ip : c->local_ip,
5080         is_rem ? &c->remote_port : &c->local_port);
5081  //DBG(("%p %s %s", conn, is_rem ? "rem" : "loc", buf));
5082}
5083
5084static void on_accept(struct ns_connection *nc, union socket_address *sa) {
5085  struct mg_server *server = (struct mg_server *) nc->mgr;
5086  struct connection *conn;
5087
5088  if (!check_acl(server->config_options[ACCESS_CONTROL_LIST],
5089                 ntohl(* (uint32_t *) &sa->sin.sin_addr)) ||
5090      (conn = (struct connection *) NS_CALLOC(1, sizeof(*conn))) == NULL) {
5091    nc->flags |= NSF_CLOSE_IMMEDIATELY;
5092  } else {
5093    // Circularly link two connection structures
5094    nc->user_data = conn;
5095    conn->ns_conn = nc;
5096
5097    // Initialize the rest of connection attributes
5098    conn->server = server;
5099    conn->mg_conn.server_param = nc->mgr->user_data;
5100    set_ips(nc, 1);
5101    set_ips(nc, 0);
5102  }
5103}
5104
5105static void process_udp(struct ns_connection *nc) {
5106  struct iobuf *io = &nc->recv_iobuf;
5107  struct connection conn;
5108
5109  memset(&conn, 0, sizeof(conn));
5110  conn.ns_conn = nc;
5111  conn.server = (struct mg_server *) nc->mgr;
5112  conn.request_len = parse_http_message(io->buf, io->len, &conn.mg_conn);
5113  on_recv_data(&conn);
5114  //ns_printf(nc, "%s", "HTTP/1.0 200 OK\r\n\r\n");
5115}
5116
5117static void mg_ev_handler(struct ns_connection *nc, int ev, void *p) {
5118  struct connection *conn = (struct connection *) nc->user_data;
5119
5120  // Send NS event to the handler. Note that call_user won't send an event
5121  // if conn == NULL. Therefore, repeat this for NS_ACCEPT event as well.
5122#ifdef MONGOOSE_SEND_NS_EVENTS
5123  {
5124    struct connection *conn = (struct connection *) nc->user_data;
5125    void *param[2] = { nc, p };
5126    if (conn != NULL) conn->mg_conn.callback_param = param;
5127    call_user(conn, (enum mg_event) ev);
5128  }
5129#endif
5130
5131  switch (ev) {
5132    case NS_ACCEPT:
5133      on_accept(nc, (union socket_address *) p);
5134#ifdef MONGOOSE_SEND_NS_EVENTS
5135      {
5136        struct connection *conn = (struct connection *) nc->user_data;
5137        void *param[2] = { nc, p };
5138        if (conn != NULL) conn->mg_conn.callback_param = param;
5139        call_user(conn, (enum mg_event) ev);
5140      }
5141#endif
5142      break;
5143
5144    case NS_CONNECT:
5145      if (nc->user_data != NULL) {
5146        set_ips(nc, 1);
5147        set_ips(nc, 0);
5148      }
5149      conn->mg_conn.status_code = * (int *) p;
5150      if (conn->mg_conn.status_code != 0 ||
5151          (!(nc->flags & MG_PROXY_CONN) &&
5152           call_user(conn, MG_CONNECT) == MG_FALSE)) {
5153        nc->flags |= NSF_CLOSE_IMMEDIATELY;
5154      }
5155      break;
5156
5157    case NS_RECV:
5158      if (conn != NULL) {
5159        conn->num_bytes_recv += * (int *) p;
5160      }
5161
5162      if (nc->flags & NSF_UDP) {
5163        process_udp(nc);
5164      } else if (nc->listener != NULL) {
5165        on_recv_data(conn);
5166#ifndef MONGOOSE_NO_CGI
5167      } else if (nc->flags & MG_CGI_CONN) {
5168        on_cgi_data(nc);
5169#endif
5170      } else if (nc->flags & MG_PROXY_CONN) {
5171        if (conn != NULL) {
5172          ns_forward(nc, conn->ns_conn);
5173        }
5174      } else {
5175        process_response(conn);
5176      }
5177      break;
5178
5179    case NS_SEND:
5180      break;
5181
5182    case NS_CLOSE:
5183      nc->user_data = NULL;
5184      if (nc->flags & (MG_CGI_CONN | MG_PROXY_CONN)) {
5185        DBG(("%p %p closing cgi/proxy conn", conn, nc));
5186        if (conn && conn->ns_conn) {
5187          conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
5188          conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len > 0 ?
5189            NSF_FINISHED_SENDING_DATA : NSF_CLOSE_IMMEDIATELY;
5190          conn->endpoint.nc = NULL;
5191        }
5192      } else if (conn != NULL) {
5193        DBG(("%p %p %d closing", conn, nc, conn->endpoint_type));
5194
5195        if (conn->endpoint_type == EP_CLIENT && nc->recv_iobuf.len > 0) {
5196          call_http_client_handler(conn);
5197        }
5198
5199        call_user(conn, MG_CLOSE);
5200        close_local_endpoint(conn);
5201        conn->ns_conn = NULL;
5202        NS_FREE(conn);
5203      }
5204      break;
5205
5206    case NS_POLL:
5207      if (conn != NULL) {
5208        if (call_user(conn, MG_POLL) == MG_TRUE) {
5209          if (conn->ns_conn->flags & MG_HEADERS_SENT) {
5210            write_terminating_chunk(conn);
5211          }
5212          close_local_endpoint(conn);
5213        }
5214
5215        if (conn->endpoint_type == EP_FILE) {
5216          transfer_file_data(conn);
5217        }
5218      }
5219
5220      // Expire idle connections
5221      {
5222        time_t current_time = * (time_t *) p;
5223
5224        if (conn != NULL && conn->mg_conn.is_websocket) {
5225          ping_idle_websocket_connection(conn, current_time);
5226        }
5227
5228        if (nc->listener != NULL &&
5229            nc->last_io_time + MONGOOSE_IDLE_TIMEOUT_SECONDS < current_time) {
5230          mg_ev_handler(nc, NS_CLOSE, NULL);
5231          nc->flags |= NSF_CLOSE_IMMEDIATELY;
5232        }
5233      }
5234      break;
5235
5236    default:
5237      break;
5238  }
5239}
5240
5241static void iter2(struct ns_connection *nc, int ev, void *param) {
5242  mg_handler_t func = NULL;
5243  struct connection *conn = (struct connection *) nc->user_data;
5244  const char *msg = (const char *) param;
5245  int n;
5246  (void) ev;
5247
5248  //DBG(("%p [%s]", conn, msg));
5249  if (sscanf(msg, "%p %n", &func, &n) && func != NULL && conn != NULL) {
5250    conn->mg_conn.callback_param = (void *) (msg + n);
5251    func(&conn->mg_conn, MG_POLL);
5252  }
5253}
5254
5255void mg_wakeup_server_ex(struct mg_server *server, mg_handler_t cb,
5256                         const char *fmt, ...) {
5257  va_list ap;
5258  char buf[8 * 1024];
5259  int len;
5260
5261  // Encode callback (cb) into a buffer
5262  len = snprintf(buf, sizeof(buf), "%p ", cb);
5263  va_start(ap, fmt);
5264  len += vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
5265  va_end(ap);
5266
5267  // "len + 1" is to include terminating \0 in the message
5268  ns_broadcast(&server->ns_mgr, iter2, buf, len + 1);
5269}
5270
5271void mg_wakeup_server(struct mg_server *server) {
5272  ns_broadcast(&server->ns_mgr, NULL, (void *) "", 0);
5273}
5274
5275const char *mg_get_option(const struct mg_server *server, const char *name) {
5276  const char **opts = (const char **) server->config_options;
5277  int i = get_option_index(name);
5278  return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i];
5279}
5280
5281struct mg_server *mg_create_server(void *server_data, mg_handler_t handler) {
5282  struct mg_server *server = (struct mg_server *) NS_CALLOC(1, sizeof(*server));
5283  ns_mgr_init(&server->ns_mgr, server_data);
5284  set_default_option_values(server->config_options);
5285  server->event_handler = handler;
5286  return server;
5287}
trunk/3rdparty/mongoose/mongoose.h
r0r242829
1// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
2// Copyright (c) 2013-2014 Cesanta Software Limited
3// All rights reserved
4//
5// This software is dual-licensed: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License version 2 as
7// published by the Free Software Foundation. For the terms of this
8// license, see <http://www.gnu.org/licenses/>.
9//
10// You are free to use this software under the terms of the GNU General
11// Public License, but WITHOUT ANY WARRANTY; without even the implied
12// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13// See the GNU General Public License for more details.
14//
15// Alternatively, you can license this software under a commercial
16// license, as set out in <http://cesanta.com/>.
17
18#ifndef MONGOOSE_HEADER_INCLUDED
19#define  MONGOOSE_HEADER_INCLUDED
20
21#define MONGOOSE_VERSION "5.6"
22
23#include <stdio.h>      // required for FILE
24#include <stddef.h>     // required for size_t
25
26#ifdef __cplusplus
27extern "C" {
28#endif // __cplusplus
29
30// This structure contains information about HTTP request.
31struct mg_connection {
32  const char *request_method; // "GET", "POST", etc
33  const char *uri;            // URL-decoded URI
34  const char *http_version;   // E.g. "1.0", "1.1"
35  const char *query_string;   // URL part after '?', not including '?', or NULL
36
37  char remote_ip[48];         // Max IPv6 string length is 45 characters
38  char local_ip[48];          // Local IP address
39  unsigned short remote_port; // Client's port
40  unsigned short local_port;  // Local port number
41
42  int num_headers;            // Number of HTTP headers
43  struct mg_header {
44    const char *name;         // HTTP header name
45    const char *value;        // HTTP header value
46  } http_headers[30];
47
48  char *content;              // POST (or websocket message) data, or NULL
49  size_t content_len;         // Data length
50
51  int is_websocket;           // Connection is a websocket connection
52  int status_code;            // HTTP status code for HTTP error handler
53  int wsbits;                 // First byte of the websocket frame
54  void *server_param;         // Parameter passed to mg_create_server()
55  void *connection_param;     // Placeholder for connection-specific data
56  void *callback_param;
57};
58
59struct mg_server; // Opaque structure describing server instance
60enum mg_result { MG_FALSE, MG_TRUE, MG_MORE };
61enum mg_event {
62  MG_POLL = 100,  // Callback return value is ignored
63  MG_CONNECT,     // If callback returns MG_FALSE, connect fails
64  MG_AUTH,        // If callback returns MG_FALSE, authentication fails
65  MG_REQUEST,     // If callback returns MG_FALSE, Mongoose continues with req
66  MG_REPLY,       // If callback returns MG_FALSE, Mongoose closes connection
67  MG_RECV,        // Mongoose has received POST data chunk.
68                  // Callback should return a number of bytes to discard from
69                  // the receive buffer, or -1 to close the connection.
70  MG_CLOSE,       // Connection is closed, callback return value is ignored
71  MG_WS_HANDSHAKE,  // New websocket connection, handshake request
72  MG_WS_CONNECT,  // New websocket connection established
73  MG_HTTP_ERROR   // If callback returns MG_FALSE, Mongoose continues with err
74};
75typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event);
76
77// Websocket opcodes, from http://tools.ietf.org/html/rfc6455
78enum {
79  WEBSOCKET_OPCODE_CONTINUATION = 0x0,
80  WEBSOCKET_OPCODE_TEXT = 0x1,
81  WEBSOCKET_OPCODE_BINARY = 0x2,
82  WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
83  WEBSOCKET_OPCODE_PING = 0x9,
84  WEBSOCKET_OPCODE_PONG = 0xa
85};
86
87// Server management functions
88struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);
89void mg_destroy_server(struct mg_server **);
90const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
91int mg_poll_server(struct mg_server *, int milliseconds);
92const char **mg_get_valid_option_names(void);
93const char *mg_get_option(const struct mg_server *server, const char *name);
94void mg_copy_listeners(struct mg_server *from, struct mg_server *to);
95struct mg_connection *mg_next(struct mg_server *, struct mg_connection *);
96void mg_wakeup_server(struct mg_server *);
97void mg_wakeup_server_ex(struct mg_server *, mg_handler_t, const char *, ...);
98struct mg_connection *mg_connect(struct mg_server *, const char *);
99
100// Connection management functions
101void mg_send_status(struct mg_connection *, int status_code);
102void mg_send_header(struct mg_connection *, const char *name, const char *val);
103size_t mg_send_data(struct mg_connection *, const void *data, int data_len);
104size_t mg_printf_data(struct mg_connection *, const char *format, ...);
105size_t mg_write(struct mg_connection *, const void *buf, int len);
106size_t mg_printf(struct mg_connection *conn, const char *fmt, ...);
107
108size_t mg_websocket_write(struct mg_connection *, int opcode,
109                          const char *data, size_t data_len);
110size_t mg_websocket_printf(struct mg_connection* conn, int opcode,
111                           const char *fmt, ...);
112
113void mg_send_file(struct mg_connection *, const char *path, const char *);
114void mg_send_file_data(struct mg_connection *, int fd);
115
116const char *mg_get_header(const struct mg_connection *, const char *name);
117const char *mg_get_mime_type(const char *name, const char *default_mime_type);
118int mg_get_var(const struct mg_connection *conn, const char *var_name,
119               char *buf, size_t buf_len);
120int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
121int mg_parse_multipart(const char *buf, int buf_len,
122                       char *var_name, int var_name_len,
123                       char *file_name, int file_name_len,
124                       const char **data, int *data_len);
125
126
127// Utility functions
128void *mg_start_thread(void *(*func)(void *), void *param);
129char *mg_md5(char buf[33], ...);
130int mg_authorize_digest(struct mg_connection *c, FILE *fp);
131int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len);
132int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int);
133int mg_terminate_ssl(struct mg_connection *c, const char *cert);
134int mg_forward(struct mg_connection *c, const char *addr);
135void *mg_mmap(FILE *fp, size_t size);
136void mg_munmap(void *p, size_t size);
137
138
139// Templates support
140struct mg_expansion {
141  const char *keyword;
142  void (*handler)(struct mg_connection *);
143};
144void mg_template(struct mg_connection *, const char *text,
145                 struct mg_expansion *expansions);
146
147#ifdef __cplusplus
148}
149#endif // __cplusplus
150
151#endif // MONGOOSE_HEADER_INCLUDED
trunk/3rdparty/mongoose/scripts/embed_binary_files.pl
r0r242829
1# This program is used to embed arbitrary data into a C binary. It takes
2# a list of files as an input, and produces a .c data file that contains
3# contents of all these files as collection of char arrays.
4#
5# Usage: perl <this_file> <file1> [file2, ...] > embedded_data.c
6
7foreach my $i (0 .. $#ARGV) {
8  open FD, '<:raw', $ARGV[$i] or die "Cannot open $ARGV[$i]: $!\n";
9  printf("static const unsigned char v%d[] = {", $i);
10  my $byte;
11  my $j = 0;
12  while (read(FD, $byte, 1)) {
13    if (($j % 12) == 0) {
14      print "\n";
15    }
16    printf ' %#04x,', ord($byte);
17    $j++;
18  }
19  print " 0x00\n};\n";
20  close FD;
21}
22
23print <<EOS;
24
25#include <stddef.h>
26#include <string.h>
27
28static const struct embedded_file {
29  const char *name;
30  const unsigned char *data;
31  size_t size;
32} embedded_files[] = {
33EOS
34
35foreach my $i (0 .. $#ARGV) {
36  print "  {\"$ARGV[$i]\", v$i, sizeof(v$i) - 1},\n";
37}
38
39print <<EOS;
40  {NULL, NULL, 0}
41};
42
43const char *find_embedded_file(const char *name, size_t *size) {
44  const struct embedded_file *p;
45  for (p = embedded_files; p->name != NULL; p++) {
46    if (!strcmp(p->name, name)) {
47      if (size != NULL) { *size = p->size; }
48      return (const char *) p->data;
49    }
50  }
51  return NULL;
52}
53EOS
trunk/3rdparty/mongoose/test/Makefile
r0r242829
1# Copyright (c) 2014 Cesanta Software
2# All rights reserved
3
4PROG = unit_test
5PROF_FLAGS = -fprofile-arcs -ftest-coverage -g -O0 -DGUI
6CFLAGS = -W -Wall -pthread -I.. $(PROF_FLAGS) $(CFLAGS_EXTRA)
7SOURCES = $(PROG).c
8
9all: $(PROG)
10   ./$(PROG)
11   gcov -b $(PROG).c | egrep '^(File|Lines)'
12
13$(PROG): $(SOURCES) Makefile ../mongoose.c clean
14   $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) -ldl -lssl
15
16win:
17   wine cl $(SOURCES) /MD /nologo /DNDEBUG /O1 /Fe$(PROG).exe
18   wine $(PROG).exe
19
20clean:
21   rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib *.gc*
trunk/3rdparty/mongoose/test/unit_test.c
r0r242829
1// Unit test for the mongoose web server.
2// g++ -W -Wall -pedantic -g unit_test.c -lssl && ./a.out
3// cl unit_test.c /MD
4
5#ifndef _WIN32
6#define NS_ENABLE_IPV6
7#define NS_ENABLE_SSL
8#endif
9#define MONGOOSE_POST_SIZE_LIMIT 999
10
11// USE_* definitions must be made before #include "mongoose.c" !
12#include "../mongoose.c"
13
14#define FAIL(str, line) do {                    \
15  printf("Fail on line %d: [%s]\n", line, str); \
16  return str;                                   \
17} while (0)
18
19#define ASSERT(expr) do {             \
20  static_num_tests++;               \
21  if (!(expr)) FAIL(#expr, __LINE__); \
22} while (0)
23
24#define RUN_TEST(test) do { const char *msg = test(); \
25  if (msg) return msg; } while (0)
26
27#define HTTP_PORT "45772"
28#define LISTENING_ADDR "127.0.0.1:" HTTP_PORT
29
30static int static_num_tests = 0;
31
32#if 0
33// Connects to host:port, and sends formatted request to it. Returns
34// malloc-ed reply and reply length, or NULL on error. Reply contains
35// everything including headers, not just the message body.
36static char *wget(const char *host, int port, int *len, const char *fmt, ...) {
37  char buf[2000], *reply = NULL;
38  int request_len, reply_size = 0, n, sock = -1;
39  struct sockaddr_in sin;
40  struct hostent *he = NULL;
41  va_list ap;
42
43  if (host != NULL &&
44      (he = gethostbyname(host)) != NULL &&
45      (sock = socket(PF_INET, SOCK_STREAM, 0)) != -1) {
46    sin.sin_family = AF_INET;
47    sin.sin_port = htons((uint16_t) port);
48    sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
49    if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) == 0) {
50
51      // Format and send the request.
52      va_start(ap, fmt);
53      request_len = vsnprintf(buf, sizeof(buf), fmt, ap);
54      va_end(ap);
55      while (request_len > 0 && (n = send(sock, buf, request_len, 0)) > 0) {
56        request_len -= n;
57      }
58      if (request_len == 0) {
59        *len = 0;
60        while ((n = recv(sock, buf, sizeof(buf), 0)) > 0) {
61          if (*len + n > reply_size) {
62            // Leak possible
63            reply = (char *) realloc(reply, reply_size + sizeof(buf));
64            reply_size += sizeof(buf);
65          }
66          if (reply != NULL) {
67            memcpy(reply + *len, buf, n);
68            *len += n;
69          }
70        }
71      }
72      closesocket(sock);
73    }
74  }
75
76  return reply;
77}
78#endif
79
80static char *read_file(const char *path, int *size) {
81  FILE *fp;
82  struct stat st;
83  char *data = NULL;
84  if ((fp = fopen(path, "rb")) != NULL && !fstat(fileno(fp), &st)) {
85    *size = (int) st.st_size;
86    data = (char *) malloc(*size);
87    fread(data, 1, *size, fp);
88    fclose(fp);
89  }
90  return data;
91}
92
93static const char *test_parse_http_message() {
94  struct mg_connection ri;
95  char req1[] = "GET / HTTP/1.1\r\n\r\n";
96  char req2[] = "BLAH / HTTP/1.1\r\n\r\n";
97  char req3[] = "GET / HTTP/1.1\r\nBah\r\n";
98  char req4[] = "GET / HTTP/1.1\r\nA: foo bar\r\nB: bar\r\nbaz\r\n\r\n";
99  char req5[] = "GET / HTTP/1.1\r\n\r\n";
100  char req6[] = "G";
101  char req7[] = " blah ";
102  char req8[] = " HTTP/1.1 200 OK \n\n";
103  char req9[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
104
105  ASSERT(get_request_len("\r\n", 3) == -1);
106  ASSERT(get_request_len("\r\n", 2) == 0);
107  ASSERT(get_request_len("GET", 3) == 0);
108  ASSERT(get_request_len("\n\n", 2) == 2);
109  ASSERT(get_request_len("\n\r\n", 3) == 3);
110  ASSERT(get_request_len("\xdd\xdd", 2) == 0);
111  ASSERT(get_request_len("\xdd\x03", 2) == -1);
112  ASSERT(get_request_len(req3, sizeof(req3) - 1) == 0);
113  ASSERT(get_request_len(req6, sizeof(req6) - 1) == 0);
114  ASSERT(get_request_len(req7, sizeof(req7) - 1) == 0);
115
116  ASSERT(parse_http_message(req9, sizeof(req9) - 1, &ri) == sizeof(req9) - 1);
117  ASSERT(ri.num_headers == 1);
118
119  ASSERT(parse_http_message(req1, sizeof(req1) - 1, &ri) == sizeof(req1) - 1);
120  ASSERT(strcmp(ri.http_version, "1.1") == 0);
121  ASSERT(ri.num_headers == 0);
122
123  ASSERT(parse_http_message(req2, sizeof(req2) - 1, &ri) == -1);
124  ASSERT(parse_http_message(req6, 0, &ri) == -1);
125  ASSERT(parse_http_message(req8, sizeof(req8) - 1, &ri) == sizeof(req8) - 1);
126
127  // TODO(lsm): Fix this. Header value may span multiple lines.
128  ASSERT(parse_http_message(req4, sizeof(req4) - 1, &ri) == sizeof(req4) - 1);
129  ASSERT(strcmp(ri.http_version, "1.1") == 0);
130  ASSERT(ri.num_headers == 3);
131  ASSERT(strcmp(ri.http_headers[0].name, "A") == 0);
132  ASSERT(strcmp(ri.http_headers[0].value, "foo bar") == 0);
133  ASSERT(strcmp(ri.http_headers[1].name, "B") == 0);
134  ASSERT(strcmp(ri.http_headers[1].value, "bar") == 0);
135  ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0);
136  ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
137
138  ASSERT(parse_http_message(req5, sizeof(req5) - 1, &ri) == sizeof(req5) - 1);
139  ASSERT(strcmp(ri.request_method, "GET") == 0);
140  ASSERT(strcmp(ri.http_version, "1.1") == 0);
141
142  return NULL;
143}
144
145static const char *test_should_keep_alive(void) {
146  struct mg_connection conn;
147  char req1[] = "GET / HTTP/1.1\r\n\r\n";
148  char req2[] = "GET / HTTP/1.0\r\n\r\n";
149  char req3[] = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n";
150  char req4[] = "GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n";
151
152  memset(&conn, 0, sizeof(conn));
153  ASSERT(parse_http_message(req1, sizeof(req1) - 1, &conn) == sizeof(req1) - 1);
154  ASSERT(should_keep_alive(&conn) != 0);
155
156  parse_http_message(req2, sizeof(req2) - 1, &conn);
157  ASSERT(should_keep_alive(&conn) == 0);
158
159  parse_http_message(req3, sizeof(req3) - 1, &conn);
160  ASSERT(should_keep_alive(&conn) == 0);
161
162  parse_http_message(req4, sizeof(req4) - 1, &conn);
163  ASSERT(should_keep_alive(&conn) != 0);
164
165  return NULL;
166}
167
168static const char *test_match_prefix(void) {
169  ASSERT(mg_match_prefix("/api", 4, "/api") == 4);
170  ASSERT(mg_match_prefix("/a/", 3, "/a/b/c") == 3);
171  ASSERT(mg_match_prefix("/a/", 3, "/ab/c") == -1);
172  ASSERT(mg_match_prefix("/*/", 3, "/ab/c") == 4);
173  ASSERT(mg_match_prefix("**", 2, "/a/b/c") == 6);
174  ASSERT(mg_match_prefix("/*", 2, "/a/b/c") == 2);
175  ASSERT(mg_match_prefix("*/*", 3, "/a/b/c") == 2);
176  ASSERT(mg_match_prefix("**/", 3, "/a/b/c") == 5);
177  ASSERT(mg_match_prefix("**.foo|**.bar", 13, "a.bar") == 5);
178  ASSERT(mg_match_prefix("a|b|cd", 6, "cdef") == 2);
179  ASSERT(mg_match_prefix("a|b|c?", 6, "cdef") == 2);
180  ASSERT(mg_match_prefix("a|?|cd", 6, "cdef") == 1);
181  ASSERT(mg_match_prefix("/a/**.cgi", 9, "/foo/bar/x.cgi") == -1);
182  ASSERT(mg_match_prefix("/a/**.cgi", 9, "/a/bar/x.cgi") == 12);
183  ASSERT(mg_match_prefix("**/", 3, "/a/b/c") == 5);
184  ASSERT(mg_match_prefix("**/$", 4, "/a/b/c") == -1);
185  ASSERT(mg_match_prefix("**/$", 4, "/a/b/") == 5);
186  ASSERT(mg_match_prefix("$", 1, "") == 0);
187  ASSERT(mg_match_prefix("$", 1, "x") == -1);
188  ASSERT(mg_match_prefix("*$", 2, "x") == 1);
189  ASSERT(mg_match_prefix("/$", 2, "/") == 1);
190  ASSERT(mg_match_prefix("**/$", 4, "/a/b/c") == -1);
191  ASSERT(mg_match_prefix("**/$", 4, "/a/b/") == 5);
192  ASSERT(mg_match_prefix("*", 1, "/hello/") == 0);
193  ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
194  ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
195  ASSERT(mg_match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
196  ASSERT(mg_match_prefix("**o$", 4, "HELLO") == 5);
197  return NULL;
198}
199
200static const char *test_remove_double_dots() {
201  struct { char before[20], after[20]; } data[] = {
202    {"////a", "/a"},
203    {"/.....", "/."},
204    {"/......", "/"},
205    {"...", "..."},
206    {"/...///", "/./"},
207    {"/a...///", "/a.../"},
208    {"/.x", "/.x"},
209    {"/\\", "/"},
210    {"/a\\", "/a\\"},
211    {"/a\\\\...", "/a\\."},
212  };
213  size_t i;
214
215  for (i = 0; i < ARRAY_SIZE(data); i++) {
216    remove_double_dots_and_double_slashes(data[i].before);
217    ASSERT(strcmp(data[i].before, data[i].after) == 0);
218  }
219
220  return NULL;
221}
222
223static const char *test_get_var(void) {
224  static const char *post[] = {
225    "a=1&&b=2&d&=&c=3%20&e=",
226    "q=&st=2012%2F11%2F13+17%3A05&et=&team_id=",
227    NULL
228  };
229  char buf[20];
230
231  ASSERT(get_var(post[0], strlen(post[0]), "a", buf, sizeof(buf)) == 1);
232  ASSERT(buf[0] == '1' && buf[1] == '\0');
233  ASSERT(get_var(post[0], strlen(post[0]), "b", buf, sizeof(buf)) == 1);
234  ASSERT(buf[0] == '2' && buf[1] == '\0');
235  ASSERT(get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2);
236  ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0');
237  ASSERT(get_var(post[0], strlen(post[0]), "e", buf, sizeof(buf)) == 0);
238  ASSERT(buf[0] == '\0');
239
240  ASSERT(get_var(post[0], strlen(post[0]), "d", buf, sizeof(buf)) == -1);
241  ASSERT(get_var(post[0], strlen(post[0]), "c", buf, 2) == -2);
242
243  ASSERT(get_var(post[0], strlen(post[0]), "x", NULL, 10) == -2);
244  ASSERT(get_var(post[0], strlen(post[0]), "x", buf, 0) == -2);
245  ASSERT(get_var(post[1], strlen(post[1]), "st", buf, 16) == -2);
246  ASSERT(get_var(post[1], strlen(post[1]), "st", buf, 17) == 16);
247  return NULL;
248}
249
250static const char *test_url_decode(void) {
251  char buf[100];
252
253  ASSERT(mg_url_decode("foo", 3, buf, 3, 0) == -1);  // No space for \0
254  ASSERT(mg_url_decode("foo", 3, buf, 4, 0) == 3);
255  ASSERT(strcmp(buf, "foo") == 0);
256
257  ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 0) == 2);
258  ASSERT(strcmp(buf, "a+") == 0);
259
260  ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 1) == 2);
261  ASSERT(strcmp(buf, "a ") == 0);
262
263  ASSERT(mg_url_decode("%61", 1, buf, sizeof(buf), 1) == 1);
264  ASSERT(strcmp(buf, "%") == 0);
265
266  ASSERT(mg_url_decode("%61", 2, buf, sizeof(buf), 1) == 2);
267  ASSERT(strcmp(buf, "%6") == 0);
268
269  ASSERT(mg_url_decode("%61", 3, buf, sizeof(buf), 1) == 1);
270  ASSERT(strcmp(buf, "a") == 0);
271  return NULL;
272}
273
274static const char *test_url_encode(void) {
275  char buf[100];
276  ASSERT(mg_url_encode("", 0, buf, sizeof(buf)) == 0);
277  ASSERT(buf[0] == '\0');
278  ASSERT(mg_url_encode("foo", 3, buf, sizeof(buf)) == 3);
279  ASSERT(strcmp(buf, "foo") == 0);
280  ASSERT(mg_url_encode("f o", 3, buf, sizeof(buf)) == 5);
281  ASSERT(strcmp(buf, "f%20o") == 0);
282  return NULL;
283}
284
285static const char *test_to64(void) {
286  ASSERT(to64("0") == 0);
287  ASSERT(to64("") == 0);
288  ASSERT(to64("123") == 123);
289  ASSERT(to64("-34") == -34);
290  ASSERT(to64("3566626116") == 3566626116);
291  return NULL;
292}
293
294static const char *test_base64_encode(void) {
295  const char *in[] = {"a", "ab", "abc", "abcd", NULL};
296  const char *out[] = {"YQ==", "YWI=", "YWJj", "YWJjZA=="};
297  char buf[100];
298  int i;
299
300  for (i = 0; in[i] != NULL; i++) {
301    base64_encode((unsigned char *) in[i], strlen(in[i]), buf);
302    ASSERT(!strcmp(buf, out[i]));
303  }
304
305  return NULL;
306}
307
308static const char *test_mg_parse_header(void) {
309  const char *str = "xx=1 kl yy, ert=234 kl=123, uri=\"/?name=x,y\", "
310    "ii=\"12\\\"34\" zz='aa bb',tt=2,gf=\"xx d=1234";
311  char buf[20];
312  ASSERT(mg_parse_header(str, "yy", buf, sizeof(buf)) == 0);
313  ASSERT(mg_parse_header(str, "ert", buf, sizeof(buf)) == 3);
314  ASSERT(strcmp(buf, "234") == 0);
315  ASSERT(mg_parse_header(str, "ert", buf, 2) == 0);
316  ASSERT(mg_parse_header(str, "ert", buf, 3) == 0);
317  ASSERT(mg_parse_header(str, "ert", buf, 4) == 3);
318  ASSERT(mg_parse_header(str, "gf", buf, sizeof(buf)) == 0);
319  ASSERT(mg_parse_header(str, "zz", buf, sizeof(buf)) == 5);
320  ASSERT(strcmp(buf, "aa bb") == 0);
321  ASSERT(mg_parse_header(str, "d", buf, sizeof(buf)) == 4);
322  ASSERT(strcmp(buf, "1234") == 0);
323  buf[0] = 'x';
324  ASSERT(mg_parse_header(str, "MMM", buf, sizeof(buf)) == 0);
325  ASSERT(buf[0] == '\0');
326  ASSERT(mg_parse_header(str, "kl", buf, sizeof(buf)) == 3);
327  ASSERT(strcmp(buf, "123") == 0);
328  ASSERT(mg_parse_header(str, "xx", buf, sizeof(buf)) == 1);
329  ASSERT(strcmp(buf, "1") == 0);
330  ASSERT(mg_parse_header(str, "ii", buf, sizeof(buf)) == 5);
331  ASSERT(strcmp(buf, "12\"34") == 0);
332  ASSERT(mg_parse_header(str, "tt", buf, sizeof(buf)) == 1);
333  ASSERT(strcmp(buf, "2") == 0);
334  ASSERT(mg_parse_header(str, "uri", buf, sizeof(buf)) == 10);
335  return NULL;
336}
337
338static const char *test_next_option(void) {
339  const char *p, *list = "x/8,/y**=1;2k,z";
340  struct vec a, b;
341  int i;
342
343  ASSERT(next_option(NULL, &a, &b) == NULL);
344  for (i = 0, p = list; (p = next_option(p, &a, &b)) != NULL; i++) {
345    ASSERT(i != 0 || (a.ptr == list && a.len == 3 && b.len == 0));
346    ASSERT(i != 1 || (a.ptr == list + 4 && a.len == 4 && b.ptr == list + 9 &&
347                      b.len == 4));
348
349    ASSERT(i != 2 || (a.ptr == list + 14 && a.len == 1 && b.len == 0));
350  }
351  return NULL;
352}
353
354static int evh1(struct mg_connection *conn, enum mg_event ev) {
355  char *buf = (char *) conn->connection_param;
356  int result = MG_FALSE;
357
358  switch (ev) {
359    case MG_CONNECT:
360      mg_printf(conn,  "GET %s HTTP/1.0\r\n\r\n",
361                buf[0] == '1' ? "/cb1" : "/non_exist");
362      result = MG_TRUE;
363      break;
364    case MG_HTTP_ERROR:
365      mg_printf(conn, "HTTP/1.0 404 NF\r\n\r\nERR: %d", conn->status_code);
366      result = MG_TRUE;
367      break;
368    case MG_REQUEST:
369      if (!strcmp(conn->uri, "/cb1")) {
370        mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n%s %s %s",
371                  (char *) conn->server_param,
372                  buf == NULL ? "?" : "!", conn->remote_ip);
373        result = MG_TRUE;
374      }
375      break;
376    case MG_REPLY:
377      if (buf != NULL) {
378        sprintf(buf + 1, "%.*s", (int) conn->content_len, conn->content);
379      }
380      break;
381    case MG_AUTH:
382      result = MG_TRUE;
383      break;
384    default:
385      break;
386  }
387
388  return result;
389}
390
391static const char *test_server(void) {
392  char buf1[100] = "1", buf2[100] = "2";
393  struct mg_server *server = mg_create_server((void *) "foo", evh1);
394  struct mg_connection *conn;
395
396  ASSERT(server != NULL);
397  ASSERT(mg_set_option(server, "listening_port", LISTENING_ADDR) == NULL);
398  ASSERT(mg_set_option(server, "document_root", ".") == NULL);
399
400  ASSERT((conn = mg_connect(server, "127.0.0.1:" HTTP_PORT)) != NULL);
401  conn->connection_param = buf1;
402  ASSERT((conn = mg_connect(server, "127.0.0.1:" HTTP_PORT)) != NULL);
403  conn->connection_param = buf2;
404
405  { int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
406  ASSERT(strcmp(buf1, "1foo ? 127.0.0.1") == 0);
407  ASSERT(strcmp(buf2, "2ERR: 404") == 0);
408
409  ASSERT(strcmp(static_config_options[URL_REWRITES * 2], "url_rewrites") == 0);
410  mg_destroy_server(&server);
411  ASSERT(server == NULL);
412  return NULL;
413}
414
415#define DISP "Content-Disposition: form/data; "
416#define CRLF "\r\n"
417#define BOUNDARY "--xyz"
418static const char *test_parse_multipart(void) {
419  char a[100], b[100];
420  const char *p;
421  static const char f1[] = BOUNDARY CRLF DISP "name=f1" CRLF CRLF
422    "some_content" CRLF BOUNDARY CRLF
423    BOUNDARY CRLF DISP "name=f2; filename=\"foo bar.txt\"" CRLF CRLF
424    "another_content" CRLF BOUNDARY CRLF
425    "--" CRLF;
426  int n, n2, len, f1_len = sizeof(f1) - 1;
427
428  ASSERT(mg_parse_multipart("", 0, a, sizeof(a), b, sizeof(b), &p, &len) == 0);
429  ASSERT(mg_parse_multipart("a", 1, a, sizeof(a), b, sizeof(b), &p, &len) == 0);
430  ASSERT((n = mg_parse_multipart(f1, f1_len, a, sizeof(a),
431                                 b, sizeof(b), &p, &len)) > 0);
432  ASSERT(len == 12);
433  ASSERT(memcmp(p, "some_content", len) == 0);
434  ASSERT(strcmp(a, "f1") == 0);
435  ASSERT(b[0] == '\0');
436
437  ASSERT((n2 = mg_parse_multipart(f1 + n, f1_len - n, a, sizeof(a),
438                                  b, sizeof(b), &p, &len)) > 0);
439  ASSERT(len == 15);
440  ASSERT(memcmp(p, "another_content", len) == 0);
441  ASSERT(strcmp(a, "f2") == 0);
442  ASSERT(strcmp(b, "foo bar.txt") == 0);
443
444  ASSERT((n2 = mg_parse_multipart(f1 + n + n2, f1_len - (n + n2), a, sizeof(a),
445                                  b, sizeof(b), &p, &len)) == 0);
446
447  return NULL;
448}
449
450static int evh2(struct mg_connection *conn, enum mg_event ev) {
451  char *file_data, *cp = (char *) conn->connection_param;
452  int file_size, result = MG_FALSE;
453
454  switch (ev) {
455    case MG_AUTH:
456      result = MG_TRUE;
457      break;
458    case MG_CONNECT:
459      mg_printf(conn, "GET /%s HTTP/1.0\r\n\r\n", cp);
460      result = MG_TRUE;
461      break;
462    case MG_REQUEST:
463      break;
464    case MG_REPLY:
465      file_data = read_file("unit_test.c", &file_size);
466      sprintf(cp, "%d %s", (size_t) file_size == conn->content_len &&
467              memcmp(file_data, conn->content, file_size) == 0 ? 1 : 0,
468              conn->query_string == NULL ? "?" : conn->query_string);
469      free(file_data);
470      break;
471    default:
472      break;
473  }
474
475  return result;
476}
477
478static const char *test_mg_set_option(void) {
479  struct mg_server *server = mg_create_server(NULL, NULL);
480  ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
481  ASSERT(mg_get_option(server, "listening_port")[0] != '\0');
482  mg_destroy_server(&server);
483  return NULL;
484}
485
486static const char *test_rewrites(void) {
487  char buf1[100] = "xx", addr[50];
488  struct mg_server *server = mg_create_server(NULL, evh2);
489  struct mg_connection *conn;
490  const char *port;
491
492  ASSERT(mg_set_option(server, "listening_port", "0") == NULL);
493  ASSERT(mg_set_option(server, "document_root", ".") == NULL);
494  ASSERT(mg_set_option(server, "url_rewrites", "/xx=unit_test.c") == NULL);
495  ASSERT((port = mg_get_option(server, "listening_port")) != NULL);
496  snprintf(addr, sizeof(addr), "127.0.0.1:%s", port);
497  ASSERT((conn = mg_connect(server, addr)) != NULL);
498  conn->connection_param = buf1;
499
500  { int i; for (i = 0; i < 50; i++) mg_poll_server(server, 1); }
501
502  ASSERT(strcmp(buf1, "1 ?") == 0);
503  mg_destroy_server(&server);
504  return NULL;
505}
506
507static const char *run_all_tests(void) {
508  RUN_TEST(test_should_keep_alive);
509  RUN_TEST(test_match_prefix);
510  RUN_TEST(test_remove_double_dots);
511  RUN_TEST(test_parse_http_message);
512  RUN_TEST(test_to64);
513  RUN_TEST(test_url_decode);
514  RUN_TEST(test_url_encode);
515  RUN_TEST(test_base64_encode);
516  RUN_TEST(test_mg_parse_header);
517  RUN_TEST(test_get_var);
518  RUN_TEST(test_next_option);
519  RUN_TEST(test_parse_multipart);
520  RUN_TEST(test_mg_set_option);
521  RUN_TEST(test_server);
522  RUN_TEST(test_rewrites);
523  return NULL;
524}
525
526int __cdecl main(void) {
527  const char *fail_msg = run_all_tests();
528  printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests);
529  return fail_msg == NULL ? EXIT_SUCCESS : EXIT_FAILURE;
530}
trunk/src/lib/lib.mak
r242828r242829
2525   $(LIBOBJ)/portmidi \
2626   $(LIBOBJ)/lua \
2727   $(LIBOBJ)/lua/lib \
28   $(LIBOBJ)/mongoose \
2829   $(LIBOBJ)/web \
2930   $(LIBOBJ)/web/json \
3031   $(LIBOBJ)/sqlite3 \
r242828r242829
535536#-------------------------------------------------
536537
537538WEBOBJS = \
538   $(LIBOBJ)/web/mongoose.o \
539   $(LIBOBJ)/mongoose/mongoose.o \
539540   $(LIBOBJ)/web/json/json_reader.o \
540541   $(LIBOBJ)/web/json/json_value.o \
541542   $(LIBOBJ)/web/json/json_writer.o \
r242828r242829
546547   @echo Compiling $<...
547548   $(CC) $(CDEFS) $(CFLAGS) -I$(LIBSRC)/web -c $< -o $@
548549
549$(LIBOBJ)/web/%.o: $(LIBSRC)/web/%.c | $(OSPREBUILD)
550$(LIBOBJ)/mongoose/%.o: $(3RDPARTY)/mongoose/%.c | $(OSPREBUILD)
550551   @echo Compiling $<...
551   $(CC) $(CDEFS) $(CFLAGS) -I$(LIBSRC)/web -DNS_STACK_SIZE=0 -c $< -o $@
552   $(CC) $(CDEFS) $(CFLAGS) -I$(3RDPARTY)/mongoose -DNS_STACK_SIZE=0 -c $< -o $@
552553
553554#-------------------------------------------------
554555# SQLite3 library objects
trunk/src/lib/web/mongoose.c
r242828r242829
1// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
2// Copyright (c) 2013-2014 Cesanta Software Limited
3// All rights reserved
4//
5// This library is dual-licensed: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License version 2 as
7// published by the Free Software Foundation. For the terms of this
8// license, see <http://www.gnu.org/licenses/>.
9//
10// You are free to use this library under the terms of the GNU General
11// Public License, but WITHOUT ANY WARRANTY; without even the implied
12// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13// See the GNU General Public License for more details.
14//
15// Alternatively, you can license this library under a commercial
16// license, as set out in <http://cesanta.com/>.
17//
18// $Date: 2014-09-01 19:53:26 UTC $
19
20#ifdef NOEMBED_NET_SKELETON
21#include "net_skeleton.h"
22#else
23// net_skeleton start
24// Copyright (c) 2014 Cesanta Software Limited
25// All rights reserved
26//
27// This software is dual-licensed: you can redistribute it and/or modify
28// it under the terms of the GNU General Public License version 2 as
29// published by the Free Software Foundation. For the terms of this
30// license, see <http://www.gnu.org/licenses/>.
31//
32// You are free to use this software under the terms of the GNU General
33// Public License, but WITHOUT ANY WARRANTY; without even the implied
34// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
35// See the GNU General Public License for more details.
36//
37// Alternatively, you can license this software under a commercial
38// license, as set out in <http://cesanta.com/>.
39
40#ifndef NS_SKELETON_HEADER_INCLUDED
41#define NS_SKELETON_HEADER_INCLUDED
42
43#define NS_SKELETON_VERSION "1.1"
44
45#undef UNICODE                  // Use ANSI WinAPI functions
46#undef _UNICODE                 // Use multibyte encoding on Windows
47#define _MBCS                   // Use multibyte encoding on Windows
48#define _INTEGRAL_MAX_BITS 64   // Enable _stati64() on Windows
49#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005+
50#undef WIN32_LEAN_AND_MEAN      // Let windows.h always include winsock2.h
51#define _XOPEN_SOURCE 600       // For flockfile() on Linux
52#define __STDC_FORMAT_MACROS    // <inttypes.h> wants this for C++
53#define __STDC_LIMIT_MACROS     // C++ wants that for INT64_MAX
54#ifndef _LARGEFILE_SOURCE
55#define _LARGEFILE_SOURCE       // Enable fseeko() and ftello() functions
56#endif
57#define _FILE_OFFSET_BITS 64    // Enable 64-bit file offsets
58
59#ifdef _MSC_VER
60#pragma warning (disable : 4127)  // FD_SET() emits warning, disable it
61#pragma warning (disable : 4204)  // missing c99 support
62#endif
63
64#include <sys/types.h>
65#include <sys/stat.h>
66#include <assert.h>
67#include <errno.h>
68#include <fcntl.h>
69#include <stdarg.h>
70#include <stddef.h>
71#include <stdio.h>
72#include <stdlib.h>
73#include <string.h>
74#include <time.h>
75#ifdef __OS2__
76#include <sys/time.h>  // struct timeval
77#endif
78#include <signal.h>
79
80#ifdef _WIN32
81#ifdef _MSC_VER
82#pragma comment(lib, "ws2_32.lib")    // Linking with winsock library
83#endif
84#include <windows.h>
85#include <process.h>
86#ifndef EINPROGRESS
87#define EINPROGRESS WSAEINPROGRESS
88#endif
89#ifndef EWOULDBLOCK
90#define EWOULDBLOCK WSAEWOULDBLOCK
91#endif
92#ifndef __func__
93#define STRX(x) #x
94#define STR(x) STRX(x)
95#define __func__ __FILE__ ":" STR(__LINE__)
96#endif
97#ifndef va_copy
98#define va_copy(x,y) x = y
99#endif // MINGW #defines va_copy
100#define snprintf _snprintf
101#define vsnprintf _vsnprintf
102#define sleep(x) Sleep((x) * 1000)
103#define to64(x) _atoi64(x)
104typedef int socklen_t;
105typedef unsigned char uint8_t;
106typedef unsigned int uint32_t;
107typedef unsigned short uint16_t;
108typedef unsigned __int64 uint64_t;
109typedef __int64   int64_t;
110typedef SOCKET sock_t;
111#ifndef S_ISDIR
112#define S_ISDIR(x) ((x) & _S_IFDIR)
113#endif
114#else
115#include <errno.h>
116#include <fcntl.h>
117#include <netdb.h>
118#include <pthread.h>
119#include <stdarg.h>
120#include <unistd.h>
121#include <arpa/inet.h>  // For inet_pton() when NS_ENABLE_IPV6 is defined
122#include <netinet/in.h>
123#include <sys/socket.h>
124#include <sys/select.h>
125#define closesocket(x) close(x)
126#ifdef __OS2__
127typedef int socklen_t;
128#else
129#define __cdecl
130#endif
131#define INVALID_SOCKET (-1)
132#define to64(x) strtoll(x, NULL, 10)
133typedef int sock_t;
134#endif
135
136#ifdef NS_ENABLE_DEBUG
137#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \
138   fflush(stdout); } while(0)
139#else
140#define DBG(x)
141#endif
142
143#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
144
145#ifdef NS_ENABLE_SSL
146#ifdef __APPLE__
147#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
148#endif
149#include <openssl/ssl.h>
150#else
151typedef void *SSL;
152typedef void *SSL_CTX;
153#endif
154
155#ifdef __cplusplus
156extern "C" {
157#endif // __cplusplus
158
159union socket_address {
160   struct sockaddr sa;
161   struct sockaddr_in sin;
162#ifdef NS_ENABLE_IPV6
163   struct sockaddr_in6 sin6;
164#else
165   struct sockaddr sin6;
166#endif
167};
168
169// IO buffers interface
170struct iobuf {
171   char *buf;
172   size_t len;
173   size_t size;
174};
175
176void iobuf_init(struct iobuf *, size_t initial_size);
177void iobuf_free(struct iobuf *);
178size_t iobuf_append(struct iobuf *, const void *data, size_t data_size);
179void iobuf_remove(struct iobuf *, size_t data_size);
180
181// Net skeleton interface
182// Events. Meaning of event parameter (evp) is given in the comment.
183enum ns_event {
184   NS_POLL,     // Sent to each connection on each call to ns_server_poll()
185   NS_ACCEPT,   // New connection accept()-ed. union socket_address *remote_addr
186   NS_CONNECT,  // connect() succeeded or failed. int *success_status
187   NS_RECV,     // Data has benn received. int *num_bytes
188   NS_SEND,     // Data has been written to a socket. int *num_bytes
189   NS_CLOSE     // Connection is closed. NULL
190};
191
192// Callback function (event handler) prototype, must be defined by user.
193// Net skeleton will call event handler, passing events defined above.
194struct ns_connection;
195typedef void (*ns_callback_t)(struct ns_connection *, enum ns_event, void *evp);
196
197struct ns_server {
198   void *server_data;
199   sock_t listening_sock;
200   struct ns_connection *active_connections;
201   ns_callback_t callback;
202   SSL_CTX *ssl_ctx;
203   SSL_CTX *client_ssl_ctx;
204   const char *hexdump_file;
205   sock_t ctl[2];
206};
207
208struct ns_connection {
209   struct ns_connection *prev, *next;
210   struct ns_server *server;
211   sock_t sock;
212   union socket_address sa;
213   struct iobuf recv_iobuf;
214   struct iobuf send_iobuf;
215   SSL *ssl;
216   void *connection_data;
217   time_t last_io_time;
218   unsigned int flags;
219#define NSF_FINISHED_SENDING_DATA   (1 << 0)
220#define NSF_BUFFER_BUT_DONT_SEND    (1 << 1)
221#define NSF_SSL_HANDSHAKE_DONE      (1 << 2)
222#define NSF_CONNECTING              (1 << 3)
223#define NSF_CLOSE_IMMEDIATELY       (1 << 4)
224#define NSF_ACCEPTED                (1 << 5)
225#define NSF_WANT_READ               (1 << 6)
226#define NSF_WANT_WRITE              (1 << 7)
227
228#define NSF_USER_1                  (1 << 26)
229#define NSF_USER_2                  (1 << 27)
230#define NSF_USER_3                  (1 << 28)
231#define NSF_USER_4                  (1 << 29)
232#define NSF_USER_5                  (1 << 30)
233#define NSF_USER_6                  (1 << 31)
234};
235
236void ns_server_init(struct ns_server *, void *server_data, ns_callback_t);
237void ns_server_free(struct ns_server *);
238int ns_server_poll(struct ns_server *, int milli);
239void ns_server_wakeup(struct ns_server *);
240void ns_server_wakeup_ex(struct ns_server *, ns_callback_t, void *, size_t);
241void ns_iterate(struct ns_server *, ns_callback_t cb, void *param);
242struct ns_connection *ns_next(struct ns_server *, struct ns_connection *);
243struct ns_connection *ns_add_sock(struct ns_server *, sock_t sock, void *p);
244
245int ns_bind(struct ns_server *, const char *addr);
246int ns_set_ssl_cert(struct ns_server *, const char *ssl_cert);
247int ns_set_ssl_ca_cert(struct ns_server *, const char *ssl_ca_cert);
248struct ns_connection *ns_connect(struct ns_server *, const char *host,
249                           int port, int ssl, void *connection_param);
250
251int ns_send(struct ns_connection *, const void *buf, int len);
252int ns_printf(struct ns_connection *, const char *fmt, ...);
253int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap);
254
255// Utility functions
256void *ns_start_thread(void *(*f)(void *), void *p);
257int ns_socketpair(sock_t [2]);
258int ns_socketpair2(sock_t [2], int sock_type);  // SOCK_STREAM or SOCK_DGRAM
259void ns_set_close_on_exec(sock_t);
260void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags);
261int ns_hexdump(const void *buf, int len, char *dst, int dst_len);
262int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap);
263
264#ifdef __cplusplus
265}
266#endif // __cplusplus
267
268#endif // NS_SKELETON_HEADER_INCLUDED
269// Copyright (c) 2014 Cesanta Software Limited
270// All rights reserved
271//
272// This software is dual-licensed: you can redistribute it and/or modify
273// it under the terms of the GNU General Public License version 2 as
274// published by the Free Software Foundation. For the terms of this
275// license, see <http://www.gnu.org/licenses/>.
276//
277// You are free to use this software under the terms of the GNU General
278// Public License, but WITHOUT ANY WARRANTY; without even the implied
279// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
280// See the GNU General Public License for more details.
281//
282// Alternatively, you can license this software under a commercial
283// license, as set out in <http://cesanta.com/>.
284
285
286#ifndef NS_MALLOC
287#define NS_MALLOC malloc
288#endif
289
290#ifndef NS_REALLOC
291#define NS_REALLOC realloc
292#endif
293
294#ifndef NS_FREE
295#define NS_FREE free
296#endif
297
298struct ctl_msg {
299   ns_callback_t callback;
300   char message[1024 * 8];
301};
302
303void iobuf_init(struct iobuf *iobuf, size_t size) {
304   iobuf->len = iobuf->size = 0;
305   iobuf->buf = NULL;
306
307   if (size > 0 && (iobuf->buf = (char *) NS_MALLOC(size)) != NULL) {
308   iobuf->size = size;
309   }
310}
311
312void iobuf_free(struct iobuf *iobuf) {
313   if (iobuf != NULL) {
314   if (iobuf->buf != NULL) NS_FREE(iobuf->buf);
315   iobuf_init(iobuf, 0);
316   }
317}
318
319size_t iobuf_append(struct iobuf *io, const void *buf, size_t len) {
320   char *p = NULL;
321
322   assert(io != NULL);
323   assert(io->len <= io->size);
324
325   if (len <= 0) {
326   } else if (io->len + len <= io->size) {
327   memcpy(io->buf + io->len, buf, len);
328   io->len += len;
329   } else if ((p = (char *) NS_REALLOC(io->buf, io->len + len)) != NULL) {
330   io->buf = p;
331   memcpy(io->buf + io->len, buf, len);
332   io->len += len;
333   io->size = io->len;
334   } else {
335   len = 0;
336   }
337
338   return len;
339}
340
341void iobuf_remove(struct iobuf *io, size_t n) {
342   if (n > 0 && n <= io->len) {
343   memmove(io->buf, io->buf + n, io->len - n);
344   io->len -= n;
345   }
346}
347
348#ifndef NS_DISABLE_THREADS
349void *ns_start_thread(void *(*f)(void *), void *p) {
350#ifdef _WIN32
351   return (void *) _beginthread((void (__cdecl *)(void *)) f, 0, p);
352#else
353   pthread_t thread_id = (pthread_t) 0;
354   pthread_attr_t attr;
355
356   (void) pthread_attr_init(&attr);
357   (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
358
359#if defined(NS_STACK_SIZE) && NS_STACK_SIZE > 1
360   (void) pthread_attr_setstacksize(&attr, NS_STACK_SIZE);
361#endif
362
363   pthread_create(&thread_id, &attr, f, p);
364   pthread_attr_destroy(&attr);
365
366   return (void *) thread_id;
367#endif
368}
369#endif  // NS_DISABLE_THREADS
370
371static void ns_add_conn(struct ns_server *server, struct ns_connection *c) {
372   c->next = server->active_connections;
373   server->active_connections = c;
374   c->prev = NULL;
375   if (c->next != NULL) c->next->prev = c;
376}
377
378static void ns_remove_conn(struct ns_connection *conn) {
379   if (conn->prev == NULL) conn->server->active_connections = conn->next;
380   if (conn->prev) conn->prev->next = conn->next;
381   if (conn->next) conn->next->prev = conn->prev;
382}
383
384// Print message to buffer. If buffer is large enough to hold the message,
385// return buffer. If buffer is to small, allocate large enough buffer on heap,
386// and return allocated buffer.
387int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
388   va_list ap_copy;
389   int len;
390
391   va_copy(ap_copy, ap);
392   len = vsnprintf(*buf, size, fmt, ap_copy);
393   va_end(ap_copy);
394
395   if (len < 0) {
396   // eCos and Windows are not standard-compliant and return -1 when
397   // the buffer is too small. Keep allocating larger buffers until we
398   // succeed or out of memory.
399   *buf = NULL;
400   while (len < 0) {
401      if (*buf) free(*buf);
402      size *= 2;
403      if ((*buf = (char *) NS_MALLOC(size)) == NULL) break;
404      va_copy(ap_copy, ap);
405      len = vsnprintf(*buf, size, fmt, ap_copy);
406      va_end(ap_copy);
407   }
408   } else if (len > (int) size) {
409   // Standard-compliant code path. Allocate a buffer that is large enough.
410   if ((*buf = (char *) NS_MALLOC(len + 1)) == NULL) {
411      len = -1;
412   } else {
413      va_copy(ap_copy, ap);
414      len = vsnprintf(*buf, len + 1, fmt, ap_copy);
415      va_end(ap_copy);
416   }
417   }
418
419   return len;
420}
421
422int ns_vprintf(struct ns_connection *conn, const char *fmt, va_list ap) {
423   char mem[2000], *buf = mem;
424   int len;
425
426   if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
427   iobuf_append(&conn->send_iobuf, buf, len);
428   }
429   if (buf != mem && buf != NULL) {
430   free(buf);
431   }
432
433   return len;
434}
435
436int ns_printf(struct ns_connection *conn, const char *fmt, ...) {
437   int len;
438   va_list ap;
439   va_start(ap, fmt);
440   len = ns_vprintf(conn, fmt, ap);
441   va_end(ap);
442   return len;
443}
444
445static void hexdump(struct ns_connection *nc, const char *path,
446               int num_bytes, enum ns_event ev) {
447   const struct iobuf *io = ev == NS_SEND ? &nc->send_iobuf : &nc->recv_iobuf;
448   FILE *fp;
449   char *buf, src[60], dst[60];
450   int buf_size = num_bytes * 5 + 100;
451
452   if ((fp = fopen(path, "a")) != NULL) {
453   ns_sock_to_str(nc->sock, src, sizeof(src), 3);
454   ns_sock_to_str(nc->sock, dst, sizeof(dst), 7);
455   fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) time(NULL),
456         nc->connection_data, src,
457         ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" :
458         ev == NS_ACCEPT ? "<A" : ev == NS_CONNECT ? "C>" : "XX",
459         dst, num_bytes);
460   if (num_bytes > 0 && (buf = (char *) malloc(buf_size)) != NULL) {
461      ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) -
462      (ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size);
463      fprintf(fp, "%s", buf);
464      free(buf);
465   }
466   fclose(fp);
467   }
468}
469
470static void ns_call(struct ns_connection *conn, enum ns_event ev, void *p) {
471   if (conn->server->hexdump_file != NULL && ev != NS_POLL) {
472   int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) p : 0;
473   hexdump(conn, conn->server->hexdump_file, len, ev);
474   }
475   if (conn->server->callback) conn->server->callback(conn, ev, p);
476}
477
478static void ns_close_conn(struct ns_connection *conn) {
479   DBG(("%p %d", conn, conn->flags));
480   ns_call(conn, NS_CLOSE, NULL);
481   ns_remove_conn(conn);
482   closesocket(conn->sock);
483   iobuf_free(&conn->recv_iobuf);
484   iobuf_free(&conn->send_iobuf);
485#ifdef NS_ENABLE_SSL
486   if (conn->ssl != NULL) {
487   SSL_free(conn->ssl);
488   }
489#endif
490   NS_FREE(conn);
491}
492
493void ns_set_close_on_exec(sock_t sock) {
494#ifdef _WIN32
495   (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
496#else
497   fcntl(sock, F_SETFD, FD_CLOEXEC);
498#endif
499}
500
501static void ns_set_non_blocking_mode(sock_t sock) {
502#ifdef _WIN32
503   unsigned long on = 1;
504   ioctlsocket(sock, FIONBIO, &on);
505#else
506   int flags = fcntl(sock, F_GETFL, 0);
507   fcntl(sock, F_SETFL, flags | O_NONBLOCK);
508#endif
509}
510
511#ifndef NS_DISABLE_SOCKETPAIR
512int ns_socketpair2(sock_t sp[2], int sock_type) {
513   union socket_address sa;
514   sock_t sock;
515   socklen_t len = sizeof(sa.sin);
516   int ret = 0;
517
518   sp[0] = sp[1] = INVALID_SOCKET;
519
520   (void) memset(&sa, 0, sizeof(sa));
521   sa.sin.sin_family = AF_INET;
522   sa.sin.sin_port = htons(0);
523   sa.sin.sin_addr.s_addr = htonl(0x7f000001);
524
525   if ((sock = socket(AF_INET, sock_type, 0)) != INVALID_SOCKET &&
526      !bind(sock, &sa.sa, len) &&
527      (sock_type == SOCK_DGRAM || !listen(sock, 1)) &&
528      !getsockname(sock, &sa.sa, &len) &&
529      (sp[0] = socket(AF_INET, sock_type, 0)) != INVALID_SOCKET &&
530      !connect(sp[0], &sa.sa, len) &&
531      (sock_type == SOCK_STREAM ||
532      (!getsockname(sp[0], &sa.sa, &len) && !connect(sock, &sa.sa, len))) &&
533      (sp[1] = (sock_type == SOCK_DGRAM ? sock :
534            accept(sock, &sa.sa, &len))) != INVALID_SOCKET) {
535   ns_set_close_on_exec(sp[0]);
536   ns_set_close_on_exec(sp[1]);
537   ret = 1;
538   } else {
539   if (sp[0] != INVALID_SOCKET) closesocket(sp[0]);
540   if (sp[1] != INVALID_SOCKET) closesocket(sp[1]);
541   sp[0] = sp[1] = INVALID_SOCKET;
542   }
543   if (sock_type != SOCK_DGRAM) closesocket(sock);
544
545   return ret;
546}
547
548int ns_socketpair(sock_t sp[2]) {
549   return ns_socketpair2(sp, SOCK_STREAM);
550}
551#endif  // NS_DISABLE_SOCKETPAIR
552
553// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128"
554static int ns_parse_port_string(const char *str, union socket_address *sa) {
555   unsigned int a, b, c, d, port;
556   int len = 0;
557#ifdef NS_ENABLE_IPV6
558   char buf[100];
559#endif
560
561   // MacOS needs that. If we do not zero it, subsequent bind() will fail.
562   // Also, all-zeroes in the socket address means binding to all addresses
563   // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
564   memset(sa, 0, sizeof(*sa));
565   sa->sin.sin_family = AF_INET;
566
567   if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
568   // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
569   sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
570   sa->sin.sin_port = htons((uint16_t) port);
571#ifdef NS_ENABLE_IPV6
572   } else if (sscanf(str, "[%49[^]]]:%u%n", buf, &port, &len) == 2 &&
573            inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
574   // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
575   sa->sin6.sin6_family = AF_INET6;
576   sa->sin6.sin6_port = htons((uint16_t) port);
577#endif
578   } else if (sscanf(str, "%u%n", &port, &len) == 1) {
579   // If only port is specified, bind to IPv4, INADDR_ANY
580   sa->sin.sin_port = htons((uint16_t) port);
581   } else {
582   port = 0;   // Parsing failure. Make port invalid.
583   }
584
585   return port <= 0xffff && str[len] == '\0';
586}
587
588// 'sa' must be an initialized address to bind to
589static sock_t ns_open_listening_socket(union socket_address *sa) {
590   socklen_t len = sizeof(*sa);
591   sock_t sock = INVALID_SOCKET;
592#ifndef _WIN32
593   int on = 1;
594#endif
595
596   if ((sock = socket(sa->sa.sa_family, SOCK_STREAM, 6)) != INVALID_SOCKET &&
597#ifndef _WIN32
598      // SO_RESUSEADDR is not enabled on Windows because the semantics of
599      // SO_REUSEADDR on UNIX and Windows is different. On Windows,
600      // SO_REUSEADDR allows to bind a socket to a port without error even if
601      // the port is already open by another program. This is not the behavior
602      // SO_REUSEADDR was designed for, and leads to hard-to-track failure
603      // scenarios. Therefore, SO_REUSEADDR was disabled on Windows.
604      !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) &&
605#endif
606      !bind(sock, &sa->sa, sa->sa.sa_family == AF_INET ?
607         sizeof(sa->sin) : sizeof(sa->sin6)) &&
608      !listen(sock, SOMAXCONN)) {
609   ns_set_non_blocking_mode(sock);
610   // In case port was set to 0, get the real port number
611   (void) getsockname(sock, &sa->sa, &len);
612   } else if (sock != INVALID_SOCKET) {
613   closesocket(sock);
614   sock = INVALID_SOCKET;
615   }
616
617   return sock;
618}
619
620// Certificate generation script is at
621// https://github.com/cesanta/net_skeleton/blob/master/examples/gen_certs.sh
622int ns_set_ssl_ca_cert(struct ns_server *server, const char *cert) {
623#ifdef NS_ENABLE_SSL
624   STACK_OF(X509_NAME) *list = SSL_load_client_CA_file(cert);
625   if (cert != NULL && server->ssl_ctx != NULL && list != NULL) {
626   SSL_CTX_set_client_CA_list(server->ssl_ctx, list);
627   SSL_CTX_set_verify(server->ssl_ctx, SSL_VERIFY_PEER |
628                  SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
629   return 0;
630   }
631#endif
632   return server != NULL && cert == NULL ? 0 : -1;
633}
634
635int ns_set_ssl_cert(struct ns_server *server, const char *cert) {
636#ifdef NS_ENABLE_SSL
637   if (cert != NULL &&
638      (server->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
639   return -1;
640   } else if (SSL_CTX_use_certificate_file(server->ssl_ctx, cert, 1) == 0 ||
641            SSL_CTX_use_PrivateKey_file(server->ssl_ctx, cert, 1) == 0) {
642   return -2;
643   } else {
644   SSL_CTX_set_mode(server->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
645   SSL_CTX_use_certificate_chain_file(server->ssl_ctx, cert);
646   return 0;
647   }
648#endif
649   return server != NULL && cert == NULL ? 0 : -3;
650}
651
652int ns_bind(struct ns_server *server, const char *str) {
653   union socket_address sa;
654   ns_parse_port_string(str, &sa);
655   if (server->listening_sock != INVALID_SOCKET) {
656   closesocket(server->listening_sock);
657   }
658   server->listening_sock = ns_open_listening_socket(&sa);
659   return server->listening_sock == INVALID_SOCKET ? -1 :
660   (int) ntohs(sa.sin.sin_port);
661}
662
663
664static struct ns_connection *accept_conn(struct ns_server *server) {
665   struct ns_connection *c = NULL;
666   union socket_address sa;
667   socklen_t len = sizeof(sa);
668   sock_t sock = INVALID_SOCKET;
669
670   // NOTE(lsm): on Windows, sock is always > FD_SETSIZE
671   if ((sock = accept(server->listening_sock, &sa.sa, &len)) == INVALID_SOCKET) {
672   } else if ((c = (struct ns_connection *) NS_MALLOC(sizeof(*c))) == NULL ||
673            memset(c, 0, sizeof(*c)) == NULL) {
674   closesocket(sock);
675#ifdef NS_ENABLE_SSL
676   } else if (server->ssl_ctx != NULL &&
677            ((c->ssl = SSL_new(server->ssl_ctx)) == NULL ||
678            SSL_set_fd(c->ssl, sock) != 1)) {
679   DBG(("SSL error"));
680   closesocket(sock);
681   free(c);
682   c = NULL;
683#endif
684   } else {
685   ns_set_close_on_exec(sock);
686   ns_set_non_blocking_mode(sock);
687   c->server = server;
688   c->sock = sock;
689   c->flags |= NSF_ACCEPTED;
690
691   ns_add_conn(server, c);
692   ns_call(c, NS_ACCEPT, &sa);
693   DBG(("%p %d %p %p", c, c->sock, c->ssl, server->ssl_ctx));
694   }
695
696   return c;
697}
698
699static int ns_is_error(int n) {
700   return n == 0 ||
701   (n < 0 && errno != EINTR && errno != EINPROGRESS &&
702      errno != EAGAIN && errno != EWOULDBLOCK
703#ifdef _WIN32
704      && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK
705#endif
706   );
707}
708
709void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) {
710   union socket_address sa;
711   socklen_t slen = sizeof(sa);
712
713   if (buf != NULL && len > 0) {
714   buf[0] = '\0';
715   memset(&sa, 0, sizeof(sa));
716   if (flags & 4) {
717      getpeername(sock, &sa.sa, &slen);
718   } else {
719      getsockname(sock, &sa.sa, &slen);
720   }
721   if (flags & 1) {
722#if defined(NS_ENABLE_IPV6)
723      inet_ntop(sa.sa.sa_family, sa.sa.sa_family == AF_INET ?
724            (void *) &sa.sin.sin_addr :
725            (void *) &sa.sin6.sin6_addr, buf, len);
726#elif defined(_WIN32)
727      // Only Windoze Vista (and newer) have inet_ntop()
728      strncpy(buf, inet_ntoa(sa.sin.sin_addr), len);
729#else
730      inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf, len);
731#endif
732   }
733   if (flags & 2) {
734      snprintf(buf + strlen(buf), len - (strlen(buf) + 1), "%s%d",
735            flags & 1 ? ":" : "", (int) ntohs(sa.sin.sin_port));
736   }
737   }
738}
739
740int ns_hexdump(const void *buf, int len, char *dst, int dst_len) {
741   const unsigned char *p = (const unsigned char *) buf;
742   char ascii[17] = "";
743   int i, idx, n = 0;
744
745   for (i = 0; i < len; i++) {
746   idx = i % 16;
747   if (idx == 0) {
748      if (i > 0) n += snprintf(dst + n, dst_len - n, "  %s\n", ascii);
749      n += snprintf(dst + n, dst_len - n, "%04x ", i);
750   }
751   n += snprintf(dst + n, dst_len - n, " %02x", p[i]);
752   ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i];
753   ascii[idx + 1] = '\0';
754   }
755
756   while (i++ % 16) n += snprintf(dst + n, dst_len - n, "%s", "   ");
757   n += snprintf(dst + n, dst_len - n, "  %s\n\n", ascii);
758
759   return n;
760}
761
762#ifdef NS_ENABLE_SSL
763static int ns_ssl_err(struct ns_connection *conn, int res) {
764   int ssl_err = SSL_get_error(conn->ssl, res);
765   if (ssl_err == SSL_ERROR_WANT_READ) conn->flags |= NSF_WANT_READ;
766   if (ssl_err == SSL_ERROR_WANT_WRITE) conn->flags |= NSF_WANT_WRITE;
767   return ssl_err;
768}
769#endif
770
771static void ns_read_from_socket(struct ns_connection *conn) {
772   char buf[2048];
773   int n = 0;
774
775   if (conn->flags & NSF_CONNECTING) {
776   int ok = 1, ret;
777   socklen_t len = sizeof(ok);
778
779   ret = getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &ok, &len);
780   (void) ret;
781#ifdef NS_ENABLE_SSL
782   if (ret == 0 && ok == 0 && conn->ssl != NULL) {
783      int res = SSL_connect(conn->ssl);
784      int ssl_err = ns_ssl_err(conn, res);
785      if (res == 1) {
786      conn->flags |= NSF_SSL_HANDSHAKE_DONE;
787      } else if (ssl_err == SSL_ERROR_WANT_READ ||
788               ssl_err == SSL_ERROR_WANT_WRITE) {
789      return; // Call us again
790      } else {
791      ok = 1;
792      }
793   }
794#endif
795   conn->flags &= ~NSF_CONNECTING;
796   DBG(("%p ok=%d", conn, ok));
797   if (ok != 0) {
798      conn->flags |= NSF_CLOSE_IMMEDIATELY;
799   }
800   ns_call(conn, NS_CONNECT, &ok);
801   return;
802   }
803
804#ifdef NS_ENABLE_SSL
805   if (conn->ssl != NULL) {
806   if (conn->flags & NSF_SSL_HANDSHAKE_DONE) {
807      // SSL library may have more bytes ready to read then we ask to read.
808      // Therefore, read in a loop until we read everything. Without the loop,
809      // we skip to the next select() cycle which can just timeout.
810      while ((n = SSL_read(conn->ssl, buf, sizeof(buf))) > 0) {
811      DBG(("%p %d <- %d bytes (SSL)", conn, conn->flags, n));
812      iobuf_append(&conn->recv_iobuf, buf, n);
813      ns_call(conn, NS_RECV, &n);
814      }
815      ns_ssl_err(conn, n);
816   } else {
817      int res = SSL_accept(conn->ssl);
818      int ssl_err = ns_ssl_err(conn, res);
819      if (res == 1) {
820      conn->flags |= NSF_SSL_HANDSHAKE_DONE;
821      } else if (ssl_err == SSL_ERROR_WANT_READ ||
822               ssl_err == SSL_ERROR_WANT_WRITE) {
823      return; // Call us again
824      } else {
825      conn->flags |= NSF_CLOSE_IMMEDIATELY;
826      }
827      return;
828   }
829   } else
830#endif
831   {
832   while ((n = recv(conn->sock, buf, sizeof(buf), 0)) > 0) {
833      DBG(("%p %d <- %d bytes (PLAIN)", conn, conn->flags, n));
834      iobuf_append(&conn->recv_iobuf, buf, n);
835      ns_call(conn, NS_RECV, &n);
836   }
837   }
838
839   if (ns_is_error(n)) {
840   conn->flags |= NSF_CLOSE_IMMEDIATELY;
841   }
842}
843
844static void ns_write_to_socket(struct ns_connection *conn) {
845   struct iobuf *io = &conn->send_iobuf;
846   int n = 0;
847
848#ifdef NS_ENABLE_SSL
849   if (conn->ssl != NULL) {
850   n = SSL_write(conn->ssl, io->buf, io->len);
851   if (n <= 0) {
852      int ssl_err = ns_ssl_err(conn, n);
853      if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) {
854      return; // Call us again
855      } else {
856      conn->flags |= NSF_CLOSE_IMMEDIATELY;
857      }
858   }
859   } else
860#endif
861   { n = send(conn->sock, io->buf, io->len, 0); }
862
863   DBG(("%p %d -> %d bytes", conn, conn->flags, n));
864
865   ns_call(conn, NS_SEND, &n);
866   if (ns_is_error(n)) {
867   conn->flags |= NSF_CLOSE_IMMEDIATELY;
868   } else if (n > 0) {
869   iobuf_remove(io, n);
870   }
871}
872
873int ns_send(struct ns_connection *conn, const void *buf, int len) {
874   return iobuf_append(&conn->send_iobuf, buf, len);
875}
876
877static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
878   if (sock != INVALID_SOCKET) {
879   FD_SET(sock, set);
880   if (*max_fd == INVALID_SOCKET || sock > *max_fd) {
881      *max_fd = sock;
882   }
883   }
884}
885
886int ns_server_poll(struct ns_server *server, int milli) {
887   struct ns_connection *conn, *tmp_conn;
888   struct timeval tv;
889   fd_set read_set, write_set;
890   int num_active_connections = 0;
891   sock_t max_fd = INVALID_SOCKET;
892   time_t current_time = time(NULL);
893
894   if (server->listening_sock == INVALID_SOCKET &&
895      server->active_connections == NULL) return 0;
896
897   FD_ZERO(&read_set);
898   FD_ZERO(&write_set);
899   ns_add_to_set(server->listening_sock, &read_set, &max_fd);
900   ns_add_to_set(server->ctl[1], &read_set, &max_fd);
901
902   for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
903   tmp_conn = conn->next;
904   ns_call(conn, NS_POLL, &current_time);
905   if (!(conn->flags & NSF_WANT_WRITE)) {
906      //DBG(("%p read_set", conn));
907      ns_add_to_set(conn->sock, &read_set, &max_fd);
908   }
909   if (((conn->flags & NSF_CONNECTING) && !(conn->flags & NSF_WANT_READ)) ||
910      (conn->send_iobuf.len > 0 && !(conn->flags & NSF_CONNECTING) &&
911         !(conn->flags & NSF_BUFFER_BUT_DONT_SEND))) {
912      //DBG(("%p write_set", conn));
913      ns_add_to_set(conn->sock, &write_set, &max_fd);
914   }
915   if (conn->flags & NSF_CLOSE_IMMEDIATELY) {
916      ns_close_conn(conn);
917   }
918   }
919
920   tv.tv_sec = milli / 1000;
921   tv.tv_usec = (milli % 1000) * 1000;
922
923   if (select((int) max_fd + 1, &read_set, &write_set, NULL, &tv) > 0) {
924   // select() might have been waiting for a long time, reset current_time
925   // now to prevent last_io_time being set to the past.
926   current_time = time(NULL);
927
928   // Accept new connections
929   if (server->listening_sock != INVALID_SOCKET &&
930      FD_ISSET(server->listening_sock, &read_set)) {
931      // We're not looping here, and accepting just one connection at
932      // a time. The reason is that eCos does not respect non-blocking
933      // flag on a listening socket and hangs in a loop.
934      if ((conn = accept_conn(server)) != NULL) {
935      conn->last_io_time = current_time;
936      }
937   }
938
939   // Read wakeup messages
940   if (server->ctl[1] != INVALID_SOCKET &&
941      FD_ISSET(server->ctl[1], &read_set)) {
942      struct ctl_msg ctl_msg;
943      int len = recv(server->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0);
944      send(server->ctl[1], ctl_msg.message, 1, 0);
945      if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) {
946      ns_iterate(server, ctl_msg.callback, ctl_msg.message);
947      }
948   }
949
950   for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
951      tmp_conn = conn->next;
952      if (FD_ISSET(conn->sock, &read_set)) {
953      conn->last_io_time = current_time;
954      ns_read_from_socket(conn);
955      }
956      if (FD_ISSET(conn->sock, &write_set)) {
957      if (conn->flags & NSF_CONNECTING) {
958         ns_read_from_socket(conn);
959      } else if (!(conn->flags & NSF_BUFFER_BUT_DONT_SEND)) {
960         conn->last_io_time = current_time;
961         ns_write_to_socket(conn);
962      }
963      }
964   }
965   }
966
967   for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
968   tmp_conn = conn->next;
969   num_active_connections++;
970   if ((conn->flags & NSF_CLOSE_IMMEDIATELY) ||
971      (conn->send_iobuf.len == 0 &&
972         (conn->flags & NSF_FINISHED_SENDING_DATA))) {
973      ns_close_conn(conn);
974   }
975   }
976   //DBG(("%d active connections", num_active_connections));
977
978   return num_active_connections;
979}
980
981struct ns_connection *ns_connect(struct ns_server *server, const char *host,
982                           int port, int use_ssl, void *param) {
983   sock_t sock = INVALID_SOCKET;
984   struct sockaddr_in sin;
985   struct hostent *he = NULL;
986   struct ns_connection *conn = NULL;
987   int connect_ret_val;
988
989   (void) use_ssl;
990
991   if (host == NULL || (he = gethostbyname(host)) == NULL ||
992      (sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
993   DBG(("gethostbyname(%s) failed: %s", host, strerror(errno)));
994   return NULL;
995   }
996
997   sin.sin_family = AF_INET;
998   sin.sin_port = htons((uint16_t) port);
999   sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
1000   ns_set_non_blocking_mode(sock);
1001
1002   connect_ret_val = connect(sock, (struct sockaddr *) &sin, sizeof(sin));
1003   if (ns_is_error(connect_ret_val)) {
1004   closesocket(sock);
1005   return NULL;
1006   } else if ((conn = (struct ns_connection *)
1007            NS_MALLOC(sizeof(*conn))) == NULL) {
1008   closesocket(sock);
1009   return NULL;
1010   }
1011
1012   memset(conn, 0, sizeof(*conn));
1013   conn->server = server;
1014   conn->sock = sock;
1015   conn->connection_data = param;
1016   conn->flags = NSF_CONNECTING;
1017   conn->last_io_time = time(NULL);
1018
1019#ifdef NS_ENABLE_SSL
1020   if (use_ssl &&
1021      (conn->ssl = SSL_new(server->client_ssl_ctx)) != NULL) {
1022   SSL_set_fd(conn->ssl, sock);
1023   }
1024#endif
1025
1026   ns_add_conn(server, conn);
1027   DBG(("%p %s:%d %d %p", conn, host, port, conn->sock, conn->ssl));
1028
1029   return conn;
1030}
1031
1032struct ns_connection *ns_add_sock(struct ns_server *s, sock_t sock, void *p) {
1033   struct ns_connection *conn;
1034   if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) {
1035   memset(conn, 0, sizeof(*conn));
1036   ns_set_non_blocking_mode(sock);
1037   conn->sock = sock;
1038   conn->connection_data = p;
1039   conn->server = s;
1040   conn->last_io_time = time(NULL);
1041   ns_add_conn(s, conn);
1042   DBG(("%p %d", conn, sock));
1043   }
1044   return conn;
1045}
1046
1047struct ns_connection *ns_next(struct ns_server *s, struct ns_connection *conn) {
1048   return conn == NULL ? s->active_connections : conn->next;
1049}
1050
1051void ns_iterate(struct ns_server *server, ns_callback_t cb, void *param) {
1052   struct ns_connection *conn, *tmp_conn;
1053
1054   for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
1055   tmp_conn = conn->next;
1056   cb(conn, NS_POLL, param);
1057   }
1058}
1059
1060void ns_server_wakeup_ex(struct ns_server *server, ns_callback_t cb,
1061                     void *data, size_t len) {
1062   struct ctl_msg ctl_msg;
1063   if (server->ctl[0] != INVALID_SOCKET && data != NULL &&
1064      len < sizeof(ctl_msg.message)) {
1065   ctl_msg.callback = cb;
1066   memcpy(ctl_msg.message, data, len);
1067   send(server->ctl[0], (char *) &ctl_msg,
1068         offsetof(struct ctl_msg, message) + len, 0);
1069   recv(server->ctl[0], (char *) &len, 1, 0);
1070   }
1071}
1072
1073void ns_server_wakeup(struct ns_server *server) {
1074   ns_server_wakeup_ex(server, NULL, (void *) "", 0);
1075}
1076
1077void ns_server_init(struct ns_server *s, void *server_data, ns_callback_t cb) {
1078   memset(s, 0, sizeof(*s));
1079   s->listening_sock = s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
1080   s->server_data = server_data;
1081   s->callback = cb;
1082
1083#ifdef _WIN32
1084   { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); }
1085#else
1086   // Ignore SIGPIPE signal, so if client cancels the request, it
1087   // won't kill the whole process.
1088   signal(SIGPIPE, SIG_IGN);
1089#endif
1090
1091#ifndef NS_DISABLE_SOCKETPAIR
1092   do {
1093   ns_socketpair2(s->ctl, SOCK_DGRAM);
1094   } while (s->ctl[0] == INVALID_SOCKET);
1095#endif
1096
1097#ifdef NS_ENABLE_SSL
1098   {static int init_done; if (!init_done) { SSL_library_init(); init_done++; }}
1099   s->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
1100#endif
1101}
1102
1103void ns_server_free(struct ns_server *s) {
1104   struct ns_connection *conn, *tmp_conn;
1105
1106   DBG(("%p", s));
1107   if (s == NULL) return;
1108   // Do one last poll, see https://github.com/cesanta/mongoose/issues/286
1109   ns_server_poll(s, 0);
1110
1111   if (s->listening_sock != INVALID_SOCKET) closesocket(s->listening_sock);
1112   if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]);
1113   if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]);
1114   s->listening_sock = s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
1115
1116   for (conn = s->active_connections; conn != NULL; conn = tmp_conn) {
1117   tmp_conn = conn->next;
1118   ns_close_conn(conn);
1119   }
1120
1121#ifdef NS_ENABLE_SSL
1122   if (s->ssl_ctx != NULL) SSL_CTX_free(s->ssl_ctx);
1123   if (s->client_ssl_ctx != NULL) SSL_CTX_free(s->client_ssl_ctx);
1124   s->ssl_ctx = s->client_ssl_ctx = NULL;
1125#endif
1126}
1127// net_skeleton end
1128#endif  // NOEMBED_NET_SKELETON
1129
1130#include <ctype.h>
1131
1132#ifdef _WIN32         //////////////// Windows specific defines and includes
1133#include <io.h>       // For _lseeki64
1134#include <direct.h>   // For _mkdir
1135#ifndef S_ISDIR
1136#define S_ISDIR(x) ((x) & _S_IFDIR)
1137#endif
1138#ifdef stat
1139#undef stat
1140#endif
1141#ifdef lseek
1142#undef lseek
1143#endif
1144#ifdef popen
1145#undef popen
1146#endif
1147#ifdef pclose
1148#undef pclose
1149#endif
1150#define stat(x, y) mg_stat((x), (y))
1151#define fopen(x, y) mg_fopen((x), (y))
1152#define open(x, y) mg_open((x), (y))
1153#define lseek(x, y, z) _lseeki64((x), (y), (z))
1154#define popen(x, y) _popen((x), (y))
1155#define pclose(x) _pclose(x)
1156#define mkdir(x, y) _mkdir(x)
1157#ifndef __func__
1158#define STRX(x) #x
1159#define STR(x) STRX(x)
1160#define __func__ __FILE__ ":" STR(__LINE__)
1161#endif
1162/* MINGW has adopted the MSVC formatting for 64-bit ints as of gcc 4.4 till 4.8*/
1163#if (defined(__MINGW32__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4 && __GNUC_MINOR__ < 8))) || defined(_MSC_VER)
1164#define INT64_FMT   "I64d"
1165#else
1166#define INT64_FMT   "lld"
1167#endif
1168#define stat(x, y) mg_stat((x), (y))
1169#define fopen(x, y) mg_fopen((x), (y))
1170#define open(x, y) mg_open((x), (y))
1171#define flockfile(x)      ((void) (x))
1172#define funlockfile(x)    ((void) (x))
1173typedef struct _stati64 file_stat_t;
1174typedef HANDLE process_id_t;
1175#else                    ////////////// UNIX specific defines and includes
1176#include <dirent.h>
1177#include <dlfcn.h>
1178#include <inttypes.h>
1179#include <pwd.h>
1180#ifndef __OS2__
1181#define O_BINARY 0
1182#endif
1183#define INT64_FMT PRId64
1184typedef struct stat file_stat_t;
1185typedef pid_t process_id_t;
1186#endif                  //////// End of platform-specific defines and includes
1187
1188#include "mongoose.h"
1189
1190#define MAX_REQUEST_SIZE 16384
1191#define IOBUF_SIZE 8192
1192#define MAX_PATH_SIZE 8192
1193#define DEFAULT_CGI_PATTERN "**.cgi$|**.pl$|**.php$"
1194#define CGI_ENVIRONMENT_SIZE 8192
1195#define MAX_CGI_ENVIR_VARS 64
1196#define ENV_EXPORT_TO_CGI "MONGOOSE_CGI"
1197#define PASSWORDS_FILE_NAME ".htpasswd"
1198
1199#ifndef MONGOOSE_USE_WEBSOCKET_PING_INTERVAL
1200#define MONGOOSE_USE_WEBSOCKET_PING_INTERVAL 5
1201#endif
1202
1203// Extra HTTP headers to send in every static file reply
1204#if !defined(MONGOOSE_USE_EXTRA_HTTP_HEADERS)
1205#define MONGOOSE_USE_EXTRA_HTTP_HEADERS ""
1206#endif
1207
1208#ifndef MONGOOSE_POST_SIZE_LIMIT
1209#define MONGOOSE_POST_SIZE_LIMIT 0
1210#endif
1211
1212#ifndef MONGOOSE_IDLE_TIMEOUT_SECONDS
1213#define MONGOOSE_IDLE_TIMEOUT_SECONDS 300
1214#endif
1215
1216#ifdef NS_DISABLE_SOCKETPAIR
1217#define MONGOOSE_NO_CGI
1218#endif
1219
1220#ifdef MONGOOSE_NO_FILESYSTEM
1221#define MONGOOSE_NO_AUTH
1222#define MONGOOSE_NO_CGI
1223#define MONGOOSE_NO_DAV
1224#define MONGOOSE_NO_DIRECTORY_LISTING
1225#define MONGOOSE_NO_LOGGING
1226#define MONGOOSE_NO_SSI
1227#define MONGOOSE_NO_DL
1228#endif
1229
1230struct vec {
1231   const char *ptr;
1232   int len;
1233};
1234
1235// For directory listing and WevDAV support
1236struct dir_entry {
1237   struct connection *conn;
1238   char *file_name;
1239   file_stat_t st;
1240};
1241
1242// NOTE(lsm): this enum shoulds be in sync with the config_options.
1243enum {
1244   ACCESS_CONTROL_LIST,
1245#ifndef MONGOOSE_NO_FILESYSTEM
1246   ACCESS_LOG_FILE,
1247#ifndef MONGOOSE_NO_AUTH
1248   AUTH_DOMAIN,
1249#endif
1250#ifndef MONGOOSE_NO_CGI
1251   CGI_INTERPRETER,
1252   CGI_PATTERN,
1253#endif
1254   DAV_AUTH_FILE,
1255   DOCUMENT_ROOT,
1256#ifndef MONGOOSE_NO_DIRECTORY_LISTING
1257   ENABLE_DIRECTORY_LISTING,
1258#endif
1259#endif
1260   ENABLE_PROXY,
1261   EXTRA_MIME_TYPES,
1262#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH)
1263   GLOBAL_AUTH_FILE,
1264#endif
1265#ifndef MONGOOSE_NO_FILESYSTEM
1266   HIDE_FILES_PATTERN,
1267   HEXDUMP_FILE,
1268   INDEX_FILES,
1269#endif
1270   LISTENING_PORT,
1271#ifndef _WIN32
1272   RUN_AS_USER,
1273#endif
1274#ifndef MONGOOSE_NO_SSI
1275   SSI_PATTERN,
1276#endif
1277#ifdef NS_ENABLE_SSL
1278   SSL_CERTIFICATE,
1279   SSL_CA_CERTIFICATE,
1280   SSL_MITM_CERTS,
1281#endif
1282   URL_REWRITES,
1283   NUM_OPTIONS
1284};
1285
1286static const char *static_config_options[] = {
1287   "access_control_list", NULL,
1288#ifndef MONGOOSE_NO_FILESYSTEM
1289   "access_log_file", NULL,
1290#ifndef MONGOOSE_NO_AUTH
1291   "auth_domain", "mydomain.com",
1292#endif
1293#ifndef MONGOOSE_NO_CGI
1294   "cgi_interpreter", NULL,
1295   "cgi_pattern", DEFAULT_CGI_PATTERN,
1296#endif
1297   "dav_auth_file", NULL,
1298   "document_root",  NULL,
1299#ifndef MONGOOSE_NO_DIRECTORY_LISTING
1300   "enable_directory_listing", "yes",
1301#endif
1302#endif
1303   "enable_proxy", NULL,
1304   "extra_mime_types", NULL,
1305#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH)
1306   "global_auth_file", NULL,
1307#endif
1308#ifndef MONGOOSE_NO_FILESYSTEM
1309   "hide_files_patterns", NULL,
1310   "hexdump_file", NULL,
1311   "index_files","index.html,index.htm,index.shtml,index.cgi,index.php,index.lp",
1312#endif
1313   "listening_port", NULL,
1314#ifndef _WIN32
1315   "run_as_user", NULL,
1316#endif
1317#ifndef MONGOOSE_NO_SSI
1318   "ssi_pattern", "**.shtml$|**.shtm$",
1319#endif
1320#ifdef NS_ENABLE_SSL
1321   "ssl_certificate", NULL,
1322   "ssl_ca_certificate", NULL,
1323   "ssl_mitm_certs", NULL,
1324#endif
1325   "url_rewrites", NULL,
1326   NULL
1327};
1328
1329struct mg_server {
1330   struct ns_server ns_server;
1331   union socket_address lsa;   // Listening socket address
1332   mg_handler_t event_handler;
1333   char *config_options[NUM_OPTIONS];
1334};
1335
1336// Local endpoint representation
1337union endpoint {
1338   int fd;                     // Opened regular local file
1339   struct ns_connection *nc;   // CGI or proxy->target connection
1340};
1341
1342enum endpoint_type {
1343   EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT, EP_CLIENT, EP_PROXY
1344};
1345
1346#define MG_HEADERS_SENT NSF_USER_1
1347#define MG_LONG_RUNNING NSF_USER_2
1348#define MG_CGI_CONN NSF_USER_3
1349#define MG_PROXY_CONN NSF_USER_4
1350#define MG_PROXY_DONT_PARSE NSF_USER_5
1351
1352struct connection {
1353   struct ns_connection *ns_conn;  // NOTE(lsm): main.c depends on this order
1354   struct mg_connection mg_conn;
1355   struct mg_server *server;
1356   union endpoint endpoint;
1357   enum endpoint_type endpoint_type;
1358   char *path_info;
1359   char *request;
1360   int64_t num_bytes_recv; // Total number of bytes received
1361   int64_t cl;             // Reply content length, for Range support
1362   int request_len;  // Request length, including last \r\n after last header
1363};
1364
1365#define MG_CONN_2_CONN(c) ((struct connection *) ((char *) (c) - \
1366   offsetof(struct connection, mg_conn)))
1367
1368static void open_local_endpoint(struct connection *conn, int skip_user);
1369static void close_local_endpoint(struct connection *conn);
1370
1371static const struct {
1372   const char *extension;
1373   size_t ext_len;
1374   const char *mime_type;
1375} static_builtin_mime_types[] = {
1376   {".html", 5, "text/html"},
1377   {".htm", 4, "text/html"},
1378   {".shtm", 5, "text/html"},
1379   {".shtml", 6, "text/html"},
1380   {".css", 4, "text/css"},
1381   {".js",  3, "application/x-javascript"},
1382   {".ico", 4, "image/x-icon"},
1383   {".gif", 4, "image/gif"},
1384   {".jpg", 4, "image/jpeg"},
1385   {".jpeg", 5, "image/jpeg"},
1386   {".png", 4, "image/png"},
1387   {".svg", 4, "image/svg+xml"},
1388   {".txt", 4, "text/plain"},
1389   {".torrent", 8, "application/x-bittorrent"},
1390   {".wav", 4, "audio/x-wav"},
1391   {".mp3", 4, "audio/x-mp3"},
1392   {".mid", 4, "audio/mid"},
1393   {".m3u", 4, "audio/x-mpegurl"},
1394   {".ogg", 4, "application/ogg"},
1395   {".ram", 4, "audio/x-pn-realaudio"},
1396   {".xml", 4, "text/xml"},
1397   {".json",  5, "application/json"},
1398   {".xslt", 5, "application/xml"},
1399   {".xsl", 4, "application/xml"},
1400   {".ra",  3, "audio/x-pn-realaudio"},
1401   {".doc", 4, "application/msword"},
1402   {".exe", 4, "application/octet-stream"},
1403   {".zip", 4, "application/x-zip-compressed"},
1404   {".xls", 4, "application/excel"},
1405   {".tgz", 4, "application/x-tar-gz"},
1406   {".tar", 4, "application/x-tar"},
1407   {".gz",  3, "application/x-gunzip"},
1408   {".arj", 4, "application/x-arj-compressed"},
1409   {".rar", 4, "application/x-rar-compressed"},
1410   {".rtf", 4, "application/rtf"},
1411   {".pdf", 4, "application/pdf"},
1412   {".swf", 4, "application/x-shockwave-flash"},
1413   {".mpg", 4, "video/mpeg"},
1414   {".webm", 5, "video/webm"},
1415   {".mpeg", 5, "video/mpeg"},
1416   {".mov", 4, "video/quicktime"},
1417   {".mp4", 4, "video/mp4"},
1418   {".m4v", 4, "video/x-m4v"},
1419   {".asf", 4, "video/x-ms-asf"},
1420   {".avi", 4, "video/x-msvideo"},
1421   {".bmp", 4, "image/bmp"},
1422   {".ttf", 4, "application/x-font-ttf"},
1423   {NULL,  0, NULL}
1424};
1425
1426#ifndef MONGOOSE_NO_THREADS
1427void *mg_start_thread(void *(*f)(void *), void *p) {
1428   return ns_start_thread(f, p);
1429}
1430#endif  // MONGOOSE_NO_THREADS
1431
1432#if defined(_WIN32) && !defined(MONGOOSE_NO_FILESYSTEM)
1433// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
1434// wbuf and wbuf_len is a target buffer and its length.
1435static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
1436   char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p;
1437
1438   strncpy(buf, path, sizeof(buf));
1439   buf[sizeof(buf) - 1] = '\0';
1440
1441   // Trim trailing slashes. Leave backslash for paths like "X:\"
1442   p = buf + strlen(buf) - 1;
1443   while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
1444
1445   // Convert to Unicode and back. If doubly-converted string does not
1446   // match the original, something is fishy, reject.
1447   memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
1448   MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
1449   WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
1450                  NULL, NULL);
1451   if (strcmp(buf, buf2) != 0) {
1452   wbuf[0] = L'\0';
1453   }
1454}
1455
1456static int mg_stat(const char *path, file_stat_t *st) {
1457   wchar_t wpath[MAX_PATH_SIZE];
1458   to_wchar(path, wpath, ARRAY_SIZE(wpath));
1459   DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st)));
1460   return _wstati64(wpath, st);
1461}
1462
1463static FILE *mg_fopen(const char *path, const char *mode) {
1464   wchar_t wpath[MAX_PATH_SIZE], wmode[10];
1465   to_wchar(path, wpath, ARRAY_SIZE(wpath));
1466   to_wchar(mode, wmode, ARRAY_SIZE(wmode));
1467   return _wfopen(wpath, wmode);
1468}
1469
1470static int mg_open(const char *path, int flag) {
1471   wchar_t wpath[MAX_PATH_SIZE];
1472   to_wchar(path, wpath, ARRAY_SIZE(wpath));
1473   return _wopen(wpath, flag);
1474}
1475#endif // _WIN32 && !MONGOOSE_NO_FILESYSTEM
1476
1477// A helper function for traversing a comma separated list of values.
1478// It returns a list pointer shifted to the next value, or NULL if the end
1479// of the list found.
1480// Value is stored in val vector. If value has form "x=y", then eq_val
1481// vector is initialized to point to the "y" part, and val vector length
1482// is adjusted to point only to "x".
1483static const char *next_option(const char *list, struct vec *val,
1484                        struct vec *eq_val) {
1485   if (list == NULL || *list == '\0') {
1486   // End of the list
1487   list = NULL;
1488   } else {
1489   val->ptr = list;
1490   if ((list = strchr(val->ptr, ',')) != NULL) {
1491      // Comma found. Store length and shift the list ptr
1492      val->len = list - val->ptr;
1493      list++;
1494   } else {
1495      // This value is the last one
1496      list = val->ptr + strlen(val->ptr);
1497      val->len = list - val->ptr;
1498   }
1499
1500   if (eq_val != NULL) {
1501      // Value has form "x=y", adjust pointers and lengths
1502      // so that val points to "x", and eq_val points to "y".
1503      eq_val->len = 0;
1504      eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len);
1505      if (eq_val->ptr != NULL) {
1506      eq_val->ptr++;  // Skip over '=' character
1507      eq_val->len = val->ptr + val->len - eq_val->ptr;
1508      val->len = (eq_val->ptr - val->ptr) - 1;
1509      }
1510   }
1511   }
1512
1513   return list;
1514}
1515
1516// Like snprintf(), but never returns negative value, or a value
1517// that is larger than a supplied buffer.
1518static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap) {
1519   int n;
1520   if (buflen < 1) return 0;
1521   n = vsnprintf(buf, buflen, fmt, ap);
1522   if (n < 0) {
1523   n = 0;
1524   } else if (n >= (int) buflen) {
1525   n = (int) buflen - 1;
1526   }
1527   buf[n] = '\0';
1528   return n;
1529}
1530
1531static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) {
1532   va_list ap;
1533   int n;
1534   va_start(ap, fmt);
1535   n = mg_vsnprintf(buf, buflen, fmt, ap);
1536   va_end(ap);
1537   return n;
1538}
1539
1540// Check whether full request is buffered. Return:
1541//   -1  if request is malformed
1542//    0  if request is not yet fully buffered
1543//   >0  actual request length, including last \r\n\r\n
1544static int get_request_len(const char *s, int buf_len) {
1545   const unsigned char *buf = (unsigned char *) s;
1546   int i;
1547
1548   for (i = 0; i < buf_len; i++) {
1549   // Control characters are not allowed but >=128 are.
1550   // Abort scan as soon as one malformed character is found.
1551   if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) {
1552      return -1;
1553   } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
1554      return i + 2;
1555   } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
1556            buf[i + 2] == '\n') {
1557      return i + 3;
1558   }
1559   }
1560
1561   return 0;
1562}
1563
1564// Skip the characters until one of the delimiters characters found.
1565// 0-terminate resulting word. Skip the rest of the delimiters if any.
1566// Advance pointer to buffer to the next word. Return found 0-terminated word.
1567static char *skip(char **buf, const char *delimiters) {
1568   char *p, *begin_word, *end_word, *end_delimiters;
1569
1570   begin_word = *buf;
1571   end_word = begin_word + strcspn(begin_word, delimiters);
1572   end_delimiters = end_word + strspn(end_word, delimiters);
1573
1574   for (p = end_word; p < end_delimiters; p++) {
1575   *p = '\0';
1576   }
1577
1578   *buf = end_delimiters;
1579
1580   return begin_word;
1581}
1582
1583// Parse HTTP headers from the given buffer, advance buffer to the point
1584// where parsing stopped.
1585static void parse_http_headers(char **buf, struct mg_connection *ri) {
1586   size_t i;
1587
1588   for (i = 0; i < ARRAY_SIZE(ri->http_headers); i++) {
1589   ri->http_headers[i].name = skip(buf, ": ");
1590   ri->http_headers[i].value = skip(buf, "\r\n");
1591   if (ri->http_headers[i].name[0] == '\0')
1592      break;
1593   ri->num_headers = i + 1;
1594   }
1595}
1596
1597static const char *status_code_to_str(int status_code) {
1598   switch (status_code) {
1599   case 100: return "Continue";
1600   case 101: return "Switching Protocols";
1601   case 102: return "Processing";
1602
1603   case 200: return "OK";
1604   case 201: return "Created";
1605   case 202: return "Accepted";
1606   case 203: return "Non-Authoritative Information";
1607   case 204: return "No Content";
1608   case 205: return "Reset Content";
1609   case 206: return "Partial Content";
1610   case 207: return "Multi-Status";
1611   case 208: return "Already Reported";
1612   case 226: return "IM Used";
1613
1614   case 300: return "Multiple Choices";
1615   case 301: return "Moved Permanently";
1616   case 302: return "Found";
1617   case 303: return "See Other";
1618   case 304: return "Not Modified";
1619   case 305: return "Use Proxy";
1620   case 306: return "Switch Proxy";
1621   case 307: return "Temporary Redirect";
1622   case 308: return "Permanent Redirect";
1623
1624   case 400: return "Bad Request";
1625   case 401: return "Unauthorized";
1626   case 402: return "Payment Required";
1627   case 403: return "Forbidden";
1628   case 404: return "Not Found";
1629   case 405: return "Method Not Allowed";
1630   case 406: return "Not Acceptable";
1631   case 407: return "Proxy Authentication Required";
1632   case 408: return "Request Timeout";
1633   case 409: return "Conflict";
1634   case 410: return "Gone";
1635   case 411: return "Length Required";
1636   case 412: return "Precondition Failed";
1637   case 413: return "Payload Too Large";
1638   case 414: return "URI Too Long";
1639   case 415: return "Unsupported Media Type";
1640   case 416: return "Requested Range Not Satisfiable";
1641   case 417: return "Expectation Failed";
1642   case 418: return "I\'m a teapot";
1643   case 422: return "Unprocessable Entity";
1644   case 423: return "Locked";
1645   case 424: return "Failed Dependency";
1646   case 426: return "Upgrade Required";
1647   case 428: return "Precondition Required";
1648   case 429: return "Too Many Requests";
1649   case 431: return "Request Header Fields Too Large";
1650   case 451: return "Unavailable For Legal Reasons";
1651
1652   case 500: return "Internal Server Error";
1653   case 501: return "Not Implemented";
1654   case 502: return "Bad Gateway";
1655   case 503: return "Service Unavailable";
1656   case 504: return "Gateway Timeout";
1657   case 505: return "HTTP Version Not Supported";
1658   case 506: return "Variant Also Negotiates";
1659   case 507: return "Insufficient Storage";
1660   case 508: return "Loop Detected";
1661   case 510: return "Not Extended";
1662   case 511: return "Network Authentication Required";
1663
1664   default:  return "Server Error";
1665   }
1666}
1667
1668static int call_user(struct connection *conn, enum mg_event ev) {
1669   return conn != NULL && conn->server != NULL &&
1670   conn->server->event_handler != NULL ?
1671   conn->server->event_handler(&conn->mg_conn, ev) : MG_FALSE;
1672}
1673
1674static void send_http_error(struct connection *conn, int code,
1675                     const char *fmt, ...) {
1676   const char *message = status_code_to_str(code);
1677   const char *rewrites = conn->server->config_options[URL_REWRITES];
1678   char headers[200], body[200];
1679   struct vec a, b;
1680   va_list ap;
1681   int body_len, headers_len, match_code;
1682
1683   conn->mg_conn.status_code = code;
1684
1685   // Invoke error handler if it is set
1686   if (call_user(conn, MG_HTTP_ERROR) == MG_TRUE) {
1687   close_local_endpoint(conn);
1688   return;
1689   }
1690
1691   // Handle error code rewrites
1692   while ((rewrites = next_option(rewrites, &a, &b)) != NULL) {
1693   if ((match_code = atoi(a.ptr)) > 0 && match_code == code) {
1694      struct mg_connection *c = &conn->mg_conn;
1695      c->status_code = 302;
1696      mg_printf(c, "HTTP/1.1 %d Moved\r\n"
1697            "Location: %.*s?code=%d&orig_uri=%s&query_string=%s\r\n\r\n",
1698            c->status_code, b.len, b.ptr, code, c->uri,
1699            c->query_string == NULL ? "" : c->query_string);
1700      close_local_endpoint(conn);
1701      return;
1702   }
1703   }
1704
1705   body_len = mg_snprintf(body, sizeof(body), "%d %s\n", code, message);
1706   if (fmt != NULL) {
1707   va_start(ap, fmt);
1708   body_len += mg_vsnprintf(body + body_len, sizeof(body) - body_len, fmt, ap);
1709   va_end(ap);
1710   }
1711   if ((code >= 300 && code <= 399) || code == 204) {
1712   // 3xx errors do not have body
1713   body_len = 0;
1714   }
1715   headers_len = mg_snprintf(headers, sizeof(headers),
1716                     "HTTP/1.1 %d %s\r\nContent-Length: %d\r\n"
1717                     "Content-Type: text/plain\r\n\r\n",
1718                     code, message, body_len);
1719   ns_send(conn->ns_conn, headers, headers_len);
1720   ns_send(conn->ns_conn, body, body_len);
1721   close_local_endpoint(conn);  // This will write to the log file
1722}
1723
1724static void write_chunk(struct connection *conn, const char *buf, int len) {
1725   char chunk_size[50];
1726   int n = mg_snprintf(chunk_size, sizeof(chunk_size), "%X\r\n", len);
1727   ns_send(conn->ns_conn, chunk_size, n);
1728   ns_send(conn->ns_conn, buf, len);
1729   ns_send(conn->ns_conn, "\r\n", 2);
1730}
1731
1732size_t mg_printf(struct mg_connection *conn, const char *fmt, ...) {
1733   struct connection *c = MG_CONN_2_CONN(conn);
1734   va_list ap;
1735
1736   va_start(ap, fmt);
1737   ns_vprintf(c->ns_conn, fmt, ap);
1738   va_end(ap);
1739
1740   return c->ns_conn->send_iobuf.len;
1741}
1742
1743static void ns_forward(struct ns_connection *from, struct ns_connection *to) {
1744   DBG(("%p -> %p %lu bytes", from, to, (unsigned long)from->recv_iobuf.len));
1745   ns_send(to, from->recv_iobuf.buf, from->recv_iobuf.len);
1746   iobuf_remove(&from->recv_iobuf, from->recv_iobuf.len);
1747}
1748
1749#ifndef MONGOOSE_NO_CGI
1750#ifdef _WIN32
1751struct threadparam {
1752   sock_t s;
1753   HANDLE hPipe;
1754};
1755
1756static int wait_until_ready(sock_t sock, int for_read) {
1757   fd_set set;
1758   FD_ZERO(&set);
1759   FD_SET(sock, &set);
1760   select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0);
1761   return 1;
1762}
1763
1764static void *push_to_stdin(void *arg) {
1765   struct threadparam *tp = (struct threadparam *)arg;
1766   int n, sent, stop = 0;
1767   DWORD k;
1768   char buf[IOBUF_SIZE];
1769
1770   while (!stop && wait_until_ready(tp->s, 1) &&
1771         (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) {
1772   if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue;
1773   for (sent = 0; !stop && sent < n; sent += k) {
1774      if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1;
1775   }
1776   }
1777   DBG(("%s", "FORWARED EVERYTHING TO CGI"));
1778   CloseHandle(tp->hPipe);
1779   free(tp);
1780   _endthread();
1781   return NULL;
1782}
1783
1784static void *pull_from_stdout(void *arg) {
1785   struct threadparam *tp = (struct threadparam *)arg;
1786   int k = 0, stop = 0;
1787   DWORD n, sent;
1788   char buf[IOBUF_SIZE];
1789
1790   while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
1791   for (sent = 0; !stop && sent < n; sent += k) {
1792      if (wait_until_ready(tp->s, 0) &&
1793         (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) stop = 1;
1794   }
1795   }
1796   DBG(("%s", "EOF FROM CGI"));
1797   CloseHandle(tp->hPipe);
1798   shutdown(tp->s, 2);  // Without this, IO thread may get truncated data
1799   closesocket(tp->s);
1800   free(tp);
1801   _endthread();
1802   return NULL;
1803}
1804
1805static void spawn_stdio_thread(sock_t sock, HANDLE hPipe,
1806                        void *(*func)(void *)) {
1807   struct threadparam *tp = (struct threadparam *)malloc(sizeof(*tp));
1808   if (tp != NULL) {
1809   tp->s = sock;
1810   tp->hPipe = hPipe;
1811   mg_start_thread(func, tp);
1812   }
1813}
1814
1815static void abs_path(const char *utf8_path, char *abs_path, size_t len) {
1816   wchar_t buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE];
1817   to_wchar(utf8_path, buf, ARRAY_SIZE(buf));
1818   GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL);
1819   WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0);
1820}
1821
1822static process_id_t start_process(char *interp, const char *cmd,
1823                           const char *env, const char *envp[],
1824                           const char *dir, sock_t sock) {
1825   STARTUPINFOW si;
1826   PROCESS_INFORMATION pi;
1827   HANDLE a[2], b[2], me = GetCurrentProcess();
1828   wchar_t wcmd[MAX_PATH_SIZE], full_dir[MAX_PATH_SIZE];
1829   char buf[MAX_PATH_SIZE], buf4[MAX_PATH_SIZE], buf5[MAX_PATH_SIZE],
1830      cmdline[MAX_PATH_SIZE], *p;
1831   DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
1832   FILE *fp;
1833
1834   memset(&si, 0, sizeof(si));
1835   memset(&pi, 0, sizeof(pi));
1836
1837   si.cb = sizeof(si);
1838   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1839   si.wShowWindow = SW_HIDE;
1840   si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1841
1842   CreatePipe(&a[0], &a[1], NULL, 0);
1843   CreatePipe(&b[0], &b[1], NULL, 0);
1844   DuplicateHandle(me, a[0], me, &si.hStdInput, 0, TRUE, flags);
1845   DuplicateHandle(me, b[1], me, &si.hStdOutput, 0, TRUE, flags);
1846
1847   if (interp == NULL && (fp = fopen(cmd, "r")) != NULL) {
1848   buf[0] = buf[1] = '\0';
1849   fgets(buf, sizeof(buf), fp);
1850   buf[sizeof(buf) - 1] = '\0';
1851   if (buf[0] == '#' && buf[1] == '!') {
1852      interp = buf + 2;
1853      for (p = interp + strlen(interp);
1854         isspace(* (uint8_t *) p) && p > interp; p--) *p = '\0';
1855   }
1856   fclose(fp);
1857   }
1858
1859   if (interp != NULL) {
1860   abs_path(interp, buf4, ARRAY_SIZE(buf4));
1861   interp = buf4;
1862   }
1863   abs_path(dir, buf5, ARRAY_SIZE(buf5));
1864   to_wchar(dir, full_dir, ARRAY_SIZE(full_dir));
1865   mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\"",
1866            interp ? interp : "", interp ? " " : "", cmd);
1867   to_wchar(cmdline, wcmd, ARRAY_SIZE(wcmd));
1868
1869   if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP,
1870                  (void *) env, full_dir, &si, &pi) != 0) {
1871   spawn_stdio_thread(sock, a[1], push_to_stdin);
1872   spawn_stdio_thread(sock, b[0], pull_from_stdout);
1873   } else {
1874   CloseHandle(a[1]);
1875   CloseHandle(b[0]);
1876   closesocket(sock);
1877   }
1878   DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess));
1879
1880   // Not closing a[0] and b[1] because we've used DUPLICATE_CLOSE_SOURCE
1881   CloseHandle(si.hStdOutput);
1882   CloseHandle(si.hStdInput);
1883   //CloseHandle(pi.hThread);
1884   //CloseHandle(pi.hProcess);
1885
1886   return pi.hProcess;
1887}
1888#else
1889static process_id_t start_process(const char *interp, const char *cmd,
1890                           const char *env, const char *envp[],
1891                           const char *dir, sock_t sock) {
1892   char buf[500];
1893   process_id_t pid = fork();
1894   (void) env;
1895
1896   if (pid == 0) {
1897   (void) chdir(dir);
1898   (void) dup2(sock, 0);
1899   (void) dup2(sock, 1);
1900   closesocket(sock);
1901
1902   // After exec, all signal handlers are restored to their default values,
1903   // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
1904   // implementation, SIGCHLD's handler will leave unchanged after exec
1905   // if it was set to be ignored. Restore it to default action.
1906   signal(SIGCHLD, SIG_DFL);
1907
1908   if (interp == NULL) {
1909      execle(cmd, cmd, (char *) 0, envp); // Using (char *) 0 to avoid warning
1910   } else {
1911      execle(interp, interp, cmd, (char *) 0, envp);
1912   }
1913   snprintf(buf, sizeof(buf), "Status: 500\r\n\r\n"
1914            "500 Server Error: %s%s%s: %s", interp == NULL ? "" : interp,
1915            interp == NULL ? "" : " ", cmd, strerror(errno));
1916   send(1, buf, strlen(buf), 0);
1917   exit(EXIT_FAILURE);  // exec call failed
1918   }
1919
1920   return pid;
1921}
1922#endif  // _WIN32
1923
1924// This structure helps to create an environment for the spawned CGI program.
1925// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
1926// last element must be NULL.
1927// However, on Windows there is a requirement that all these VARIABLE=VALUE\0
1928// strings must reside in a contiguous buffer. The end of the buffer is
1929// marked by two '\0' characters.
1930// We satisfy both worlds: we create an envp array (which is vars), all
1931// entries are actually pointers inside buf.
1932struct cgi_env_block {
1933   struct mg_connection *conn;
1934   char buf[CGI_ENVIRONMENT_SIZE];       // Environment buffer
1935   const char *vars[MAX_CGI_ENVIR_VARS]; // char *envp[]
1936   int len;                              // Space taken
1937   int nvars;                            // Number of variables in envp[]
1938};
1939
1940// Append VARIABLE=VALUE\0 string to the buffer, and add a respective
1941// pointer into the vars array.
1942static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
1943   int n, space;
1944   char *added;
1945   va_list ap;
1946
1947   // Calculate how much space is left in the buffer
1948   space = sizeof(block->buf) - block->len - 2;
1949   assert(space >= 0);
1950
1951   // Make a pointer to the free space int the buffer
1952   added = block->buf + block->len;
1953
1954   // Copy VARIABLE=VALUE\0 string into the free space
1955   va_start(ap, fmt);
1956   n = mg_vsnprintf(added, (size_t) space, fmt, ap);
1957   va_end(ap);
1958
1959   // Make sure we do not overflow buffer and the envp array
1960   if (n > 0 && n + 1 < space &&
1961      block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
1962   // Append a pointer to the added string into the envp array
1963   block->vars[block->nvars++] = added;
1964   // Bump up used length counter. Include \0 terminator
1965   block->len += n + 1;
1966   }
1967
1968   return added;
1969}
1970
1971static void addenv2(struct cgi_env_block *blk, const char *name) {
1972   const char *s;
1973   if ((s = getenv(name)) != NULL) addenv(blk, "%s=%s", name, s);
1974}
1975
1976static void prepare_cgi_environment(struct connection *conn,
1977                           const char *prog,
1978                           struct cgi_env_block *blk) {
1979   struct mg_connection *ri = &conn->mg_conn;
1980   const char *s, *slash;
1981   char *p, **opts = conn->server->config_options;
1982   int  i;
1983
1984   blk->len = blk->nvars = 0;
1985   blk->conn = ri;
1986
1987   if ((s = getenv("SERVER_NAME")) != NULL) {
1988   addenv(blk, "SERVER_NAME=%s", s);
1989   } else {
1990   addenv(blk, "SERVER_NAME=%s", ri->local_ip);
1991   }
1992   addenv(blk, "SERVER_ROOT=%s", opts[DOCUMENT_ROOT]);
1993   addenv(blk, "DOCUMENT_ROOT=%s", opts[DOCUMENT_ROOT]);
1994   addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MONGOOSE_VERSION);
1995
1996   // Prepare the environment block
1997   addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
1998   addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
1999   addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
2000
2001   // TODO(lsm): fix this for IPv6 case
2002   //addenv(blk, "SERVER_PORT=%d", ri->remote_port);
2003
2004   addenv(blk, "REQUEST_METHOD=%s", ri->request_method);
2005   addenv(blk, "REMOTE_ADDR=%s", ri->remote_ip);
2006   addenv(blk, "REMOTE_PORT=%d", ri->remote_port);
2007   addenv(blk, "REQUEST_URI=%s%s%s", ri->uri,
2008         ri->query_string == NULL ? "" : "?",
2009         ri->query_string == NULL ? "" : ri->query_string);
2010
2011   // SCRIPT_NAME
2012   if (conn->path_info != NULL) {
2013   addenv(blk, "SCRIPT_NAME=%.*s",
2014         (int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri);
2015   addenv(blk, "PATH_INFO=%s", conn->path_info);
2016   } else {
2017   s = strrchr(prog, '/');
2018   slash = strrchr(ri->uri, '/');
2019   addenv(blk, "SCRIPT_NAME=%.*s%s",
2020         slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri,
2021         s == NULL ? prog : s);
2022   }
2023
2024   addenv(blk, "SCRIPT_FILENAME=%s", prog);
2025   addenv(blk, "PATH_TRANSLATED=%s", prog);
2026   addenv(blk, "HTTPS=%s", conn->ns_conn->ssl != NULL ? "on" : "off");
2027
2028   if ((s = mg_get_header(ri, "Content-Type")) != NULL)
2029   addenv(blk, "CONTENT_TYPE=%s", s);
2030
2031   if (ri->query_string != NULL)
2032   addenv(blk, "QUERY_STRING=%s", ri->query_string);
2033
2034   if ((s = mg_get_header(ri, "Content-Length")) != NULL)
2035   addenv(blk, "CONTENT_LENGTH=%s", s);
2036
2037   addenv2(blk, "PATH");
2038   addenv2(blk, "TMP");
2039   addenv2(blk, "TEMP");
2040   addenv2(blk, "TMPDIR");
2041   addenv2(blk, "PERLLIB");
2042   addenv2(blk, ENV_EXPORT_TO_CGI);
2043
2044#if defined(_WIN32)
2045   addenv2(blk, "COMSPEC");
2046   addenv2(blk, "SYSTEMROOT");
2047   addenv2(blk, "SystemDrive");
2048   addenv2(blk, "ProgramFiles");
2049   addenv2(blk, "ProgramFiles(x86)");
2050   addenv2(blk, "CommonProgramFiles(x86)");
2051#else
2052   addenv2(blk, "LD_LIBRARY_PATH");
2053#endif // _WIN32
2054
2055   // Add all headers as HTTP_* variables
2056   for (i = 0; i < ri->num_headers; i++) {
2057   p = addenv(blk, "HTTP_%s=%s",
2058      ri->http_headers[i].name, ri->http_headers[i].value);
2059
2060   // Convert variable name into uppercase, and change - to _
2061   for (; *p != '=' && *p != '\0'; p++) {
2062      if (*p == '-')
2063      *p = '_';
2064      *p = (char) toupper(* (unsigned char *) p);
2065   }
2066   }
2067
2068   blk->vars[blk->nvars++] = NULL;
2069   blk->buf[blk->len++] = '\0';
2070
2071   assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
2072   assert(blk->len > 0);
2073   assert(blk->len < (int) sizeof(blk->buf));
2074}
2075
2076static const char cgi_status[] = "HTTP/1.1 200 OK\r\n";
2077
2078static void open_cgi_endpoint(struct connection *conn, const char *prog) {
2079   struct cgi_env_block blk;
2080   char dir[MAX_PATH_SIZE];
2081   const char *p;
2082   sock_t fds[2];
2083
2084   prepare_cgi_environment(conn, prog, &blk);
2085   // CGI must be executed in its own directory. 'dir' must point to the
2086   // directory containing executable program, 'p' must point to the
2087   // executable program name relative to 'dir'.
2088   if ((p = strrchr(prog, '/')) == NULL) {
2089   mg_snprintf(dir, sizeof(dir), "%s", ".");
2090   } else {
2091   mg_snprintf(dir, sizeof(dir), "%.*s", (int) (p - prog), prog);
2092   }
2093
2094   // Try to create socketpair in a loop until success. ns_socketpair()
2095   // can be interrupted by a signal and fail.
2096   // TODO(lsm): use sigaction to restart interrupted syscall
2097   do {
2098   ns_socketpair(fds);
2099   } while (fds[0] == INVALID_SOCKET);
2100
2101   if (start_process(conn->server->config_options[CGI_INTERPRETER],
2102               prog, blk.buf, blk.vars, dir, fds[1]) != 0) {
2103   conn->endpoint_type = EP_CGI;
2104   conn->endpoint.nc = ns_add_sock(&conn->server->ns_server, fds[0], conn);
2105   conn->endpoint.nc->flags |= MG_CGI_CONN;
2106   ns_send(conn->ns_conn, cgi_status, sizeof(cgi_status) - 1);
2107   conn->mg_conn.status_code = 200;
2108   conn->ns_conn->flags |= NSF_BUFFER_BUT_DONT_SEND;
2109   // Pass POST data to the CGI process
2110   conn->endpoint.nc->send_iobuf = conn->ns_conn->recv_iobuf;
2111   iobuf_init(&conn->ns_conn->recv_iobuf, 0);
2112   } else {
2113   closesocket(fds[0]);
2114   send_http_error(conn, 500, "start_process(%s) failed", prog);
2115   }
2116
2117#ifndef _WIN32
2118   closesocket(fds[1]);  // On Windows, CGI stdio thread closes that socket
2119#endif
2120}
2121
2122static void on_cgi_data(struct ns_connection *nc) {
2123   struct connection *conn = (struct connection *) nc->connection_data;
2124   const char *status = "500";
2125   struct mg_connection c;
2126
2127   if (!conn) return;
2128
2129   // Copy CGI data from CGI socket to the client send buffer
2130   ns_forward(nc, conn->ns_conn);
2131
2132   // If reply has not been parsed yet, parse it
2133   if (conn->ns_conn->flags & NSF_BUFFER_BUT_DONT_SEND) {
2134   struct iobuf *io = &conn->ns_conn->send_iobuf;
2135   int s_len = sizeof(cgi_status) - 1;
2136   int len = get_request_len(io->buf + s_len, io->len - s_len);
2137   char buf[MAX_REQUEST_SIZE], *s = buf;
2138
2139   if (len == 0) return;
2140
2141   if (len < 0 || len > (int) sizeof(buf)) {
2142      len = io->len;
2143      iobuf_remove(io, io->len);
2144      send_http_error(conn, 500, "CGI program sent malformed headers: [%.*s]",
2145      len, io->buf);
2146   } else {
2147      memset(&c, 0, sizeof(c));
2148      memcpy(buf, io->buf + s_len, len);
2149      buf[len - 1] = '\0';
2150      parse_http_headers(&s, &c);
2151      if (mg_get_header(&c, "Location") != NULL) {
2152      status = "302";
2153      } else if ((status = (char *) mg_get_header(&c, "Status")) == NULL) {
2154      status = "200";
2155      }
2156      memcpy(io->buf + 9, status, 3);
2157      conn->mg_conn.status_code = atoi(status);
2158   }
2159   conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
2160   }
2161}
2162#endif  // !MONGOOSE_NO_CGI
2163
2164static char *mg_strdup(const char *str) {
2165   char *copy = (char *) malloc(strlen(str) + 1);
2166   if (copy != NULL) {
2167   strcpy(copy, str);
2168   }
2169   return copy;
2170}
2171
2172static int isbyte(int n) {
2173   return n >= 0 && n <= 255;
2174}
2175
2176static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
2177   int n, a, b, c, d, slash = 32, len = 0;
2178
2179   if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 ||
2180      sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) &&
2181      isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) &&
2182      slash >= 0 && slash < 33) {
2183   len = n;
2184   *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | d;
2185   *mask = slash ? 0xffffffffU << (32 - slash) : 0;
2186   }
2187
2188   return len;
2189}
2190
2191// Verify given socket address against the ACL.
2192// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
2193static int check_acl(const char *acl, uint32_t remote_ip) {
2194   int allowed, flag;
2195   uint32_t net, mask;
2196   struct vec vec;
2197
2198   // If any ACL is set, deny by default
2199   allowed = acl == NULL ? '+' : '-';
2200
2201   while ((acl = next_option(acl, &vec, NULL)) != NULL) {
2202   flag = vec.ptr[0];
2203   if ((flag != '+' && flag != '-') ||
2204      parse_net(&vec.ptr[1], &net, &mask) == 0) {
2205      return -1;
2206   }
2207
2208   if (net == (remote_ip & mask)) {
2209      allowed = flag;
2210   }
2211   }
2212
2213   return allowed == '+';
2214}
2215
2216// Protect against directory disclosure attack by removing '..',
2217// excessive '/' and '\' characters
2218static void remove_double_dots_and_double_slashes(char *s) {
2219   char *p = s;
2220
2221   while (*s != '\0') {
2222   *p++ = *s++;
2223   if (s[-1] == '/' || s[-1] == '\\') {
2224      // Skip all following slashes, backslashes and double-dots
2225      while (s[0] != '\0') {
2226      if (s[0] == '/' || s[0] == '\\') { s++; }
2227      else if (s[0] == '.' && s[1] == '.') { s += 2; }
2228      else { break; }
2229      }
2230   }
2231   }
2232   *p = '\0';
2233}
2234
2235int mg_url_decode(const char *src, int src_len, char *dst,
2236               int dst_len, int is_form_url_encoded) {
2237   int i, j, a, b;
2238#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
2239
2240   for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
2241   if (src[i] == '%' && i < src_len - 2 &&
2242      isxdigit(* (const unsigned char *) (src + i + 1)) &&
2243      isxdigit(* (const unsigned char *) (src + i + 2))) {
2244      a = tolower(* (const unsigned char *) (src + i + 1));
2245      b = tolower(* (const unsigned char *) (src + i + 2));
2246      dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
2247      i += 2;
2248   } else if (is_form_url_encoded && src[i] == '+') {
2249      dst[j] = ' ';
2250   } else {
2251      dst[j] = src[i];
2252   }
2253   }
2254
2255   dst[j] = '\0'; // Null-terminate the destination
2256
2257   return i >= src_len ? j : -1;
2258}
2259
2260static int is_valid_http_method(const char *s) {
2261   return !strcmp(s, "GET") || !strcmp(s, "POST") || !strcmp(s, "HEAD") ||
2262   !strcmp(s, "CONNECT") || !strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
2263   !strcmp(s, "OPTIONS") || !strcmp(s, "PROPFIND") || !strcmp(s, "MKCOL");
2264}
2265
2266// Parse HTTP request, fill in mg_request structure.
2267// This function modifies the buffer by NUL-terminating
2268// HTTP request components, header names and header values.
2269// Note that len must point to the last \n of HTTP headers.
2270static int parse_http_message(char *buf, int len, struct mg_connection *ri) {
2271   int is_request, n;
2272
2273   // Reset the connection. Make sure that we don't touch fields that are
2274   // set elsewhere: remote_ip, remote_port, server_param
2275   ri->request_method = ri->uri = ri->http_version = ri->query_string = NULL;
2276   ri->num_headers = ri->status_code = ri->is_websocket = ri->content_len = 0;
2277
2278   buf[len - 1] = '\0';
2279
2280   // RFC says that all initial whitespaces should be ingored
2281   while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
2282   buf++;
2283   }
2284   ri->request_method = skip(&buf, " ");
2285   ri->uri = skip(&buf, " ");
2286   ri->http_version = skip(&buf, "\r\n");
2287
2288   // HTTP message could be either HTTP request or HTTP response, e.g.
2289   // "GET / HTTP/1.0 ...." or  "HTTP/1.0 200 OK ..."
2290   is_request = is_valid_http_method(ri->request_method);
2291   if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) ||
2292      (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) {
2293   len = -1;
2294   } else {
2295   if (is_request) {
2296      ri->http_version += 5;
2297   }
2298   parse_http_headers(&buf, ri);
2299
2300   if ((ri->query_string = strchr(ri->uri, '?')) != NULL) {
2301      *(char *) ri->query_string++ = '\0';
2302   }
2303   n = (int) strlen(ri->uri);
2304   mg_url_decode(ri->uri, n, (char *) ri->uri, n + 1, 0);
2305   if (*ri->uri == '/' || *ri->uri == '.') {
2306      remove_double_dots_and_double_slashes((char *) ri->uri);
2307   }
2308   }
2309
2310   return len;
2311}
2312
2313static int lowercase(const char *s) {
2314   return tolower(* (const unsigned char *) s);
2315}
2316
2317static int mg_strcasecmp(const char *s1, const char *s2) {
2318   int diff;
2319
2320   do {
2321   diff = lowercase(s1++) - lowercase(s2++);
2322   } while (diff == 0 && s1[-1] != '\0');
2323
2324   return diff;
2325}
2326
2327static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
2328   int diff = 0;
2329
2330   if (len > 0)
2331   do {
2332      diff = lowercase(s1++) - lowercase(s2++);
2333   } while (diff == 0 && s1[-1] != '\0' && --len > 0);
2334
2335   return diff;
2336}
2337
2338// Return HTTP header value, or NULL if not found.
2339const char *mg_get_header(const struct mg_connection *ri, const char *s) {
2340   int i;
2341
2342   for (i = 0; i < ri->num_headers; i++)
2343   if (!mg_strcasecmp(s, ri->http_headers[i].name))
2344      return ri->http_headers[i].value;
2345
2346   return NULL;
2347}
2348
2349// Perform case-insensitive match of string against pattern
2350int mg_match_prefix(const char *pattern, int pattern_len, const char *str) {
2351   const char *or_str;
2352   int len, res, i = 0, j = 0;
2353
2354   if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) {
2355   res = mg_match_prefix(pattern, or_str - pattern, str);
2356   return res > 0 ? res : mg_match_prefix(or_str + 1,
2357      (pattern + pattern_len) - (or_str + 1), str);
2358   }
2359
2360   for (; i < pattern_len; i++, j++) {
2361   if (pattern[i] == '?' && str[j] != '\0') {
2362      continue;
2363   } else if (pattern[i] == '$') {
2364      return str[j] == '\0' ? j : -1;
2365   } else if (pattern[i] == '*') {
2366      i++;
2367      if (pattern[i] == '*') {
2368      i++;
2369      len = (int) strlen(str + j);
2370      } else {
2371      len = (int) strcspn(str + j, "/");
2372      }
2373      if (i == pattern_len) {
2374      return j + len;
2375      }
2376      do {
2377      res = mg_match_prefix(pattern + i, pattern_len - i, str + j + len);
2378      } while (res == -1 && len-- > 0);
2379      return res == -1 ? -1 : j + res + len;
2380   } else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
2381      return -1;
2382   }
2383   }
2384   return j;
2385}
2386
2387// This function prints HTML pages, and expands "{{something}}" blocks
2388// inside HTML by calling appropriate callback functions.
2389// Note that {{@path/to/file}} construct outputs embedded file's contents,
2390// which provides SSI-like functionality.
2391void mg_template(struct mg_connection *conn, const char *s,
2392               struct mg_expansion *expansions) {
2393   int i, j, pos = 0, inside_marker = 0;
2394
2395   for (i = 0; s[i] != '\0'; i++) {
2396   if (inside_marker == 0 && !memcmp(&s[i], "{{", 2)) {
2397      if (i > pos) {
2398      mg_send_data(conn, &s[pos], i - pos);
2399      }
2400      pos = i;
2401      inside_marker = 1;
2402   }
2403   if (inside_marker == 1 && !memcmp(&s[i], "}}", 2)) {
2404      for (j = 0; expansions[j].keyword != NULL; j++) {
2405      const char *kw = expansions[j].keyword;
2406      if ((int) strlen(kw) == i - (pos + 2) &&
2407         memcmp(kw, &s[pos + 2], i - (pos + 2)) == 0) {
2408         expansions[j].handler(conn);
2409         pos = i + 2;
2410         break;
2411      }
2412      }
2413      inside_marker = 0;
2414   }
2415   }
2416   if (i > pos) {
2417   mg_send_data(conn, &s[pos], i - pos);
2418   }
2419}
2420
2421#ifndef MONGOOSE_NO_FILESYSTEM
2422static int must_hide_file(struct connection *conn, const char *path) {
2423   const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
2424   const char *pattern = conn->server->config_options[HIDE_FILES_PATTERN];
2425   return mg_match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
2426   (pattern != NULL && mg_match_prefix(pattern, strlen(pattern), path) > 0);
2427}
2428
2429// Return 1 if real file has been found, 0 otherwise
2430static int convert_uri_to_file_name(struct connection *conn, char *buf,
2431                           size_t buf_len, file_stat_t *st) {
2432   struct vec a, b;
2433   const char *rewrites = conn->server->config_options[URL_REWRITES];
2434   const char *root = conn->server->config_options[DOCUMENT_ROOT];
2435#ifndef MONGOOSE_NO_CGI
2436   const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
2437   char *p;
2438#endif
2439   const char *uri = conn->mg_conn.uri;
2440   const char *domain = mg_get_header(&conn->mg_conn, "Host");
2441   int match_len, root_len = root == NULL ? 0 : strlen(root);
2442
2443   // Perform virtual hosting rewrites
2444   if (rewrites != NULL && domain != NULL) {
2445   const char *colon = strchr(domain, ':');
2446   int domain_len = colon == NULL ? (int) strlen(domain) : colon - domain;
2447
2448   while ((rewrites = next_option(rewrites, &a, &b)) != NULL) {
2449      if (a.len > 1 && a.ptr[0] == '@' && a.len == domain_len + 1 &&
2450         mg_strncasecmp(a.ptr + 1, domain, domain_len) == 0) {
2451      root = b.ptr;
2452      root_len = b.len;
2453      break;
2454      }
2455   }
2456   }
2457
2458   // No filesystem access
2459   if (root == NULL || root_len == 0) return 0;
2460
2461   // Handle URL rewrites
2462   mg_snprintf(buf, buf_len, "%.*s%s", root_len, root, uri);
2463   rewrites = conn->server->config_options[URL_REWRITES];  // Re-initialize!
2464   while ((rewrites = next_option(rewrites, &a, &b)) != NULL) {
2465   if ((match_len = mg_match_prefix(a.ptr, a.len, uri)) > 0) {
2466      mg_snprintf(buf, buf_len, "%.*s%s", (int) b.len, b.ptr, uri + match_len);
2467      break;
2468   }
2469   }
2470
2471   if (stat(buf, st) == 0) return 1;
2472
2473#ifndef MONGOOSE_NO_CGI
2474   // Support PATH_INFO for CGI scripts.
2475   for (p = buf + strlen(root) + 2; *p != '\0'; p++) {
2476   if (*p == '/') {
2477      *p = '\0';
2478      if (mg_match_prefix(cgi_pat, strlen(cgi_pat), buf) > 0 &&
2479         !stat(buf, st)) {
2480      DBG(("!!!! [%s]", buf));
2481      *p = '/';
2482      conn->path_info = mg_strdup(p);
2483      *p = '\0';
2484      return 1;
2485      }
2486      *p = '/';
2487   }
2488   }
2489#endif
2490
2491   return 0;
2492}
2493#endif  // MONGOOSE_NO_FILESYSTEM
2494
2495static int should_keep_alive(const struct mg_connection *conn) {
2496   struct connection *c = MG_CONN_2_CONN(conn);
2497   const char *method = conn->request_method;
2498   const char *http_version = conn->http_version;
2499   const char *header = mg_get_header(conn, "Connection");
2500   return method != NULL &&
2501   (!strcmp(method, "GET") || c->endpoint_type == EP_USER) &&
2502   ((header != NULL && !mg_strcasecmp(header, "keep-alive")) ||
2503      (header == NULL && http_version && !strcmp(http_version, "1.1")));
2504}
2505
2506size_t mg_write(struct mg_connection *c, const void *buf, int len) {
2507   struct connection *conn = MG_CONN_2_CONN(c);
2508   ns_send(conn->ns_conn, buf, len);
2509   return conn->ns_conn->send_iobuf.len;
2510}
2511
2512void mg_send_status(struct mg_connection *c, int status) {
2513   if (c->status_code == 0) {
2514   c->status_code = status;
2515   mg_printf(c, "HTTP/1.1 %d %s\r\n", status, status_code_to_str(status));
2516   }
2517}
2518
2519void mg_send_header(struct mg_connection *c, const char *name, const char *v) {
2520   if (c->status_code == 0) {
2521   c->status_code = 200;
2522   mg_printf(c, "HTTP/1.1 %d %s\r\n", 200, status_code_to_str(200));
2523   }
2524   mg_printf(c, "%s: %s\r\n", name, v);
2525}
2526
2527static void terminate_headers(struct mg_connection *c) {
2528   struct connection *conn = MG_CONN_2_CONN(c);
2529   if (!(conn->ns_conn->flags & MG_HEADERS_SENT)) {
2530   mg_send_header(c, "Transfer-Encoding", "chunked");
2531   mg_write(c, "\r\n", 2);
2532   conn->ns_conn->flags |= MG_HEADERS_SENT;
2533   }
2534}
2535
2536size_t mg_send_data(struct mg_connection *c, const void *data, int data_len) {
2537   struct connection *conn = MG_CONN_2_CONN(c);
2538   terminate_headers(c);
2539   write_chunk(MG_CONN_2_CONN(c), (const char *) data, data_len);
2540   return conn->ns_conn->send_iobuf.len;
2541}
2542
2543size_t mg_printf_data(struct mg_connection *c, const char *fmt, ...) {
2544   struct connection *conn = MG_CONN_2_CONN(c);
2545   va_list ap;
2546   int len;
2547   char mem[IOBUF_SIZE], *buf = mem;
2548
2549   terminate_headers(c);
2550
2551   va_start(ap, fmt);
2552   len = ns_avprintf(&buf, sizeof(mem), fmt, ap);
2553   va_end(ap);
2554
2555   if (len >= 0) {
2556   write_chunk((struct connection *) conn, buf, len);
2557   }
2558   if (buf != mem && buf != NULL) {
2559   free(buf);
2560   }
2561   return conn->ns_conn->send_iobuf.len;
2562}
2563
2564#if !defined(MONGOOSE_NO_WEBSOCKET) || !defined(MONGOOSE_NO_AUTH)
2565static int is_big_endian(void) {
2566   static const int n = 1;
2567   return ((char *) &n)[0] == 0;
2568}
2569#endif
2570
2571#ifndef MONGOOSE_NO_WEBSOCKET
2572// START OF SHA-1 code
2573// Copyright(c) By Steve Reid <steve@edmweb.com>
2574#define SHA1HANDSOFF
2575#if defined(__sun)
2576#include "solarisfixes.h"
2577#endif
2578
2579union char64long16 { unsigned char c[64]; uint32_t l[16]; };
2580
2581#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
2582
2583static uint32_t blk0(union char64long16 *block, int i) {
2584   // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN
2585   if (!is_big_endian()) {
2586   block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
2587      (rol(block->l[i], 8) & 0x00FF00FF);
2588   }
2589   return block->l[i];
2590}
2591
2592#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
2593   ^block->l[(i+2)&15]^block->l[i&15],1))
2594#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
2595#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
2596#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
2597#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
2598#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
2599
2600typedef struct {
2601   uint32_t state[5];
2602   uint32_t count[2];
2603   unsigned char buffer[64];
2604} SHA1_CTX;
2605
2606static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) {
2607   uint32_t a, b, c, d, e;
2608   union char64long16 block[1];
2609
2610   memcpy(block, buffer, 64);
2611   a = state[0];
2612   b = state[1];
2613   c = state[2];
2614   d = state[3];
2615   e = state[4];
2616   R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
2617   R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
2618   R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
2619   R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
2620   R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
2621   R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
2622   R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
2623   R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
2624   R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
2625   R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
2626   R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
2627   R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
2628   R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
2629   R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
2630   R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
2631   R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
2632   R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
2633   R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
2634   R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
2635   R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
2636   state[0] += a;
2637   state[1] += b;
2638   state[2] += c;
2639   state[3] += d;
2640   state[4] += e;
2641   // Erase working structures. The order of operations is important,
2642   // used to ensure that compiler doesn't optimize those out.
2643   memset(block, 0, sizeof(block));
2644   a = b = c = d = e = 0;
2645   (void) a; (void) b; (void) c; (void) d; (void) e;
2646}
2647
2648static void SHA1Init(SHA1_CTX *context) {
2649   context->state[0] = 0x67452301;
2650   context->state[1] = 0xEFCDAB89;
2651   context->state[2] = 0x98BADCFE;
2652   context->state[3] = 0x10325476;
2653   context->state[4] = 0xC3D2E1F0;
2654   context->count[0] = context->count[1] = 0;
2655}
2656
2657static void SHA1Update(SHA1_CTX *context, const unsigned char *data,
2658                  uint32_t len) {
2659   uint32_t i, j;
2660
2661   j = context->count[0];
2662   if ((context->count[0] += len << 3) < j)
2663   context->count[1]++;
2664   context->count[1] += (len>>29);
2665   j = (j >> 3) & 63;
2666   if ((j + len) > 63) {
2667   memcpy(&context->buffer[j], data, (i = 64-j));
2668   SHA1Transform(context->state, context->buffer);
2669   for ( ; i + 63 < len; i += 64) {
2670      SHA1Transform(context->state, &data[i]);
2671   }
2672   j = 0;
2673   }
2674   else i = 0;
2675   memcpy(&context->buffer[j], &data[i], len - i);
2676}
2677
2678static void SHA1Final(unsigned char digest[20], SHA1_CTX *context) {
2679   unsigned i;
2680   unsigned char finalcount[8], c;
2681
2682   for (i = 0; i < 8; i++) {
2683   finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
2684                              >> ((3-(i & 3)) * 8) ) & 255);
2685   }
2686   c = 0200;
2687   SHA1Update(context, &c, 1);
2688   while ((context->count[0] & 504) != 448) {
2689   c = 0000;
2690   SHA1Update(context, &c, 1);
2691   }
2692   SHA1Update(context, finalcount, 8);
2693   for (i = 0; i < 20; i++) {
2694   digest[i] = (unsigned char)
2695      ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
2696   }
2697   memset(context, '\0', sizeof(*context));
2698   memset(&finalcount, '\0', sizeof(finalcount));
2699}
2700// END OF SHA1 CODE
2701
2702static void base64_encode(const unsigned char *src, int src_len, char *dst) {
2703   static const char *b64 =
2704   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2705   int i, j, a, b, c;
2706
2707   for (i = j = 0; i < src_len; i += 3) {
2708   a = src[i];
2709   b = i + 1 >= src_len ? 0 : src[i + 1];
2710   c = i + 2 >= src_len ? 0 : src[i + 2];
2711
2712   dst[j++] = b64[a >> 2];
2713   dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
2714   if (i + 1 < src_len) {
2715      dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
2716   }
2717   if (i + 2 < src_len) {
2718      dst[j++] = b64[c & 63];
2719   }
2720   }
2721   while (j % 4 != 0) {
2722   dst[j++] = '=';
2723   }
2724   dst[j++] = '\0';
2725}
2726
2727static void send_websocket_handshake(struct mg_connection *conn,
2728                              const char *key) {
2729   static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
2730   char buf[500], sha[20], b64_sha[sizeof(sha) * 2];
2731   SHA1_CTX sha_ctx;
2732
2733   mg_snprintf(buf, sizeof(buf), "%s%s", key, magic);
2734   SHA1Init(&sha_ctx);
2735   SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf));
2736   SHA1Final((unsigned char *) sha, &sha_ctx);
2737   base64_encode((unsigned char *) sha, sizeof(sha), b64_sha);
2738   mg_snprintf(buf, sizeof(buf), "%s%s%s",
2739            "HTTP/1.1 101 Switching Protocols\r\n"
2740            "Upgrade: websocket\r\n"
2741            "Connection: Upgrade\r\n"
2742            "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
2743
2744   mg_write(conn, buf, strlen(buf));
2745}
2746
2747static int deliver_websocket_frame(struct connection *conn) {
2748   // Having buf unsigned char * is important, as it is used below in arithmetic
2749   unsigned char *buf = (unsigned char *) conn->ns_conn->recv_iobuf.buf;
2750   int i, len, buf_len = conn->ns_conn->recv_iobuf.len, frame_len = 0,
2751      mask_len = 0, header_len = 0, data_len = 0, buffered = 0;
2752
2753   if (buf_len >= 2) {
2754   len = buf[1] & 127;
2755   mask_len = buf[1] & 128 ? 4 : 0;
2756   if (len < 126 && buf_len >= mask_len) {
2757      data_len = len;
2758      header_len = 2 + mask_len;
2759   } else if (len == 126 && buf_len >= 4 + mask_len) {
2760      header_len = 4 + mask_len;
2761      data_len = ((((int) buf[2]) << 8) + buf[3]);
2762   } else if (buf_len >= 10 + mask_len) {
2763      header_len = 10 + mask_len;
2764      data_len = (int) (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) +
2765      htonl(* (uint32_t *) &buf[6]);
2766   }
2767   }
2768
2769   frame_len = header_len + data_len;
2770   buffered = frame_len > 0 && frame_len <= buf_len;
2771
2772   if (buffered) {
2773   conn->mg_conn.content_len = data_len;
2774   conn->mg_conn.content = (char *) buf + header_len;
2775   conn->mg_conn.wsbits = buf[0];
2776
2777   // Apply mask if necessary
2778   if (mask_len > 0) {
2779      for (i = 0; i < data_len; i++) {
2780      buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4];
2781      }
2782   }
2783
2784   // Call the handler and remove frame from the iobuf
2785   if (call_user(conn, MG_REQUEST) == MG_FALSE) {
2786      conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
2787   }
2788   iobuf_remove(&conn->ns_conn->recv_iobuf, frame_len);
2789   }
2790
2791   return buffered;
2792}
2793
2794size_t mg_websocket_write(struct mg_connection *conn, int opcode,
2795                  const char *data, size_t data_len) {
2796   unsigned char mem[4192], *copy = mem;
2797   size_t copy_len = 0;
2798
2799   if (data_len + 10 > sizeof(mem) &&
2800      (copy = (unsigned char *) malloc(data_len + 10)) == NULL) {
2801      return 0;
2802   }
2803
2804   copy[0] = 0x80 + (opcode & 0x0f);
2805
2806   // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2
2807   if (data_len < 126) {
2808      // Inline 7-bit length field
2809      copy[1] = data_len;
2810      memcpy(copy + 2, data, data_len);
2811      copy_len = 2 + data_len;
2812   } else if (data_len <= 0xFFFF) {
2813      // 16-bit length field
2814      copy[1] = 126;
2815      * (uint16_t *) (copy + 2) = (uint16_t) htons((uint16_t) data_len);
2816      memcpy(copy + 4, data, data_len);
2817      copy_len = 4 + data_len;
2818   } else {
2819      // 64-bit length field
2820      copy[1] = 127;
2821      * (uint32_t *) (copy + 2) = (uint32_t)
2822      htonl((uint32_t) ((uint64_t) data_len >> 32));
2823      * (uint32_t *) (copy + 6) = (uint32_t) htonl(data_len & 0xffffffff);
2824      memcpy(copy + 10, data, data_len);
2825      copy_len = 10 + data_len;
2826   }
2827
2828   if (copy_len > 0) {
2829      mg_write(conn, copy, copy_len);
2830   }
2831   if (copy != mem) {
2832      free(copy);
2833   }
2834
2835   // If we send closing frame, schedule a connection to be closed after
2836   // data is drained to the client.
2837   if (opcode == WEBSOCKET_OPCODE_CONNECTION_CLOSE) {
2838      MG_CONN_2_CONN(conn)->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
2839   }
2840
2841   return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len;
2842}
2843
2844size_t mg_websocket_printf(struct mg_connection *conn, int opcode,
2845                     const char *fmt, ...) {
2846   char mem[4192], *buf = mem;
2847   va_list ap;
2848   int len;
2849
2850   va_start(ap, fmt);
2851   if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
2852   mg_websocket_write(conn, opcode, buf, len);
2853   }
2854   va_end(ap);
2855
2856   if (buf != mem && buf != NULL) {
2857   free(buf);
2858   }
2859
2860   return MG_CONN_2_CONN(conn)->ns_conn->send_iobuf.len;
2861}
2862
2863static void send_websocket_handshake_if_requested(struct mg_connection *conn) {
2864   const char *ver = mg_get_header(conn, "Sec-WebSocket-Version"),
2865      *key = mg_get_header(conn, "Sec-WebSocket-Key");
2866   if (ver != NULL && key != NULL) {
2867   conn->is_websocket = 1;
2868   if (call_user(MG_CONN_2_CONN(conn), MG_WS_HANDSHAKE) == MG_FALSE) {
2869      send_websocket_handshake(conn, key);
2870   }
2871   call_user(MG_CONN_2_CONN(conn), MG_WS_CONNECT);
2872   }
2873}
2874
2875static void ping_idle_websocket_connection(struct connection *conn, time_t t) {
2876   if (t - conn->ns_conn->last_io_time > MONGOOSE_USE_WEBSOCKET_PING_INTERVAL) {
2877   mg_websocket_write(&conn->mg_conn, WEBSOCKET_OPCODE_PING, "", 0);
2878   }
2879}
2880#else
2881#define ping_idle_websocket_connection(conn, t)
2882#endif // !MONGOOSE_NO_WEBSOCKET
2883
2884static void write_terminating_chunk(struct connection *conn) {
2885   mg_write(&conn->mg_conn, "0\r\n\r\n", 5);
2886}
2887
2888static int call_request_handler(struct connection *conn) {
2889   int result;
2890   conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf;
2891   if ((result = call_user(conn, MG_REQUEST)) == MG_TRUE) {
2892   if (conn->ns_conn->flags & MG_HEADERS_SENT) {
2893      write_terminating_chunk(conn);
2894   }
2895   close_local_endpoint(conn);
2896   }
2897   return result;
2898}
2899
2900const char *mg_get_mime_type(const char *path, const char *default_mime_type) {
2901   const char *ext;
2902   size_t i, path_len;
2903
2904   path_len = strlen(path);
2905
2906   for (i = 0; static_builtin_mime_types[i].extension != NULL; i++) {
2907   ext = path + (path_len - static_builtin_mime_types[i].ext_len);
2908   if (path_len > static_builtin_mime_types[i].ext_len &&
2909      mg_strcasecmp(ext, static_builtin_mime_types[i].extension) == 0) {
2910      return static_builtin_mime_types[i].mime_type;
2911   }
2912   }
2913
2914   return default_mime_type;
2915}
2916
2917#ifndef MONGOOSE_NO_FILESYSTEM
2918// Convert month to the month number. Return -1 on error, or month number
2919static int get_month_index(const char *s) {
2920   static const char *month_names[] = {
2921   "Jan", "Feb", "Mar", "Apr", "May", "Jun",
2922   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2923   };
2924   int i;
2925
2926   for (i = 0; i < (int) ARRAY_SIZE(month_names); i++)
2927   if (!strcmp(s, month_names[i]))
2928      return i;
2929
2930   return -1;
2931}
2932
2933static int num_leap_years(int year) {
2934   return year / 4 - year / 100 + year / 400;
2935}
2936
2937// Parse UTC date-time string, and return the corresponding time_t value.
2938static time_t parse_date_string(const char *datetime) {
2939   static const unsigned short days_before_month[] = {
2940   0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
2941   };
2942   char month_str[32];
2943   int second, minute, hour, day, month, year, leap_days, days;
2944   time_t result = (time_t) 0;
2945
2946   if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
2947            &day, month_str, &year, &hour, &minute, &second) == 6) ||
2948      (sscanf(datetime, "%d %3s %d %d:%d:%d",
2949            &day, month_str, &year, &hour, &minute, &second) == 6) ||
2950      (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
2951            &day, month_str, &year, &hour, &minute, &second) == 6) ||
2952      (sscanf(datetime, "%d-%3s-%d %d:%d:%d",
2953            &day, month_str, &year, &hour, &minute, &second) == 6)) &&
2954      year > 1970 &&
2955      (month = get_month_index(month_str)) != -1) {
2956   leap_days = num_leap_years(year) - num_leap_years(1970);
2957   year -= 1970;
2958   days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
2959   result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
2960   }
2961
2962   return result;
2963}
2964
2965// Look at the "path" extension and figure what mime type it has.
2966// Store mime type in the vector.
2967static void get_mime_type(const struct mg_server *server, const char *path,
2968                     struct vec *vec) {
2969   struct vec ext_vec, mime_vec;
2970   const char *list, *ext;
2971   size_t path_len;
2972
2973   path_len = strlen(path);
2974
2975   // Scan user-defined mime types first, in case user wants to
2976   // override default mime types.
2977   list = server->config_options[EXTRA_MIME_TYPES];
2978   while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
2979   // ext now points to the path suffix
2980   ext = path + path_len - ext_vec.len;
2981   if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
2982      *vec = mime_vec;
2983      return;
2984   }
2985   }
2986
2987   vec->ptr = mg_get_mime_type(path, "text/plain");
2988   vec->len = strlen(vec->ptr);
2989}
2990
2991static const char *suggest_connection_header(const struct mg_connection *conn) {
2992   return should_keep_alive(conn) ? "keep-alive" : "close";
2993}
2994
2995static void construct_etag(char *buf, size_t buf_len, const file_stat_t *st) {
2996   mg_snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
2997            (unsigned long) st->st_mtime, (int64_t) st->st_size);
2998}
2999
3000// Return True if we should reply 304 Not Modified.
3001static int is_not_modified(const struct connection *conn,
3002                     const file_stat_t *stp) {
3003   char etag[64];
3004   const char *ims = mg_get_header(&conn->mg_conn, "If-Modified-Since");
3005   const char *inm = mg_get_header(&conn->mg_conn, "If-None-Match");
3006   construct_etag(etag, sizeof(etag), stp);
3007   return (inm != NULL && !mg_strcasecmp(etag, inm)) ||
3008   (ims != NULL && stp->st_mtime <= parse_date_string(ims));
3009}
3010
3011// For given directory path, substitute it to valid index file.
3012// Return 0 if index file has been found, -1 if not found.
3013// If the file is found, it's stats is returned in stp.
3014static int find_index_file(struct connection *conn, char *path,
3015                     size_t path_len, file_stat_t *stp) {
3016   const char *list = conn->server->config_options[INDEX_FILES];
3017   file_stat_t st;
3018   struct vec filename_vec;
3019   size_t n = strlen(path), found = 0;
3020
3021   // The 'path' given to us points to the directory. Remove all trailing
3022   // directory separator characters from the end of the path, and
3023   // then append single directory separator character.
3024   while (n > 0 && path[n - 1] == '/') {
3025   n--;
3026   }
3027   path[n] = '/';
3028
3029   // Traverse index files list. For each entry, append it to the given
3030   // path and see if the file exists. If it exists, break the loop
3031   while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
3032   // Ignore too long entries that may overflow path buffer
3033   if (filename_vec.len > (int) (path_len - (n + 2)))
3034      continue;
3035
3036   // Prepare full path to the index file
3037   strncpy(path + n + 1, filename_vec.ptr, filename_vec.len);
3038   path[n + 1 + filename_vec.len] = '\0';
3039
3040   //DBG(("[%s]", path));
3041
3042   // Does it exist?
3043   if (!stat(path, &st)) {
3044      // Yes it does, break the loop
3045      *stp = st;
3046      found = 1;
3047      break;
3048   }
3049   }
3050
3051   // If no index file exists, restore directory path
3052   if (!found) {
3053   path[n] = '\0';
3054   }
3055
3056   return found;
3057}
3058
3059static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
3060   return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
3061}
3062
3063static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
3064   strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
3065}
3066
3067static void open_file_endpoint(struct connection *conn, const char *path,
3068                        file_stat_t *st) {
3069   char date[64], lm[64], etag[64], range[64], headers[500];
3070   const char *msg = "OK", *hdr;
3071   time_t curtime = time(NULL);
3072   int64_t r1, r2;
3073   struct vec mime_vec;
3074   int n;
3075
3076   conn->endpoint_type = EP_FILE;
3077   ns_set_close_on_exec(conn->endpoint.fd);
3078   conn->mg_conn.status_code = 200;
3079
3080   get_mime_type(conn->server, path, &mime_vec);
3081   conn->cl = st->st_size;
3082   range[0] = '\0';
3083
3084   // If Range: header specified, act accordingly
3085   r1 = r2 = 0;
3086   hdr = mg_get_header(&conn->mg_conn, "Range");
3087   if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
3088      r1 >= 0 && r2 >= 0) {
3089   conn->mg_conn.status_code = 206;
3090   conn->cl = n == 2 ? (r2 > conn->cl ? conn->cl : r2) - r1 + 1: conn->cl - r1;
3091   mg_snprintf(range, sizeof(range), "Content-Range: bytes "
3092            "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n",
3093            r1, r1 + conn->cl - 1, (int64_t) st->st_size);
3094   msg = "Partial Content";
3095   lseek(conn->endpoint.fd, r1, SEEK_SET);
3096   }
3097
3098   // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
3099   // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
3100   gmt_time_string(date, sizeof(date), &curtime);
3101   gmt_time_string(lm, sizeof(lm), &st->st_mtime);
3102   construct_etag(etag, sizeof(etag), st);
3103
3104   n = mg_snprintf(headers, sizeof(headers),
3105               "HTTP/1.1 %d %s\r\n"
3106               "Date: %s\r\n"
3107               "Last-Modified: %s\r\n"
3108               "Etag: %s\r\n"
3109               "Content-Type: %.*s\r\n"
3110               "Content-Length: %" INT64_FMT "\r\n"
3111               "Connection: %s\r\n"
3112               "Accept-Ranges: bytes\r\n"
3113               "%s%s\r\n",
3114               conn->mg_conn.status_code, msg, date, lm, etag,
3115               (int) mime_vec.len, mime_vec.ptr, conn->cl,
3116               suggest_connection_header(&conn->mg_conn),
3117               range, MONGOOSE_USE_EXTRA_HTTP_HEADERS);
3118   ns_send(conn->ns_conn, headers, n);
3119
3120   if (!strcmp(conn->mg_conn.request_method, "HEAD")) {
3121   conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
3122   close(conn->endpoint.fd);
3123   conn->endpoint_type = EP_NONE;
3124   }
3125}
3126
3127void mg_send_file_data(struct mg_connection *c, int fd) {
3128   struct connection *conn = MG_CONN_2_CONN(c);
3129   conn->endpoint_type = EP_FILE;
3130   conn->endpoint.fd = fd;
3131   ns_set_close_on_exec(conn->endpoint.fd);
3132}
3133#endif  // MONGOOSE_NO_FILESYSTEM
3134
3135static void call_request_handler_if_data_is_buffered(struct connection *conn) {
3136#ifndef MONGOOSE_NO_WEBSOCKET
3137   if (conn->mg_conn.is_websocket) {
3138   do { } while (deliver_websocket_frame(conn));
3139   } else
3140#endif
3141   if (conn->num_bytes_recv >= (conn->cl + conn->request_len) &&
3142      call_request_handler(conn) == MG_FALSE) {
3143   open_local_endpoint(conn, 1);
3144   }
3145}
3146
3147#if !defined(MONGOOSE_NO_DIRECTORY_LISTING) || !defined(MONGOOSE_NO_DAV)
3148
3149#ifdef _WIN32
3150struct dirent {
3151   char d_name[MAX_PATH_SIZE];
3152};
3153
3154typedef struct DIR {
3155   HANDLE   handle;
3156   WIN32_FIND_DATAW info;
3157   struct dirent result;
3158} DIR;
3159
3160// Implementation of POSIX opendir/closedir/readdir for Windows.
3161static DIR *opendir(const char *name) {
3162   DIR *dir = NULL;
3163   wchar_t wpath[MAX_PATH_SIZE];
3164   DWORD attrs;
3165
3166   if (name == NULL) {
3167   SetLastError(ERROR_BAD_ARGUMENTS);
3168   } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
3169   SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3170   } else {
3171   to_wchar(name, wpath, ARRAY_SIZE(wpath));
3172   attrs = GetFileAttributesW(wpath);
3173   if (attrs != 0xFFFFFFFF &&
3174      ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
3175      (void) wcscat(wpath, L"\\*");
3176      dir->handle = FindFirstFileW(wpath, &dir->info);
3177      dir->result.d_name[0] = '\0';
3178   } else {
3179      free(dir);
3180      dir = NULL;
3181   }
3182   }
3183
3184   return dir;
3185}
3186
3187static int closedir(DIR *dir) {
3188   int result = 0;
3189
3190   if (dir != NULL) {
3191   if (dir->handle != INVALID_HANDLE_VALUE)
3192      result = FindClose(dir->handle) ? 0 : -1;
3193
3194   free(dir);
3195   } else {
3196   result = -1;
3197   SetLastError(ERROR_BAD_ARGUMENTS);
3198   }
3199
3200   return result;
3201}
3202
3203static struct dirent *readdir(DIR *dir) {
3204   struct dirent *result = 0;
3205
3206   if (dir) {
3207   if (dir->handle != INVALID_HANDLE_VALUE) {
3208      result = &dir->result;
3209      (void) WideCharToMultiByte(CP_UTF8, 0,
3210         dir->info.cFileName, -1, result->d_name,
3211         sizeof(result->d_name), NULL, NULL);
3212
3213      if (!FindNextFileW(dir->handle, &dir->info)) {
3214      (void) FindClose(dir->handle);
3215      dir->handle = INVALID_HANDLE_VALUE;
3216      }
3217
3218   } else {
3219      SetLastError(ERROR_FILE_NOT_FOUND);
3220   }
3221   } else {
3222   SetLastError(ERROR_BAD_ARGUMENTS);
3223   }
3224
3225   return result;
3226}
3227#endif // _WIN32  POSIX opendir/closedir/readdir implementation
3228
3229static int scan_directory(struct connection *conn, const char *dir,
3230                     struct dir_entry **arr) {
3231   char path[MAX_PATH_SIZE];
3232   struct dir_entry *p;
3233   struct dirent *dp;
3234   int arr_size = 0, arr_ind = 0, inc = 100;
3235   DIR *dirp;
3236
3237   *arr = NULL;
3238   if ((dirp = (opendir(dir))) == NULL) return 0;
3239
3240   while ((dp = readdir(dirp)) != NULL) {
3241   // Do not show current dir and hidden files
3242   if (!strcmp(dp->d_name, ".") ||
3243      !strcmp(dp->d_name, "..") ||
3244      must_hide_file(conn, dp->d_name)) {
3245      continue;
3246   }
3247   mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
3248
3249   // Resize the array if nesessary
3250   if (arr_ind >= arr_size) {
3251      if ((p = (struct dir_entry *)
3252         realloc(*arr, (inc + arr_size) * sizeof(**arr))) != NULL) {
3253      // Memset new chunk to zero, otherwize st_mtime will have garbage which
3254      // can make strftime() segfault, see
3255      // http://code.google.com/p/mongoose/issues/detail?id=79
3256      memset(p + arr_size, 0, sizeof(**arr) * inc);
3257
3258      *arr = p;
3259      arr_size += inc;
3260      }
3261   }
3262
3263   if (arr_ind < arr_size) {
3264      (*arr)[arr_ind].conn = conn;
3265      (*arr)[arr_ind].file_name = strdup(dp->d_name);
3266      stat(path, &(*arr)[arr_ind].st);
3267      arr_ind++;
3268   }
3269   }
3270   closedir(dirp);
3271
3272   return arr_ind;
3273}
3274
3275int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len) {
3276   static const char *dont_escape = "._-$,;~()";
3277   static const char *hex = "0123456789abcdef";
3278   size_t i = 0, j = 0;
3279
3280   for (i = j = 0; dst_len > 0 && i < s_len && j + 2 < dst_len - 1; i++, j++) {
3281   if (isalnum(* (const unsigned char *) (src + i)) ||
3282      strchr(dont_escape, * (const unsigned char *) (src + i)) != NULL) {
3283      dst[j] = src[i];
3284   } else if (j + 3 < dst_len) {
3285      dst[j] = '%';
3286      dst[j + 1] = hex[(* (const unsigned char *) (src + i)) >> 4];
3287      dst[j + 2] = hex[(* (const unsigned char *) (src + i)) & 0xf];
3288      j += 2;
3289   }
3290   }
3291
3292   dst[j] = '\0';
3293   return j;
3294}
3295#endif  // !NO_DIRECTORY_LISTING || !MONGOOSE_NO_DAV
3296
3297#ifndef MONGOOSE_NO_DIRECTORY_LISTING
3298
3299static void print_dir_entry(const struct dir_entry *de) {
3300   char size[64], mod[64], href[MAX_PATH_SIZE * 3];
3301   int64_t fsize = de->st.st_size;
3302   int is_dir = S_ISDIR(de->st.st_mode);
3303   const char *slash = is_dir ? "/" : "";
3304
3305   if (is_dir) {
3306   mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]");
3307   } else {
3308      // We use (signed) cast below because MSVC 6 compiler cannot
3309      // convert unsigned __int64 to double.
3310   if (fsize < 1024) {
3311      mg_snprintf(size, sizeof(size), "%d", (int) fsize);
3312   } else if (fsize < 0x100000) {
3313      mg_snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0);
3314   } else if (fsize < 0x40000000) {
3315      mg_snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576);
3316   } else {
3317      mg_snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824);
3318   }
3319   }
3320   strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.st_mtime));
3321   mg_url_encode(de->file_name, strlen(de->file_name), href, sizeof(href));
3322   mg_printf_data(&de->conn->mg_conn,
3323               "<tr><td><a href=\"%s%s\">%s%s</a></td>"
3324               "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
3325               href, slash, de->file_name, slash, mod, size);
3326}
3327
3328// Sort directory entries by size, or name, or modification time.
3329// On windows, __cdecl specification is needed in case if project is built
3330// with __stdcall convention. qsort always requires __cdels callback.
3331static int __cdecl compare_dir_entries(const void *p1, const void *p2) {
3332   const struct dir_entry *a = (const struct dir_entry *) p1,
3333      *b = (const struct dir_entry *) p2;
3334   const char *qs = a->conn->mg_conn.query_string ?
3335   a->conn->mg_conn.query_string : "na";
3336   int cmp_result = 0;
3337
3338   if (S_ISDIR(a->st.st_mode) && !S_ISDIR(b->st.st_mode)) {
3339   return -1;  // Always put directories on top
3340   } else if (!S_ISDIR(a->st.st_mode) && S_ISDIR(b->st.st_mode)) {
3341   return 1;   // Always put directories on top
3342   } else if (*qs == 'n') {
3343   cmp_result = strcmp(a->file_name, b->file_name);
3344   } else if (*qs == 's') {
3345   cmp_result = a->st.st_size == b->st.st_size ? 0 :
3346      a->st.st_size > b->st.st_size ? 1 : -1;
3347   } else if (*qs == 'd') {
3348   cmp_result = a->st.st_mtime == b->st.st_mtime ? 0 :
3349      a->st.st_mtime > b->st.st_mtime ? 1 : -1;
3350   }
3351
3352   return qs[1] == 'd' ? -cmp_result : cmp_result;
3353}
3354
3355static void send_directory_listing(struct connection *conn, const char *dir) {
3356   struct dir_entry *arr = NULL;
3357   int i, num_entries, sort_direction = conn->mg_conn.query_string != NULL &&
3358   conn->mg_conn.query_string[1] == 'd' ? 'a' : 'd';
3359
3360   mg_send_header(&conn->mg_conn, "Transfer-Encoding", "chunked");
3361   mg_send_header(&conn->mg_conn, "Content-Type", "text/html; charset=utf-8");
3362
3363   mg_printf_data(&conn->mg_conn,
3364            "<html><head><title>Index of %s</title>"
3365            "<style>th {text-align: left;}</style></head>"
3366            "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
3367            "<tr><th><a href=\"?n%c\">Name</a></th>"
3368            "<th><a href=\"?d%c\">Modified</a></th>"
3369            "<th><a href=\"?s%c\">Size</a></th></tr>"
3370            "<tr><td colspan=\"3\"><hr></td></tr>",
3371            conn->mg_conn.uri, conn->mg_conn.uri,
3372            sort_direction, sort_direction, sort_direction);
3373
3374   num_entries = scan_directory(conn, dir, &arr);
3375   qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries);
3376   for (i = 0; i < num_entries; i++) {
3377   print_dir_entry(&arr[i]);
3378   free(arr[i].file_name);
3379   }
3380   free(arr);
3381
3382   write_terminating_chunk(conn);
3383   close_local_endpoint(conn);
3384}
3385#endif  // MONGOOSE_NO_DIRECTORY_LISTING
3386
3387#ifndef MONGOOSE_NO_DAV
3388static void print_props(struct connection *conn, const char *uri,
3389                  file_stat_t *stp) {
3390   char mtime[64];
3391
3392   gmt_time_string(mtime, sizeof(mtime), &stp->st_mtime);
3393   mg_printf(&conn->mg_conn,
3394      "<d:response>"
3395      "<d:href>%s</d:href>"
3396      "<d:propstat>"
3397      "<d:prop>"
3398         "<d:resourcetype>%s</d:resourcetype>"
3399         "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
3400         "<d:getlastmodified>%s</d:getlastmodified>"
3401      "</d:prop>"
3402      "<d:status>HTTP/1.1 200 OK</d:status>"
3403      "</d:propstat>"
3404      "</d:response>\n",
3405      uri, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "",
3406      (int64_t) stp->st_size, mtime);
3407}
3408
3409static void handle_propfind(struct connection *conn, const char *path,
3410                     file_stat_t *stp, int exists) {
3411   static const char header[] = "HTTP/1.1 207 Multi-Status\r\n"
3412   "Connection: close\r\n"
3413   "Content-Type: text/xml; charset=utf-8\r\n\r\n"
3414   "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
3415   "<d:multistatus xmlns:d='DAV:'>\n";
3416   static const char footer[] = "</d:multistatus>";
3417   const char *depth = mg_get_header(&conn->mg_conn, "Depth"),
3418      *list_dir = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
3419
3420   conn->mg_conn.status_code = 207;
3421
3422   // Print properties for the requested resource itself
3423   if (!exists) {
3424   conn->mg_conn.status_code = 404;
3425   mg_printf(&conn->mg_conn, "%s", "HTTP/1.1 404 Not Found\r\n\r\n");
3426   } else if (S_ISDIR(stp->st_mode) && mg_strcasecmp(list_dir, "yes") != 0) {
3427   conn->mg_conn.status_code = 403;
3428   mg_printf(&conn->mg_conn, "%s",
3429            "HTTP/1.1 403 Directory Listing Denied\r\n\r\n");
3430   } else {
3431   ns_send(conn->ns_conn, header, sizeof(header) - 1);
3432   print_props(conn, conn->mg_conn.uri, stp);
3433
3434   if (S_ISDIR(stp->st_mode) &&
3435            (depth == NULL || strcmp(depth, "0") != 0)) {
3436      struct dir_entry *arr = NULL;
3437      int i, num_entries = scan_directory(conn, path, &arr);
3438
3439      for (i = 0; i < num_entries; i++) {
3440      char buf[MAX_PATH_SIZE * 3];
3441      struct dir_entry *de = &arr[i];
3442      mg_url_encode(de->file_name, strlen(de->file_name), buf, sizeof(buf));
3443      print_props(conn, buf, &de->st);
3444      free(de->file_name);
3445      }
3446      free(arr);
3447   }
3448   ns_send(conn->ns_conn, footer, sizeof(footer) - 1);
3449   }
3450
3451   close_local_endpoint(conn);
3452}
3453
3454static void handle_mkcol(struct connection *conn, const char *path) {
3455   int status_code = 500;
3456
3457   if (conn->mg_conn.content_len > 0) {
3458   status_code = 415;
3459   } else if (!mkdir(path, 0755)) {
3460   status_code = 201;
3461   } else if (errno == EEXIST) {
3462   status_code = 405;
3463   } else if (errno == EACCES) {
3464   status_code = 403;
3465   } else if (errno == ENOENT) {
3466   status_code = 409;
3467   }
3468   send_http_error(conn, status_code, NULL);
3469}
3470
3471static int remove_directory(const char *dir) {
3472   char path[MAX_PATH_SIZE];
3473   struct dirent *dp;
3474   file_stat_t st;
3475   DIR *dirp;
3476
3477   if ((dirp = opendir(dir)) == NULL) return 0;
3478
3479   while ((dp = readdir(dirp)) != NULL) {
3480   if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
3481   mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
3482   stat(path, &st);
3483   if (S_ISDIR(st.st_mode)) {
3484      remove_directory(path);
3485   } else {
3486      remove(path);
3487   }
3488   }
3489   closedir(dirp);
3490   rmdir(dir);
3491
3492   return 1;
3493}
3494
3495static void handle_delete(struct connection *conn, const char *path) {
3496   file_stat_t st;
3497
3498   if (stat(path, &st) != 0) {
3499   send_http_error(conn, 404, NULL);
3500   } else if (S_ISDIR(st.st_mode)) {
3501   remove_directory(path);
3502   send_http_error(conn, 204, NULL);
3503   } else if (remove(path) == 0) {
3504   send_http_error(conn, 204, NULL);
3505   } else {
3506   send_http_error(conn, 423, NULL);
3507   }
3508}
3509
3510// For a given PUT path, create all intermediate subdirectories
3511// for given path. Return 0 if the path itself is a directory,
3512// or -1 on error, 1 if OK.
3513static int put_dir(const char *path) {
3514   char buf[MAX_PATH_SIZE];
3515   const char *s, *p;
3516   file_stat_t st;
3517
3518   // Create intermediate directories if they do not exist
3519   for (s = p = path + 1; (p = strchr(s, '/')) != NULL; s = ++p) {
3520   if (p - path >= (int) sizeof(buf)) return -1; // Buffer overflow
3521   memcpy(buf, path, p - path);
3522   buf[p - path] = '\0';
3523   if (stat(buf, &st) != 0 && mkdir(buf, 0755) != 0) return -1;
3524   if (p[1] == '\0') return 0;  // Path is a directory itself
3525   }
3526
3527   return 1;
3528}
3529
3530static void handle_put(struct connection *conn, const char *path) {
3531   file_stat_t st;
3532   const char *range, *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
3533   int64_t r1, r2;
3534   int rc;
3535
3536   conn->mg_conn.status_code = !stat(path, &st) ? 200 : 201;
3537   if ((rc = put_dir(path)) == 0) {
3538   mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\n\r\n",
3539            conn->mg_conn.status_code);
3540   close_local_endpoint(conn);
3541   } else if (rc == -1) {
3542   send_http_error(conn, 500, "put_dir: %s", strerror(errno));
3543   } else if (cl_hdr == NULL) {
3544   send_http_error(conn, 411, NULL);
3545#ifdef _WIN32
3546   //On Windows, open() is a macro with 2 params
3547   } else if ((conn->endpoint.fd =
3548            open(path, O_RDWR | O_CREAT | O_TRUNC)) < 0) {
3549#else
3550   } else if ((conn->endpoint.fd =
3551            open(path, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
3552#endif
3553   send_http_error(conn, 500, "open(%s): %s", path, strerror(errno));
3554   } else {
3555   DBG(("PUT [%s] %lu", path, (unsigned long) conn->ns_conn->recv_iobuf.len));
3556   conn->endpoint_type = EP_PUT;
3557   ns_set_close_on_exec(conn->endpoint.fd);
3558   range = mg_get_header(&conn->mg_conn, "Content-Range");
3559   conn->cl = to64(cl_hdr);
3560   r1 = r2 = 0;
3561   if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
3562      conn->mg_conn.status_code = 206;
3563      lseek(conn->endpoint.fd, r1, SEEK_SET);
3564      conn->cl = r2 > r1 ? r2 - r1 + 1: conn->cl - r1;
3565   }
3566   mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n",
3567            conn->mg_conn.status_code);
3568   }
3569}
3570
3571static void forward_put_data(struct connection *conn) {
3572   struct iobuf *io = &conn->ns_conn->recv_iobuf;
3573   size_t k = conn->cl < (int64_t) io->len ? conn->cl : (int64_t) io->len;   // To write
3574   int n = write(conn->endpoint.fd, io->buf, k);   // Write them!
3575   if (n > 0) {
3576   iobuf_remove(io, n);
3577   conn->cl -= n;
3578   }
3579   if (conn->cl <= 0) {
3580   close_local_endpoint(conn);
3581   }
3582}
3583#endif //  MONGOOSE_NO_DAV
3584
3585static void send_options(struct connection *conn) {
3586   conn->mg_conn.status_code = 200;
3587   mg_printf(&conn->mg_conn, "%s",
3588         "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, CONNECT, PUT, "
3589         "DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n");
3590   close_local_endpoint(conn);
3591}
3592
3593#ifndef MONGOOSE_NO_AUTH
3594void mg_send_digest_auth_request(struct mg_connection *c) {
3595   struct connection *conn = MG_CONN_2_CONN(c);
3596   c->status_code = 401;
3597   mg_printf(c,
3598         "HTTP/1.1 401 Unauthorized\r\n"
3599         "WWW-Authenticate: Digest qop=\"auth\", "
3600         "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
3601         conn->server->config_options[AUTH_DOMAIN],
3602         (unsigned long) time(NULL));
3603   close_local_endpoint(conn);
3604}
3605
3606// Use the global passwords file, if specified by auth_gpass option,
3607// or search for .htpasswd in the requested directory.
3608static FILE *open_auth_file(struct connection *conn, const char *path,
3609                     int is_directory) {
3610   char name[MAX_PATH_SIZE];
3611   const char *p, *gpass = conn->server->config_options[GLOBAL_AUTH_FILE];
3612   FILE *fp = NULL;
3613
3614   if (gpass != NULL) {
3615   // Use global passwords file
3616   fp = fopen(gpass, "r");
3617   } else if (is_directory) {
3618   mg_snprintf(name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME);
3619   fp = fopen(name, "r");
3620   } else {
3621   // Try to find .htpasswd in requested directory.
3622   if ((p = strrchr(path, '/')) == NULL) p = path;
3623   mg_snprintf(name, sizeof(name), "%.*s%c%s",
3624            (int) (p - path), path, '/', PASSWORDS_FILE_NAME);
3625   fp = fopen(name, "r");
3626   }
3627
3628   return fp;
3629}
3630
3631#if !defined(HAVE_MD5) && !defined(MONGOOSE_NO_AUTH)
3632typedef struct MD5Context {
3633   uint32_t buf[4];
3634   uint32_t bits[2];
3635   unsigned char in[64];
3636} MD5_CTX;
3637
3638static void byteReverse(unsigned char *buf, unsigned longs) {
3639   uint32_t t;
3640
3641   // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
3642   if (is_big_endian()) {
3643   do {
3644      t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
3645      ((unsigned) buf[1] << 8 | buf[0]);
3646      * (uint32_t *) buf = t;
3647      buf += 4;
3648   } while (--longs);
3649   }
3650}
3651
3652#define F1(x, y, z) (z ^ (x & (y ^ z)))
3653#define F2(x, y, z) F1(z, x, y)
3654#define F3(x, y, z) (x ^ y ^ z)
3655#define F4(x, y, z) (y ^ (x | ~z))
3656
3657#define MD5STEP(f, w, x, y, z, data, s) \
3658   ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
3659
3660// Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
3661// initialization constants.
3662static void MD5Init(MD5_CTX *ctx) {
3663   ctx->buf[0] = 0x67452301;
3664   ctx->buf[1] = 0xefcdab89;
3665   ctx->buf[2] = 0x98badcfe;
3666   ctx->buf[3] = 0x10325476;
3667
3668   ctx->bits[0] = 0;
3669   ctx->bits[1] = 0;
3670}
3671
3672static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
3673   register uint32_t a, b, c, d;
3674
3675   a = buf[0];
3676   b = buf[1];
3677   c = buf[2];
3678   d = buf[3];
3679
3680   MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
3681   MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
3682   MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
3683   MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
3684   MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
3685   MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
3686   MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
3687   MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
3688   MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
3689   MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
3690   MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
3691   MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
3692   MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
3693   MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
3694   MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
3695   MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
3696
3697   MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
3698   MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
3699   MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
3700   MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
3701   MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
3702   MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
3703   MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
3704   MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
3705   MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
3706   MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
3707   MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
3708   MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
3709   MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
3710   MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
3711   MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
3712   MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
3713
3714   MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
3715   MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
3716   MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
3717   MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
3718   MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
3719   MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
3720   MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
3721   MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
3722   MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
3723   MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
3724   MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
3725   MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
3726   MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
3727   MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
3728   MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
3729   MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
3730
3731   MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
3732   MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
3733   MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
3734   MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
3735   MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
3736   MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
3737   MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
3738   MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
3739   MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
3740   MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
3741   MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
3742   MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
3743   MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
3744   MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
3745   MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
3746   MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
3747
3748   buf[0] += a;
3749   buf[1] += b;
3750   buf[2] += c;
3751   buf[3] += d;
3752}
3753
3754static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
3755   uint32_t t;
3756
3757   t = ctx->bits[0];
3758   if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
3759   ctx->bits[1]++;
3760   ctx->bits[1] += len >> 29;
3761
3762   t = (t >> 3) & 0x3f;
3763
3764   if (t) {
3765   unsigned char *p = (unsigned char *) ctx->in + t;
3766
3767   t = 64 - t;
3768   if (len < t) {
3769      memcpy(p, buf, len);
3770      return;
3771   }
3772   memcpy(p, buf, t);
3773   byteReverse(ctx->in, 16);
3774   MD5Transform(ctx->buf, (uint32_t *) ctx->in);
3775   buf += t;
3776   len -= t;
3777   }
3778
3779   while (len >= 64) {
3780   memcpy(ctx->in, buf, 64);
3781   byteReverse(ctx->in, 16);
3782   MD5Transform(ctx->buf, (uint32_t *) ctx->in);
3783   buf += 64;
3784   len -= 64;
3785   }
3786
3787   memcpy(ctx->in, buf, len);
3788}
3789
3790static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
3791   unsigned count;
3792   unsigned char *p;
3793   uint32_t *a;
3794
3795   count = (ctx->bits[0] >> 3) & 0x3F;
3796
3797   p = ctx->in + count;
3798   *p++ = 0x80;
3799   count = 64 - 1 - count;
3800   if (count < 8) {
3801   memset(p, 0, count);
3802   byteReverse(ctx->in, 16);
3803   MD5Transform(ctx->buf, (uint32_t *) ctx->in);
3804   memset(ctx->in, 0, 56);
3805   } else {
3806   memset(p, 0, count - 8);
3807   }
3808   byteReverse(ctx->in, 14);
3809
3810   a = (uint32_t *)ctx->in;
3811   a[14] = ctx->bits[0];
3812   a[15] = ctx->bits[1];
3813
3814   MD5Transform(ctx->buf, (uint32_t *) ctx->in);
3815   byteReverse((unsigned char *) ctx->buf, 4);
3816   memcpy(digest, ctx->buf, 16);
3817   memset((char *) ctx, 0, sizeof(*ctx));
3818}
3819#endif // !HAVE_MD5
3820
3821
3822
3823// Stringify binary data. Output buffer must be twice as big as input,
3824// because each byte takes 2 bytes in string representation
3825static void bin2str(char *to, const unsigned char *p, size_t len) {
3826   static const char *hex = "0123456789abcdef";
3827
3828   for (; len--; p++) {
3829   *to++ = hex[p[0] >> 4];
3830   *to++ = hex[p[0] & 0x0f];
3831   }
3832   *to = '\0';
3833}
3834
3835// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
3836char *mg_md5(char buf[33], ...) {
3837   unsigned char hash[16];
3838   const char *p;
3839   va_list ap;
3840   MD5_CTX ctx;
3841
3842   MD5Init(&ctx);
3843
3844   va_start(ap, buf);
3845   while ((p = va_arg(ap, const char *)) != NULL) {
3846   MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
3847   }
3848   va_end(ap);
3849
3850   MD5Final(hash, &ctx);
3851   bin2str(buf, hash, sizeof(hash));
3852   return buf;
3853}
3854
3855// Check the user's password, return 1 if OK
3856static int check_password(const char *method, const char *ha1, const char *uri,
3857                     const char *nonce, const char *nc, const char *cnonce,
3858                     const char *qop, const char *response) {
3859   char ha2[32 + 1], expected_response[32 + 1];
3860
3861#if 0
3862   // Check for authentication timeout
3863   if ((unsigned long) time(NULL) - (unsigned long) to64(nonce) > 3600 * 2) {
3864   return 0;
3865   }
3866#endif
3867
3868   mg_md5(ha2, method, ":", uri, NULL);
3869   mg_md5(expected_response, ha1, ":", nonce, ":", nc,
3870      ":", cnonce, ":", qop, ":", ha2, NULL);
3871
3872   return mg_strcasecmp(response, expected_response) == 0 ? MG_TRUE : MG_FALSE;
3873}
3874
3875
3876// Authorize against the opened passwords file. Return 1 if authorized.
3877int mg_authorize_digest(struct mg_connection *c, FILE *fp) {
3878   struct connection *conn = MG_CONN_2_CONN(c);
3879   const char *hdr;
3880   char line[256], f_user[256], ha1[256], f_domain[256], user[100], nonce[100],
3881      uri[MAX_REQUEST_SIZE], cnonce[100], resp[100], qop[100], nc[100];
3882
3883   if (c == NULL || fp == NULL) return 0;
3884   if ((hdr = mg_get_header(c, "Authorization")) == NULL ||
3885      mg_strncasecmp(hdr, "Digest ", 7) != 0) return 0;
3886   if (!mg_parse_header(hdr, "username", user, sizeof(user))) return 0;
3887   if (!mg_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce))) return 0;
3888   if (!mg_parse_header(hdr, "response", resp, sizeof(resp))) return 0;
3889   if (!mg_parse_header(hdr, "uri", uri, sizeof(uri))) return 0;
3890   if (!mg_parse_header(hdr, "qop", qop, sizeof(qop))) return 0;
3891   if (!mg_parse_header(hdr, "nc", nc, sizeof(nc))) return 0;
3892   if (!mg_parse_header(hdr, "nonce", nonce, sizeof(nonce))) return 0;
3893
3894   while (fgets(line, sizeof(line), fp) != NULL) {
3895   if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) == 3 &&
3896      !strcmp(user, f_user) &&
3897      // NOTE(lsm): due to a bug in MSIE, we do not compare URIs
3898      !strcmp(conn->server->config_options[AUTH_DOMAIN], f_domain))
3899      return check_password(c->request_method, ha1, uri,
3900                     nonce, nc, cnonce, qop, resp);
3901   }
3902   return MG_FALSE;
3903}
3904
3905
3906// Return 1 if request is authorised, 0 otherwise.
3907static int is_authorized(struct connection *conn, const char *path,
3908                     int is_directory) {
3909   FILE *fp;
3910   int authorized = MG_TRUE;
3911
3912   if ((fp = open_auth_file(conn, path, is_directory)) != NULL) {
3913   authorized = mg_authorize_digest(&conn->mg_conn, fp);
3914   fclose(fp);
3915   }
3916
3917   return authorized;
3918}
3919
3920static int is_authorized_for_dav(struct connection *conn) {
3921   const char *auth_file = conn->server->config_options[DAV_AUTH_FILE];
3922   const char *method = conn->mg_conn.request_method;
3923   FILE *fp;
3924   int authorized = MG_FALSE;
3925
3926   // If dav_auth_file is not set, allow non-authorized PROPFIND
3927   if (method != NULL && !strcmp(method, "PROPFIND") && auth_file == NULL) {
3928   authorized = MG_TRUE;
3929   } else if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
3930   authorized = mg_authorize_digest(&conn->mg_conn, fp);
3931   fclose(fp);
3932   }
3933
3934   return authorized;
3935}
3936
3937static int is_dav_request(const struct connection *conn) {
3938   const char *s = conn->mg_conn.request_method;
3939   return !strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
3940   !strcmp(s, "MKCOL") || !strcmp(s, "PROPFIND");
3941}
3942#endif // MONGOOSE_NO_AUTH
3943
3944static int parse_header(const char *str, int str_len, const char *var_name,
3945                  char *buf, size_t buf_size) {
3946   int ch = ' ', len = 0, n = strlen(var_name);
3947   const char *p, *end = str + str_len, *s = NULL;
3948
3949   if (buf != NULL && buf_size > 0) buf[0] = '\0';
3950
3951   // Find where variable starts
3952   for (s = str; s != NULL && s + n < end; s++) {
3953   if ((s == str || s[-1] == ' ' || s[-1] == ',') && s[n] == '=' &&
3954      !memcmp(s, var_name, n)) break;
3955   }
3956
3957   if (s != NULL && &s[n + 1] < end) {
3958   s += n + 1;
3959   if (*s == '"' || *s == '\'') ch = *s++;
3960   p = s;
3961   while (p < end && p[0] != ch && p[0] != ',' && len < (int) buf_size) {
3962      if (p[0] == '\\' && p[1] == ch) p++;
3963      buf[len++] = *p++;
3964   }
3965   if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
3966      len = 0;
3967   } else {
3968      if (len > 0 && s[len - 1] == ',') len--;
3969      if (len > 0 && s[len - 1] == ';') len--;
3970      buf[len] = '\0';
3971   }
3972   }
3973
3974   return len;
3975}
3976
3977int mg_parse_header(const char *s, const char *var_name, char *buf,
3978               size_t buf_size) {
3979   return parse_header(s, s == NULL ? 0 : strlen(s), var_name, buf, buf_size);
3980}
3981
3982#ifndef MONGOOSE_NO_SSI
3983static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
3984
3985static void send_file_data(struct mg_connection *conn, FILE *fp) {
3986   char buf[IOBUF_SIZE];
3987   int n;
3988   while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
3989   mg_write(conn, buf, n);
3990   }
3991}
3992
3993static void do_ssi_include(struct mg_connection *conn, const char *ssi,
3994                     char *tag, int include_level) {
3995   char file_name[IOBUF_SIZE], path[MAX_PATH_SIZE], *p;
3996   char **opts = (MG_CONN_2_CONN(conn))->server->config_options;
3997   FILE *fp;
3998
3999   // sscanf() is safe here, since send_ssi_file() also uses buffer
4000   // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
4001   if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
4002   // File name is relative to the webserver root
4003   mg_snprintf(path, sizeof(path), "%s%c%s",
4004            opts[DOCUMENT_ROOT], '/', file_name);
4005   } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) {
4006   // File name is relative to the webserver working directory
4007   // or it is absolute system path
4008   mg_snprintf(path, sizeof(path), "%s", file_name);
4009   } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 ||
4010            sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
4011   // File name is relative to the currect document
4012   mg_snprintf(path, sizeof(path), "%s", ssi);
4013   if ((p = strrchr(path, '/')) != NULL) {
4014      p[1] = '\0';
4015   }
4016   mg_snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s",
4017            file_name);
4018   } else {
4019   mg_printf(conn, "Bad SSI #include: [%s]", tag);
4020   return;
4021   }
4022
4023   if ((fp = fopen(path, "rb")) == NULL) {
4024   mg_printf(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
4025            tag, path, strerror(errno));
4026   } else {
4027   ns_set_close_on_exec(fileno(fp));
4028   if (mg_match_prefix(opts[SSI_PATTERN], strlen(opts[SSI_PATTERN]),
4029      path) > 0) {
4030      send_ssi_file(conn, path, fp, include_level + 1);
4031   } else {
4032      send_file_data(conn, fp);
4033   }
4034   fclose(fp);
4035   }
4036}
4037
4038#ifndef MONGOOSE_NO_POPEN
4039static void do_ssi_exec(struct mg_connection *conn, char *tag) {
4040   char cmd[IOBUF_SIZE];
4041   FILE *fp;
4042
4043   if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
4044   mg_printf(conn, "Bad SSI #exec: [%s]", tag);
4045   } else if ((fp = popen(cmd, "r")) == NULL) {
4046   mg_printf(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(errno));
4047   } else {
4048   send_file_data(conn, fp);
4049   pclose(fp);
4050   }
4051}
4052#endif // !MONGOOSE_NO_POPEN
4053
4054static void send_ssi_file(struct mg_connection *conn, const char *path,
4055                     FILE *fp, int include_level) {
4056   char buf[IOBUF_SIZE];
4057   int ch, offset, len, in_ssi_tag;
4058
4059   if (include_level > 10) {
4060   mg_printf(conn, "SSI #include level is too deep (%s)", path);
4061   return;
4062   }
4063
4064   in_ssi_tag = len = offset = 0;
4065   while ((ch = fgetc(fp)) != EOF) {
4066   if (in_ssi_tag && ch == '>') {
4067      in_ssi_tag = 0;
4068      buf[len++] = (char) ch;
4069      buf[len] = '\0';
4070      assert(len <= (int) sizeof(buf));
4071      if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
4072      // Not an SSI tag, pass it
4073      (void) mg_write(conn, buf, (size_t) len);
4074      } else {
4075      if (!memcmp(buf + 5, "include", 7)) {
4076         do_ssi_include(conn, path, buf + 12, include_level);
4077#if !defined(MONGOOSE_NO_POPEN)
4078      } else if (!memcmp(buf + 5, "exec", 4)) {
4079         do_ssi_exec(conn, buf + 9);
4080#endif // !NO_POPEN
4081      } else {
4082         mg_printf(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
4083      }
4084      }
4085      len = 0;
4086   } else if (in_ssi_tag) {
4087      if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
4088      // Not an SSI tag
4089      in_ssi_tag = 0;
4090      } else if (len == (int) sizeof(buf) - 2) {
4091      mg_printf(conn, "%s: SSI tag is too large", path);
4092      len = 0;
4093      }
4094      buf[len++] = ch & 0xff;
4095   } else if (ch == '<') {
4096      in_ssi_tag = 1;
4097      if (len > 0) {
4098      mg_write(conn, buf, (size_t) len);
4099      }
4100      len = 0;
4101      buf[len++] = ch & 0xff;
4102   } else {
4103      buf[len++] = ch & 0xff;
4104      if (len == (int) sizeof(buf)) {
4105      mg_write(conn, buf, (size_t) len);
4106      len = 0;
4107      }
4108   }
4109   }
4110
4111   // Send the rest of buffered data
4112   if (len > 0) {
4113   mg_write(conn, buf, (size_t) len);
4114   }
4115}
4116
4117static void handle_ssi_request(struct connection *conn, const char *path) {
4118   FILE *fp;
4119   struct vec mime_vec;
4120
4121   if ((fp = fopen(path, "rb")) == NULL) {
4122   send_http_error(conn, 500, "fopen(%s): %s", path, strerror(errno));
4123   } else {
4124   ns_set_close_on_exec(fileno(fp));
4125   get_mime_type(conn->server, path, &mime_vec);
4126   conn->mg_conn.status_code = 200;
4127   mg_printf(&conn->mg_conn,
4128            "HTTP/1.1 %d OK\r\n"
4129            "Content-Type: %.*s\r\n"
4130            "Connection: close\r\n\r\n",
4131            conn->mg_conn.status_code, (int) mime_vec.len, mime_vec.ptr);
4132   send_ssi_file(&conn->mg_conn, path, fp, 0);
4133   fclose(fp);
4134   close_local_endpoint(conn);
4135   }
4136}
4137#endif
4138
4139static void proxy_request(struct ns_connection *pc, struct mg_connection *c) {
4140   int i, sent_close_header = 0;
4141
4142   ns_printf(pc, "%s %s HTTP/%s\r\n", c->request_method, c->uri,
4143         c->http_version);
4144   for (i = 0; i < c->num_headers; i++) {
4145   if (mg_strcasecmp(c->http_headers[i].name, "Connection") == 0) {
4146      // Force connection close, cause we don't parse proxy replies
4147      // therefore we don't know message boundaries
4148      ns_printf(pc, "%s: %s\r\n", "Connection", "close");
4149      sent_close_header = 1;
4150   } else {
4151      ns_printf(pc, "%s: %s\r\n", c->http_headers[i].name,
4152            c->http_headers[i].value);
4153   }
4154   }
4155   if (!sent_close_header) {
4156   ns_printf(pc, "%s: %s\r\n", "Connection", "close");
4157   }
4158   ns_printf(pc, "%s", "\r\n");
4159   ns_send(pc, c->content, c->content_len);
4160
4161}
4162
4163#ifdef NS_ENABLE_SSL
4164int mg_terminate_ssl(struct mg_connection *c, const char *cert) {
4165   static const char ok[] = "HTTP/1.0 200 OK\r\n\r\n";
4166   struct connection *conn = MG_CONN_2_CONN(c);
4167   SSL_CTX *ctx;
4168
4169   DBG(("%p MITM", conn));
4170   SSL_library_init();
4171   if ((ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) return 0;
4172
4173   SSL_CTX_use_certificate_file(ctx, cert, 1);
4174   SSL_CTX_use_PrivateKey_file(ctx, cert, 1);
4175   SSL_CTX_use_certificate_chain_file(ctx, cert);
4176
4177   // When clear-text reply is pushed to client, switch to SSL mode.
4178   // TODO(lsm): check for send() failure
4179   send(conn->ns_conn->sock, ok, sizeof(ok) - 1, 0);
4180   DBG(("%p %lu %d SEND", c, (unsigned long)sizeof(ok) - 1, n));
4181   conn->ns_conn->send_iobuf.len = 0;
4182   conn->endpoint_type = EP_USER;  // To keep-alive in close_local_endpoint()
4183   close_local_endpoint(conn);     // Clean up current CONNECT request
4184   if ((conn->ns_conn->ssl = SSL_new(ctx)) != NULL) {
4185   SSL_set_fd(conn->ns_conn->ssl, conn->ns_conn->sock);
4186   }
4187   SSL_CTX_free(ctx);
4188   return 1;
4189}
4190#endif
4191
4192int mg_forward(struct mg_connection *c, const char *host, int port, int ssl) {
4193   struct connection *conn = MG_CONN_2_CONN(c);
4194   struct ns_server *server = &conn->server->ns_server;
4195   struct ns_connection *pc;
4196
4197   if ((pc = ns_connect(server, host, port, ssl, conn)) == NULL) {
4198   conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
4199   return 0;
4200   }
4201
4202   // Interlink two connections
4203   pc->flags |= MG_PROXY_CONN;
4204   conn->endpoint_type = EP_PROXY;
4205   conn->endpoint.nc = pc;
4206   DBG(("%p [%s] [%s:%d] -> %p %p",
4207      conn, c->uri, host, port, pc, conn->ns_conn->ssl));
4208
4209   if (strcmp(c->request_method, "CONNECT") == 0) {
4210   // For CONNECT request, reply with 200 OK. Tunnel is established.
4211   mg_printf(c, "%s", "HTTP/1.1 200 OK\r\n\r\n");
4212   } else {
4213   // Strip "http://host:port" part from the URI
4214   if (memcmp(c->uri, "http://", 7) == 0) c->uri += 7;
4215   while (*c->uri != '\0' && *c->uri != '/') c->uri++;
4216   proxy_request(pc, c);
4217   }
4218   return 1;
4219}
4220
4221static void proxify_connection(struct connection *conn) {
4222   char proto[10], host[500], cert[500];
4223   unsigned short port = 80;
4224   struct mg_connection *c = &conn->mg_conn;
4225   int n = 0;
4226   const char *url = c->uri;
4227
4228   proto[0] = host[0] = cert[0] = '\0';
4229   if (sscanf(url, "%499[^: ]:%hu%n", host, &port, &n) != 2 &&
4230      sscanf(url, "%9[a-z]://%499[^: ]:%hu%n", proto, host, &port, &n) != 3 &&
4231      sscanf(url, "%9[a-z]://%499[^/ ]%n", proto, host, &n) != 2) {
4232   n = 0;
4233   }
4234
4235#ifdef NS_ENABLE_SSL
4236   // Find out whether we should be in the MITM mode
4237   {
4238   const char *certs = conn->server->config_options[SSL_MITM_CERTS];
4239   int host_len = strlen(host);
4240   struct vec a, b;
4241
4242   while (conn->ns_conn->ssl == NULL && port != 80 &&
4243         (certs = next_option(certs, &a, &b)) != NULL) {
4244      if (a.len != host_len || mg_strncasecmp(a.ptr, host, a.len)) continue;
4245      snprintf(cert, sizeof(cert), "%.*s", b.len, b.ptr);
4246      mg_terminate_ssl(&conn->mg_conn, cert);
4247      return;
4248   }
4249   }
4250#endif
4251
4252   if (n > 0 && mg_forward(c, host, port, conn->ns_conn->ssl != NULL)) {
4253   } else {
4254   conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
4255   }
4256}
4257
4258#ifndef MONGOOSE_NO_FILESYSTEM
4259void mg_send_file_internal(struct mg_connection *c, const char *file_name,
4260                     file_stat_t *st, int exists) {
4261   struct connection *conn = MG_CONN_2_CONN(c);
4262   char path[MAX_PATH_SIZE];
4263   const int is_directory = S_ISDIR(st->st_mode);
4264#ifndef MONGOOSE_NO_CGI
4265   const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
4266#else
4267   const char *cgi_pat = DEFAULT_CGI_PATTERN;
4268#endif
4269#ifndef MONGOOSE_NO_DIRECTORY_LISTING
4270   const char *dir_lst = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
4271#else
4272   const char *dir_lst = "yes";
4273#endif
4274
4275   mg_snprintf(path, sizeof(path), "%s", file_name);
4276
4277   if (!exists || must_hide_file(conn, path)) {
4278   send_http_error(conn, 404, NULL);
4279   } else if (is_directory &&
4280            conn->mg_conn.uri[strlen(conn->mg_conn.uri) - 1] != '/') {
4281   conn->mg_conn.status_code = 301;
4282   mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n"
4283            "Location: %s/\r\n\r\n", conn->mg_conn.uri);
4284   close_local_endpoint(conn);
4285   } else if (is_directory && !find_index_file(conn, path, sizeof(path), st)) {
4286   if (!mg_strcasecmp(dir_lst, "yes")) {
4287#ifndef MONGOOSE_NO_DIRECTORY_LISTING
4288      send_directory_listing(conn, path);
4289#else
4290      send_http_error(conn, 501, NULL);
4291#endif
4292   } else {
4293      send_http_error(conn, 403, NULL);
4294   }
4295   } else if (mg_match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
4296#if !defined(MONGOOSE_NO_CGI)
4297   open_cgi_endpoint(conn, path);
4298#else
4299   send_http_error(conn, 501, NULL);
4300#endif // !MONGOOSE_NO_CGI
4301#ifndef MONGOOSE_NO_SSI
4302   } else if (mg_match_prefix(conn->server->config_options[SSI_PATTERN],
4303                        strlen(conn->server->config_options[SSI_PATTERN]),
4304                        path) > 0) {
4305   handle_ssi_request(conn, path);
4306#endif
4307   } else if (is_not_modified(conn, st)) {
4308   send_http_error(conn, 304, NULL);
4309   } else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY)) != -1) {
4310   // O_BINARY is required for Windows, otherwise in default text mode
4311   // two bytes \r\n will be read as one.
4312   open_file_endpoint(conn, path, st);
4313   } else {
4314   send_http_error(conn, 404, NULL);
4315   }
4316}
4317void mg_send_file(struct mg_connection *c, const char *file_name) {
4318   file_stat_t st;
4319   const int exists = stat(file_name, &st) == 0;
4320   mg_send_file_internal(c, file_name, &st, exists);
4321}
4322#endif  // !MONGOOSE_NO_FILESYSTEM
4323
4324static void open_local_endpoint(struct connection *conn, int skip_user) {
4325#ifndef MONGOOSE_NO_FILESYSTEM
4326   char path[MAX_PATH_SIZE];
4327   file_stat_t st;
4328   int exists = 0;
4329#endif
4330
4331   // If EP_USER was set in a prev call, reset it
4332   conn->endpoint_type = EP_NONE;
4333
4334#ifndef MONGOOSE_NO_AUTH
4335   if (conn->server->event_handler && call_user(conn, MG_AUTH) == MG_FALSE) {
4336   mg_send_digest_auth_request(&conn->mg_conn);
4337   return;
4338   }
4339#endif
4340
4341   // Call URI handler if one is registered for this URI
4342   if (skip_user == 0 && conn->server->event_handler != NULL) {
4343   conn->endpoint_type = EP_USER;
4344#if MONGOOSE_POST_SIZE_LIMIT > 1
4345   {
4346      const char *cl = mg_get_header(&conn->mg_conn, "Content-Length");
4347      if ((strcmp(conn->mg_conn.request_method, "POST") == 0 ||
4348         strcmp(conn->mg_conn.request_method, "PUT") == 0) &&
4349         (cl == NULL || to64(cl) > MONGOOSE_POST_SIZE_LIMIT)) {
4350      send_http_error(conn, 500, "POST size > %lu",
4351                  (unsigned long) MONGOOSE_POST_SIZE_LIMIT);
4352      }
4353   }
4354#endif
4355   return;
4356   }
4357
4358   if (strcmp(conn->mg_conn.request_method, "CONNECT") == 0 ||
4359      mg_strncasecmp(conn->mg_conn.uri, "http", 4) == 0) {
4360   const char *enp = conn->server->config_options[ENABLE_PROXY];
4361   if (enp == NULL || strcmp(enp, "yes") != 0) {
4362      send_http_error(conn, 405, NULL);
4363   } else {
4364      proxify_connection(conn);
4365   }
4366   return;
4367   }
4368
4369   if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
4370   send_options(conn);
4371   return;
4372   }
4373
4374#ifdef MONGOOSE_NO_FILESYSTEM
4375   send_http_error(conn, 404, NULL);
4376#else
4377   exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
4378
4379   if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
4380   send_options(conn);
4381   } else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) {
4382   send_http_error(conn, 404, NULL);
4383#ifndef MONGOOSE_NO_AUTH
4384   } else if ((!is_dav_request(conn) && !is_authorized(conn, path,
4385            exists && S_ISDIR(st.st_mode))) ||
4386            (is_dav_request(conn) && !is_authorized_for_dav(conn))) {
4387   mg_send_digest_auth_request(&conn->mg_conn);
4388   close_local_endpoint(conn);
4389#endif
4390#ifndef MONGOOSE_NO_DAV
4391   } else if (must_hide_file(conn, path)) {
4392   send_http_error(conn, 404, NULL);
4393   } else if (!strcmp(conn->mg_conn.request_method, "PROPFIND")) {
4394   handle_propfind(conn, path, &st, exists);
4395   } else if (!strcmp(conn->mg_conn.request_method, "MKCOL")) {
4396   handle_mkcol(conn, path);
4397   } else if (!strcmp(conn->mg_conn.request_method, "DELETE")) {
4398   handle_delete(conn, path);
4399   } else if (!strcmp(conn->mg_conn.request_method, "PUT")) {
4400   handle_put(conn, path);
4401#endif
4402   } else {
4403   mg_send_file_internal(&conn->mg_conn, path, &st, exists);
4404   }
4405#endif  // MONGOOSE_NO_FILESYSTEM
4406}
4407
4408static void send_continue_if_expected(struct connection *conn) {
4409   static const char expect_response[] = "HTTP/1.1 100 Continue\r\n\r\n";
4410   const char *expect_hdr = mg_get_header(&conn->mg_conn, "Expect");
4411
4412   if (expect_hdr != NULL && !mg_strcasecmp(expect_hdr, "100-continue")) {
4413   ns_send(conn->ns_conn, expect_response, sizeof(expect_response) - 1);
4414   }
4415}
4416
4417// Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
4418static int is_valid_uri(const char *uri) {
4419   unsigned short n;
4420   return uri[0] == '/' ||
4421   strcmp(uri, "*") == 0 ||            // OPTIONS method can use asterisk URI
4422   mg_strncasecmp(uri, "http", 4) == 0 || // Naive check for the absolute URI
4423   sscanf(uri, "%*[^ :]:%hu", &n) > 0; // CONNECT method can use host:port
4424}
4425
4426static void try_parse(struct connection *conn) {
4427   struct iobuf *io = &conn->ns_conn->recv_iobuf;
4428
4429   if (conn->request_len == 0 &&
4430      (conn->request_len = get_request_len(io->buf, io->len)) > 0) {
4431   // If request is buffered in, remove it from the iobuf. This is because
4432   // iobuf could be reallocated, and pointers in parsed request could
4433   // become invalid.
4434   conn->request = (char *) malloc(conn->request_len);
4435   memcpy(conn->request, io->buf, conn->request_len);
4436   //DBG(("%p [%.*s]", conn, conn->request_len, conn->request));
4437   iobuf_remove(io, conn->request_len);
4438   conn->request_len = parse_http_message(conn->request, conn->request_len,
4439                                 &conn->mg_conn);
4440   if (conn->request_len > 0) {
4441      const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
4442      conn->cl = cl_hdr == NULL ? 0 : to64(cl_hdr);
4443      conn->mg_conn.content_len = (size_t) conn->cl;
4444   }
4445   }
4446}
4447
4448static void do_proxy(struct connection *conn) {
4449   if (0 && conn->request_len == 0) {
4450   try_parse(conn);
4451   DBG(("%p parsing -> %d", conn, conn->request_len));
4452   if (conn->request_len > 0 && call_user(conn, MG_REQUEST) == MG_FALSE) {
4453      proxy_request(conn->endpoint.nc, &conn->mg_conn);
4454   } else if (conn->request_len < 0) {
4455      ns_forward(conn->ns_conn, conn->endpoint.nc);
4456   }
4457   } else {
4458   DBG(("%p forwarding", conn));
4459   ns_forward(conn->ns_conn, conn->endpoint.nc);
4460   }
4461}
4462
4463static void on_recv_data(struct connection *conn) {
4464   struct iobuf *io = &conn->ns_conn->recv_iobuf;
4465   int n;
4466
4467   if (conn->endpoint_type == EP_PROXY) {
4468   if (conn->endpoint.nc != NULL) do_proxy(conn);
4469   return;
4470   }
4471
4472   try_parse(conn);
4473   DBG(("%p %d %lu %d", conn, conn->request_len, (unsigned long)io->len,
4474      conn->ns_conn->flags));
4475   if (conn->request_len < 0 ||
4476      (conn->request_len > 0 && !is_valid_uri(conn->mg_conn.uri))) {
4477   send_http_error(conn, 400, NULL);
4478   } else if (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE) {
4479   send_http_error(conn, 413, NULL);
4480   } else if (conn->request_len > 0 &&
4481            strcmp(conn->mg_conn.http_version, "1.0") != 0 &&
4482            strcmp(conn->mg_conn.http_version, "1.1") != 0) {
4483   send_http_error(conn, 505, NULL);
4484   } else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) {
4485#ifndef MONGOOSE_NO_WEBSOCKET
4486   send_websocket_handshake_if_requested(&conn->mg_conn);
4487#endif
4488   send_continue_if_expected(conn);
4489   open_local_endpoint(conn, 0);
4490   }
4491
4492#ifndef MONGOOSE_NO_CGI
4493   if (conn->endpoint_type == EP_CGI && conn->endpoint.nc != NULL) {
4494   ns_forward(conn->ns_conn, conn->endpoint.nc);
4495   }
4496#endif
4497   if (conn->endpoint_type == EP_USER) {
4498   conn->mg_conn.content = io->buf;
4499   conn->mg_conn.content_len = io->len;
4500   n = call_user(conn, MG_RECV);
4501   if (n < 0) {
4502      conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
4503   } else if ((size_t) n <= io->len) {
4504      iobuf_remove(io, n);
4505   }
4506   call_request_handler_if_data_is_buffered(conn);
4507   }
4508#ifndef MONGOOSE_NO_DAV
4509   if (conn->endpoint_type == EP_PUT && io->len > 0) {
4510   forward_put_data(conn);
4511   }
4512#endif
4513}
4514
4515static void call_http_client_handler(struct connection *conn) {
4516   //conn->mg_conn.status_code = code;
4517   // For responses without Content-Lengh, use the whole buffer
4518   if (conn->cl == 0) {
4519   conn->mg_conn.content_len = conn->ns_conn->recv_iobuf.len;
4520   }
4521   conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf;
4522   if (call_user(conn, MG_REPLY) == MG_FALSE) {
4523   conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
4524   }
4525   iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len);
4526   conn->mg_conn.status_code = 0;
4527   conn->cl = conn->num_bytes_recv = conn->request_len = 0;
4528   free(conn->request);
4529   conn->request = NULL;
4530}
4531
4532static void process_response(struct connection *conn) {
4533   struct iobuf *io = &conn->ns_conn->recv_iobuf;
4534
4535   try_parse(conn);
4536   DBG(("%p %d %lu", conn, conn->request_len, (unsigned long)io->len));
4537   if (conn->request_len < 0 ||
4538      (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE)) {
4539   call_http_client_handler(conn);
4540   } else if ((int64_t) io->len >= conn->cl) {
4541   call_http_client_handler(conn);
4542   }
4543}
4544
4545struct mg_connection *mg_connect(struct mg_server *server, const char *host,
4546                           int port, int use_ssl) {
4547   struct ns_connection *nsconn;
4548   struct connection *conn;
4549
4550   nsconn = ns_connect(&server->ns_server, host, port, use_ssl, NULL);
4551   if (nsconn == NULL) return 0;
4552
4553   if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
4554   nsconn->flags |= NSF_CLOSE_IMMEDIATELY;
4555   return 0;
4556   }
4557
4558   // Interlink two structs
4559   conn->ns_conn = nsconn;
4560   nsconn->connection_data = conn;
4561
4562   conn->server = server;
4563   conn->endpoint_type = EP_CLIENT;
4564   //conn->handler = handler;
4565   conn->mg_conn.server_param = server->ns_server.server_data;
4566   conn->ns_conn->flags = NSF_CONNECTING;
4567
4568   return &conn->mg_conn;
4569}
4570
4571#ifndef MONGOOSE_NO_LOGGING
4572static void log_header(const struct mg_connection *conn, const char *header,
4573                  FILE *fp) {
4574   const char *header_value;
4575
4576   if ((header_value = mg_get_header(conn, header)) == NULL) {
4577   (void) fprintf(fp, "%s", " -");
4578   } else {
4579   (void) fprintf(fp, " \"%s\"", header_value);
4580   }
4581}
4582
4583static void log_access(const struct connection *conn, const char *path) {
4584   const struct mg_connection *c = &conn->mg_conn;
4585   FILE *fp = (path == NULL) ?  NULL : fopen(path, "a+");
4586   char date[64], user[100];
4587   time_t now;
4588
4589   if (fp == NULL) return;
4590   now = time(NULL);
4591   strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", localtime(&now));
4592
4593   flockfile(fp);
4594   mg_parse_header(mg_get_header(&conn->mg_conn, "Authorization"), "username",
4595               user, sizeof(user));
4596   fprintf(fp, "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d 0",
4597         c->remote_ip, user[0] == '\0' ? "-" : user, date,
4598         c->request_method ? c->request_method : "-",
4599         c->uri ? c->uri : "-", c->query_string ? "?" : "",
4600         c->query_string ? c->query_string : "",
4601         c->http_version, c->status_code);
4602   log_header(c, "Referer", fp);
4603   log_header(c, "User-Agent", fp);
4604   fputc('\n', fp);
4605   fflush(fp);
4606
4607   funlockfile(fp);
4608   fclose(fp);
4609}
4610#endif
4611
4612static void close_local_endpoint(struct connection *conn) {
4613   struct mg_connection *c = &conn->mg_conn;
4614   // Must be done before free()
4615   int keep_alive = should_keep_alive(&conn->mg_conn) &&
4616   (conn->endpoint_type == EP_FILE || conn->endpoint_type == EP_USER);
4617   DBG(("%p %d %d %d", conn, conn->endpoint_type, keep_alive,
4618      conn->ns_conn->flags));
4619
4620   switch (conn->endpoint_type) {
4621   case EP_PUT:
4622   case EP_FILE:
4623      close(conn->endpoint.fd);
4624      break;
4625   case EP_CGI:
4626   case EP_PROXY:
4627      if (conn->endpoint.nc != NULL) {
4628      DBG(("%p %p %p :-)", conn, conn->ns_conn, conn->endpoint.nc));
4629      conn->endpoint.nc->flags |= NSF_CLOSE_IMMEDIATELY;
4630      conn->endpoint.nc->connection_data = NULL;
4631      }
4632      break;
4633   default: break;
4634   }
4635
4636#ifndef MONGOOSE_NO_LOGGING
4637   if (c->status_code > 0 && conn->endpoint_type != EP_CLIENT &&
4638      c->status_code != 400) {
4639   log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
4640   }
4641#endif
4642
4643   // Gobble possible POST data sent to the URI handler
4644   iobuf_free(&conn->ns_conn->recv_iobuf);
4645   free(conn->request);
4646   free(conn->path_info);
4647   conn->endpoint.nc = NULL;
4648   conn->request = conn->path_info = NULL;
4649
4650   conn->endpoint_type = EP_NONE;
4651   conn->cl = conn->num_bytes_recv = conn->request_len = 0;
4652   conn->ns_conn->flags &= ~(NSF_FINISHED_SENDING_DATA |
4653                     NSF_BUFFER_BUT_DONT_SEND | NSF_CLOSE_IMMEDIATELY |
4654                     MG_HEADERS_SENT | MG_LONG_RUNNING);
4655
4656   // Do not memset() the whole structure, as some of the fields
4657   // (IP addresses & ports, server_param) must survive. Nullify the rest.
4658   c->request_method = c->uri = c->http_version = c->query_string = NULL;
4659   c->num_headers = c->status_code = c->is_websocket = c->content_len = 0;
4660   c->connection_param = c->callback_param = NULL;
4661
4662   if (keep_alive) {
4663   on_recv_data(conn);  // Can call us recursively if pipelining is used
4664   } else {
4665   conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len == 0 ?
4666      NSF_CLOSE_IMMEDIATELY : NSF_FINISHED_SENDING_DATA;
4667   }
4668}
4669
4670static void transfer_file_data(struct connection *conn) {
4671   char buf[IOBUF_SIZE];
4672   int n;
4673
4674   // If output buffer is too big, don't send anything. Wait until
4675   // mongoose drains already buffered data to the client.
4676   if (conn->ns_conn->send_iobuf.len > sizeof(buf) * 2) return;
4677
4678   // Do not send anyt
4679   n = read(conn->endpoint.fd, buf, conn->cl < (int64_t) sizeof(buf) ?
4680         (int) conn->cl : (int) sizeof(buf));
4681
4682   if (n <= 0) {
4683   close_local_endpoint(conn);
4684   } else if (n > 0) {
4685   conn->cl -= n;
4686   ns_send(conn->ns_conn, buf, n);
4687   if (conn->cl <= 0) {
4688      close_local_endpoint(conn);
4689   }
4690   }
4691}
4692
4693int mg_poll_server(struct mg_server *server, int milliseconds) {
4694   return ns_server_poll(&server->ns_server, milliseconds);
4695}
4696
4697void mg_destroy_server(struct mg_server **server) {
4698   if (server != NULL && *server != NULL) {
4699   struct mg_server *s = *server;
4700   int i;
4701
4702   ns_server_free(&s->ns_server);
4703   for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) {
4704      free(s->config_options[i]);  // It is OK to free(NULL)
4705   }
4706   free(s);
4707   *server = NULL;
4708   }
4709}
4710
4711struct mg_iterator {
4712   mg_handler_t cb;
4713   void *param;
4714};
4715
4716static void iter(struct ns_connection *nsconn, enum ns_event ev, void *param) {
4717   if (ev == NS_POLL) {
4718   struct mg_iterator *it = (struct mg_iterator *) param;
4719   struct connection *c = (struct connection *) nsconn->connection_data;
4720   if (c != NULL) c->mg_conn.callback_param = it->param;
4721   it->cb(&c->mg_conn, MG_POLL);
4722   }
4723}
4724
4725struct mg_connection *mg_next(struct mg_server *s, struct mg_connection *c) {
4726   struct connection *conn = MG_CONN_2_CONN(c);
4727   struct ns_connection *nc = ns_next(&s->ns_server,
4728                              c == NULL ? NULL : conn->ns_conn);
4729
4730   return nc == NULL ? NULL :
4731   & ((struct connection *) nc->connection_data)->mg_conn;
4732}
4733
4734// Apply function to all active connections.
4735void mg_iterate_over_connections(struct mg_server *server, mg_handler_t cb,
4736   void *param) {
4737   struct mg_iterator it = { cb, param };
4738   ns_iterate(&server->ns_server, iter, &it);
4739}
4740
4741static int get_var(const char *data, size_t data_len, const char *name,
4742               char *dst, size_t dst_len) {
4743   const char *p, *e, *s;
4744   size_t name_len;
4745   int len;
4746
4747   if (dst == NULL || dst_len == 0) {
4748   len = -2;
4749   } else if (data == NULL || name == NULL || data_len == 0) {
4750   len = -1;
4751   dst[0] = '\0';
4752   } else {
4753   name_len = strlen(name);
4754   e = data + data_len;
4755   len = -1;
4756   dst[0] = '\0';
4757
4758   // data is "var1=val1&var2=val2...". Find variable first
4759   for (p = data; p + name_len < e; p++) {
4760      if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
4761         !mg_strncasecmp(name, p, name_len)) {
4762      // Point p to variable value
4763      p += name_len + 1;
4764
4765      // Point s to the end of the value
4766      s = (const char *) memchr(p, '&', (size_t)(e - p));
4767      if (s == NULL) {
4768         s = e;
4769      }
4770      assert(s >= p);
4771
4772      // Decode variable into destination buffer
4773      len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
4774
4775      // Redirect error code from -1 to -2 (destination buffer too small).
4776      if (len == -1) {
4777         len = -2;
4778      }
4779      break;
4780      }
4781   }
4782   }
4783
4784   return len;
4785}
4786
4787int mg_get_var(const struct mg_connection *conn, const char *name,
4788            char *dst, size_t dst_len) {
4789   int len = get_var(conn->query_string, conn->query_string == NULL ? 0 :
4790               strlen(conn->query_string), name, dst, dst_len);
4791   if (len < 0) {
4792   len = get_var(conn->content, conn->content_len, name, dst, dst_len);
4793   }
4794   return len;
4795}
4796
4797static int get_line_len(const char *buf, int buf_len) {
4798   int len = 0;
4799   while (len < buf_len && buf[len] != '\n') len++;
4800   return buf[len] == '\n' ? len + 1: -1;
4801}
4802
4803int mg_parse_multipart(const char *buf, int buf_len,
4804                  char *var_name, int var_name_len,
4805                  char *file_name, int file_name_len,
4806                  const char **data, int *data_len) {
4807   static const char cd[] = "Content-Disposition: ";
4808   //struct mg_connection c;
4809   int hl, bl, n, ll, pos, cdl = sizeof(cd) - 1;
4810   //char *p;
4811
4812   if (buf == NULL || buf_len <= 0) return 0;
4813   if ((hl = get_request_len(buf, buf_len)) <= 0) return 0;
4814   if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0;
4815
4816   // Get boundary length
4817   bl = get_line_len(buf, buf_len);
4818
4819   // Loop through headers, fetch variable name and file name
4820   var_name[0] = file_name[0] = '\0';
4821   for (n = bl; (ll = get_line_len(buf + n, hl - n)) > 0; n += ll) {
4822   if (mg_strncasecmp(cd, buf + n, cdl) == 0) {
4823      parse_header(buf + n + cdl, ll - (cdl + 2), "name",
4824               var_name, var_name_len);
4825      parse_header(buf + n + cdl, ll - (cdl + 2), "filename",
4826               file_name, file_name_len);
4827   }
4828   }
4829
4830   // Scan body, search for terminating boundary
4831   for (pos = hl; pos + (bl - 2) < buf_len; pos++) {
4832   if (buf[pos] == '-' && !memcmp(buf, &buf[pos], bl - 2)) {
4833      if (data_len != NULL) *data_len = (pos - 2) - hl;
4834      if (data != NULL) *data = buf + hl;
4835      return pos;
4836   }
4837   }
4838
4839   return 0;
4840}
4841
4842const char **mg_get_valid_option_names(void) {
4843   return static_config_options;
4844}
4845
4846static int get_option_index(const char *name) {
4847   int i;
4848
4849   for (i = 0; static_config_options[i * 2] != NULL; i++) {
4850   if (strcmp(static_config_options[i * 2], name) == 0) {
4851      return i;
4852   }
4853   }
4854   return -1;
4855}
4856
4857static void set_default_option_values(char **opts) {
4858   const char *value, **all_opts = mg_get_valid_option_names();
4859   int i;
4860
4861   for (i = 0; all_opts[i * 2] != NULL; i++) {
4862   value = all_opts[i * 2 + 1];
4863   if (opts[i] == NULL && value != NULL) {
4864      opts[i] = mg_strdup(value);
4865   }
4866   }
4867}
4868
4869const char *mg_set_option(struct mg_server *server, const char *name,
4870                     const char *value) {
4871   int ind = get_option_index(name);
4872   const char *error_msg = NULL;
4873   char **v = NULL;
4874
4875   if (ind < 0) return  "No such option";
4876   v = &server->config_options[ind];
4877
4878   // Return success immediately if setting to the same value
4879   if ((*v == NULL && value == NULL) ||
4880      (value != NULL && *v != NULL && !strcmp(value, *v))) {
4881   return NULL;
4882   }
4883
4884   if (*v != NULL) {
4885   free(*v);
4886   *v = NULL;
4887   }
4888
4889   if (value == NULL || value[0] == '\0') return NULL;
4890
4891   *v = mg_strdup(value);
4892   DBG(("%s [%s]", name, *v));
4893
4894   if (ind == LISTENING_PORT) {
4895   int port = ns_bind(&server->ns_server, value);
4896   if (port < 0) {
4897      error_msg = "Cannot bind to port";
4898   } else {
4899      char buf[100];
4900      ns_sock_to_str(server->ns_server.listening_sock, buf, sizeof(buf), 2);
4901      free(*v);
4902      *v = mg_strdup(buf);
4903   }
4904#ifndef MONGOOSE_NO_FILESYSTEM
4905   } else if (ind == HEXDUMP_FILE) {
4906   server->ns_server.hexdump_file = *v;
4907#endif
4908#ifndef _WIN32
4909   } else if (ind == RUN_AS_USER) {
4910   struct passwd *pw;
4911   if ((pw = getpwnam(value)) == NULL) {
4912      error_msg = "Unknown user";
4913   } else if (setgid(pw->pw_gid) != 0) {
4914      error_msg = "setgid() failed";
4915   } else if (setuid(pw->pw_uid) != 0) {
4916      error_msg = "setuid() failed";
4917   }
4918#endif
4919#ifdef NS_ENABLE_SSL
4920   } else if (ind == SSL_CERTIFICATE) {
4921   int res = ns_set_ssl_cert(&server->ns_server, value);
4922   if (res == -2) {
4923      error_msg = "Cannot load PEM";
4924   } else if (res == -3) {
4925      error_msg = "SSL not enabled";
4926   } else if (res == -1) {
4927      error_msg = "SSL_CTX_new() failed";
4928   }
4929   } else if (ind == SSL_CA_CERTIFICATE) {
4930   if (ns_set_ssl_ca_cert(&server->ns_server, value) != 0) {
4931      error_msg = "Error setting CA cert";
4932      }
4933#endif
4934   }
4935
4936   return error_msg;
4937}
4938
4939static void set_ips(struct ns_connection *nc, int is_rem) {
4940   struct connection *conn = (struct connection *) nc->connection_data;
4941   struct mg_connection *c = &conn->mg_conn;
4942   char buf[100];
4943
4944   ns_sock_to_str(nc->sock, buf, sizeof(buf), is_rem ? 7 : 3);
4945   sscanf(buf, "%47[^:]:%hu",
4946         is_rem ? c->remote_ip : c->local_ip,
4947         is_rem ? &c->remote_port : &c->local_port);
4948   //DBG(("%p %s %s", conn, is_rem ? "rem" : "loc", buf));
4949}
4950
4951static void on_accept(struct ns_connection *nc, union socket_address *sa) {
4952   struct mg_server *server = (struct mg_server *) nc->server;
4953   struct connection *conn;
4954
4955   if (!check_acl(server->config_options[ACCESS_CONTROL_LIST],
4956               ntohl(* (uint32_t *) &sa->sin.sin_addr)) ||
4957      (conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
4958   nc->flags |= NSF_CLOSE_IMMEDIATELY;
4959   } else {
4960   // Circularly link two connection structures
4961   nc->connection_data = conn;
4962   conn->ns_conn = nc;
4963
4964   // Initialize the rest of connection attributes
4965   conn->server = server;
4966   conn->mg_conn.server_param = nc->server->server_data;
4967   set_ips(nc, 1);
4968   set_ips(nc, 0);
4969   }
4970}
4971
4972static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
4973   struct connection *conn = (struct connection *) nc->connection_data;
4974
4975   // Send NS event to the handler. Note that call_user won't send an event
4976   // if conn == NULL. Therefore, repeat this for NS_ACCEPT event as well.
4977#ifdef MONGOOSE_SEND_NS_EVENTS
4978   {
4979   struct connection *conn = (struct connection *) nc->connection_data;
4980   void *param[2] = { nc, p };
4981   if (conn != NULL) conn->mg_conn.callback_param = param;
4982   call_user(conn, (enum mg_event) ev);
4983   }
4984#endif
4985
4986   switch (ev) {
4987   case NS_ACCEPT:
4988      on_accept(nc, (union socket_address *) p);
4989#ifdef MONGOOSE_SEND_NS_EVENTS
4990      {
4991      struct connection *conn = (struct connection *) nc->connection_data;
4992      void *param[2] = { nc, p };
4993      if (conn != NULL) conn->mg_conn.callback_param = param;
4994      call_user(conn, (enum mg_event) ev);
4995      }
4996#endif
4997      break;
4998
4999   case NS_CONNECT:
5000      if (nc->connection_data != NULL) {
5001      set_ips(nc, 1);
5002      set_ips(nc, 0);
5003      }
5004      conn->mg_conn.status_code = * (int *) p;
5005      if (conn->mg_conn.status_code != 0 ||
5006         (!(nc->flags & MG_PROXY_CONN) &&
5007         call_user(conn, MG_CONNECT) == MG_FALSE)) {
5008      nc->flags |= NSF_CLOSE_IMMEDIATELY;
5009      }
5010      break;
5011
5012   case NS_RECV:
5013      if (conn != NULL) {
5014      conn->num_bytes_recv += * (int *) p;
5015      }
5016      if (nc->flags & NSF_ACCEPTED) {
5017      on_recv_data(conn);
5018#ifndef MONGOOSE_NO_CGI
5019      } else if (nc->flags & MG_CGI_CONN) {
5020      on_cgi_data(nc);
5021#endif
5022      } else if (nc->flags & MG_PROXY_CONN) {
5023      if (conn != NULL) {
5024         ns_forward(nc, conn->ns_conn);
5025      }
5026      } else {
5027      process_response(conn);
5028      }
5029      break;
5030
5031   case NS_SEND:
5032      break;
5033
5034   case NS_CLOSE:
5035      nc->connection_data = NULL;
5036      if (nc->flags & (MG_CGI_CONN | MG_PROXY_CONN)) {
5037      DBG(("%p %p closing cgi/proxy conn", conn, nc));
5038      if (conn && conn->ns_conn) {
5039         conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
5040         conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len > 0 ?
5041         NSF_FINISHED_SENDING_DATA : NSF_CLOSE_IMMEDIATELY;
5042         conn->endpoint.nc = NULL;
5043      }
5044      } else if (conn != NULL) {
5045      DBG(("%p %p %d closing", conn, nc, conn->endpoint_type));
5046
5047      if (conn->endpoint_type == EP_CLIENT && nc->recv_iobuf.len > 0) {
5048         call_http_client_handler(conn);
5049      }
5050
5051      call_user(conn, MG_CLOSE);
5052      close_local_endpoint(conn);
5053      conn->ns_conn = NULL;
5054      free(conn);
5055      }
5056      break;
5057
5058   case NS_POLL:
5059      if (conn != NULL) {
5060      if (call_user(conn, MG_POLL) == MG_TRUE) {
5061         if (conn->ns_conn->flags & MG_HEADERS_SENT) {
5062         write_terminating_chunk(conn);
5063         }
5064         close_local_endpoint(conn);
5065      }
5066
5067      if (conn->endpoint_type == EP_FILE) {
5068         transfer_file_data(conn);
5069      }
5070      }
5071
5072      // Expire idle connections
5073      {
5074      time_t current_time = * (time_t *) p;
5075
5076      if (conn != NULL && conn->mg_conn.is_websocket) {
5077         ping_idle_websocket_connection(conn, current_time);
5078      }
5079
5080      if (nc->last_io_time + MONGOOSE_IDLE_TIMEOUT_SECONDS < current_time) {
5081         mg_ev_handler(nc, NS_CLOSE, NULL);
5082         nc->flags |= NSF_CLOSE_IMMEDIATELY;
5083      }
5084      }
5085      break;
5086
5087   default:
5088      break;
5089   }
5090}
5091
5092static void iter2(struct ns_connection *nc, enum ns_event ev, void *param) {
5093   mg_handler_t func = NULL;
5094   struct connection *conn = (struct connection *) nc->connection_data;
5095   const char *msg = (const char *) param;
5096   int n;
5097   (void) ev;
5098
5099   //DBG(("%p [%s]", conn, msg));
5100   if (sscanf(msg, "%p %n", &func, &n) && func != NULL) {
5101   conn->mg_conn.callback_param = (void *) (msg + n);
5102   func(&conn->mg_conn, MG_POLL);
5103   }
5104}
5105
5106void mg_wakeup_server_ex(struct mg_server *server, mg_handler_t cb,
5107                     const char *fmt, ...) {
5108   va_list ap;
5109   char buf[8 * 1024];
5110   int len;
5111
5112   // Encode callback (cb) into a buffer
5113   len = snprintf(buf, sizeof(buf), "%p ", cb);
5114   va_start(ap, fmt);
5115   len += vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
5116   va_end(ap);
5117
5118   // "len + 1" is to include terminating \0 in the message
5119   ns_server_wakeup_ex(&server->ns_server, iter2, buf, len + 1);
5120}
5121
5122void mg_wakeup_server(struct mg_server *server) {
5123   ns_server_wakeup_ex(&server->ns_server, NULL, (void *) "", 0);
5124}
5125
5126void mg_set_listening_socket(struct mg_server *server, int sock) {
5127   if (server->ns_server.listening_sock != INVALID_SOCKET) {
5128   closesocket(server->ns_server.listening_sock);
5129   }
5130   server->ns_server.listening_sock = (sock_t) sock;
5131}
5132
5133int mg_get_listening_socket(struct mg_server *server) {
5134   return server->ns_server.listening_sock;
5135}
5136
5137const char *mg_get_option(const struct mg_server *server, const char *name) {
5138   const char **opts = (const char **) server->config_options;
5139   int i = get_option_index(name);
5140   return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i];
5141}
5142
5143struct mg_server *mg_create_server(void *server_data, mg_handler_t handler) {
5144   struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));
5145   ns_server_init(&server->ns_server, server_data, mg_ev_handler);
5146   set_default_option_values(server->config_options);
5147   server->event_handler = handler;
5148   return server;
5149}
5150
5151#ifdef _WIN32
5152static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
5153               int offset) {
5154   HANDLE fh = (HANDLE) _get_osfhandle(fd);
5155   HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
5156   void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
5157   CloseHandle(mh);
5158   return p;
5159}
5160#define munmap(x, y)  UnmapViewOfFile(x)
5161#define MAP_FAILED NULL
5162#define MAP_PRIVATE 0
5163#define PROT_READ 0
5164#elif defined(__OS2__)
5165static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
5166               int offset) {
5167   void *p;
5168   off_t curpos;
5169
5170   p = malloc(len);
5171   if (!p)
5172      return NULL;
5173
5174   curpos = lseek(fd, 0, SEEK_CUR);
5175   lseek(fd, offset, SEEK_SET);
5176   read(fd, p, len);
5177   lseek(fd, curpos, SEEK_SET);
5178
5179   return p;
5180}
5181
5182#define munmap(x, y)  free(x)
5183#define MAP_FAILED NULL
5184#define MAP_PRIVATE 0
5185#define PROT_READ 0
5186#else
5187#include <sys/mman.h>
5188#endif
5189
5190void *mg_mmap(FILE *fp, size_t size) {
5191   return mmap(NULL, size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
5192}
5193
5194void mg_munmap(void *p, size_t size) {
5195   munmap(p, size);
5196}
trunk/src/lib/web/mongoose.h
r242828r242829
1// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
2// Copyright (c) 2013-2014 Cesanta Software Limited
3// All rights reserved
4//
5// This library is dual-licensed: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License version 2 as
7// published by the Free Software Foundation. For the terms of this
8// license, see <http://www.gnu.org/licenses/>.
9//
10// You are free to use this library under the terms of the GNU General
11// Public License, but WITHOUT ANY WARRANTY; without even the implied
12// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13// See the GNU General Public License for more details.
14//
15// Alternatively, you can license this library under a commercial
16// license, as set out in <http://cesanta.com/>.
17//
18// $Date: 2014-09-01 19:53:26 UTC $
19
20#ifndef MONGOOSE_HEADER_INCLUDED
21#define  MONGOOSE_HEADER_INCLUDED
22
23#define MONGOOSE_VERSION "5.4"
24
25#include <stdio.h>      // required for FILE
26#include <stddef.h>     // required for size_t
27
28#ifdef __cplusplus
29extern "C" {
30#endif // __cplusplus
31
32// This structure contains information about HTTP request.
33struct mg_connection {
34   const char *request_method; // "GET", "POST", etc
35   const char *uri;            // URL-decoded URI
36   const char *http_version;   // E.g. "1.0", "1.1"
37   const char *query_string;   // URL part after '?', not including '?', or NULL
38
39   char remote_ip[48];         // Max IPv6 string length is 45 characters
40   char local_ip[48];          // Local IP address
41   unsigned short remote_port; // Client's port
42   unsigned short local_port;  // Local port number
43
44   int num_headers;            // Number of HTTP headers
45   struct mg_header {
46   const char *name;         // HTTP header name
47   const char *value;        // HTTP header value
48   } http_headers[30];
49
50   char *content;              // POST (or websocket message) data, or NULL
51   size_t content_len;         // Data length
52
53   int is_websocket;           // Connection is a websocket connection
54   int status_code;            // HTTP status code for HTTP error handler
55   int wsbits;                 // First byte of the websocket frame
56   void *server_param;         // Parameter passed to mg_add_uri_handler()
57   void *connection_param;     // Placeholder for connection-specific data
58   void *callback_param;       // Needed by mg_iterate_over_connections()
59};
60
61struct mg_server; // Opaque structure describing server instance
62enum mg_result { MG_FALSE, MG_TRUE, MG_MORE };
63enum mg_event {
64   MG_POLL = 100,  // Callback return value is ignored
65   MG_CONNECT,     // If callback returns MG_FALSE, connect fails
66   MG_AUTH,        // If callback returns MG_FALSE, authentication fails
67   MG_REQUEST,     // If callback returns MG_FALSE, Mongoose continues with req
68   MG_REPLY,       // If callback returns MG_FALSE, Mongoose closes connection
69   MG_RECV,        // Mongoose has received POST data chunk.
70               // Callback should return a number of bytes to discard from
71               // the receive buffer, or -1 to close the connection.
72   MG_CLOSE,       // Connection is closed, callback return value is ignored
73   MG_WS_HANDSHAKE,  // New websocket connection, handshake request
74   MG_WS_CONNECT,  // New websocket connection established
75   MG_HTTP_ERROR   // If callback returns MG_FALSE, Mongoose continues with err
76};
77typedef int (*mg_handler_t)(struct mg_connection *, enum mg_event);
78
79// Websocket opcodes, from http://tools.ietf.org/html/rfc6455
80enum {
81   WEBSOCKET_OPCODE_CONTINUATION = 0x0,
82   WEBSOCKET_OPCODE_TEXT = 0x1,
83   WEBSOCKET_OPCODE_BINARY = 0x2,
84   WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
85   WEBSOCKET_OPCODE_PING = 0x9,
86   WEBSOCKET_OPCODE_PONG = 0xa
87};
88
89// Server management functions
90struct mg_server *mg_create_server(void *server_param, mg_handler_t handler);
91void mg_destroy_server(struct mg_server **);
92const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
93int mg_poll_server(struct mg_server *, int milliseconds);
94const char **mg_get_valid_option_names(void);
95const char *mg_get_option(const struct mg_server *server, const char *name);
96void mg_set_listening_socket(struct mg_server *, int sock);
97int mg_get_listening_socket(struct mg_server *);
98void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
99struct mg_connection *mg_next(struct mg_server *, struct mg_connection *);
100void mg_wakeup_server(struct mg_server *);
101void mg_wakeup_server_ex(struct mg_server *, mg_handler_t, const char *, ...);
102struct mg_connection *mg_connect(struct mg_server *, const char *, int, int);
103
104// Connection management functions
105void mg_send_status(struct mg_connection *, int status_code);
106void mg_send_header(struct mg_connection *, const char *name, const char *val);
107size_t mg_send_data(struct mg_connection *, const void *data, int data_len);
108size_t mg_printf_data(struct mg_connection *, const char *format, ...);
109size_t mg_write(struct mg_connection *, const void *buf, int len);
110size_t mg_printf(struct mg_connection *conn, const char *fmt, ...);
111
112size_t mg_websocket_write(struct mg_connection *, int opcode,
113                     const char *data, size_t data_len);
114size_t mg_websocket_printf(struct mg_connection* conn, int opcode,
115                     const char *fmt, ...);
116
117void mg_send_file(struct mg_connection *, const char *path);
118void mg_send_file_data(struct mg_connection *, int fd);
119
120const char *mg_get_header(const struct mg_connection *, const char *name);
121const char *mg_get_mime_type(const char *name, const char *default_mime_type);
122int mg_get_var(const struct mg_connection *conn, const char *var_name,
123            char *buf, size_t buf_len);
124int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
125int mg_parse_multipart(const char *buf, int buf_len,
126                  char *var_name, int var_name_len,
127                  char *file_name, int file_name_len,
128                  const char **data, int *data_len);
129
130// Utility functions
131void *mg_start_thread(void *(*func)(void *), void *param);
132char *mg_md5(char buf[33], ...);
133int mg_authorize_digest(struct mg_connection *c, FILE *fp);
134int mg_url_encode(const char *src, size_t s_len, char *dst, size_t dst_len);
135int mg_url_decode(const char *src, int src_len, char *dst, int dst_len, int);
136int mg_terminate_ssl(struct mg_connection *c, const char *cert);
137int mg_forward(struct mg_connection *, const char *host, int port, int use_ssl);
138void *mg_mmap(FILE *fp, size_t size);
139void mg_munmap(void *p, size_t size);
140
141
142// Templates support
143struct mg_expansion {
144   const char *keyword;
145   void (*handler)(struct mg_connection *);
146};
147void mg_template(struct mg_connection *, const char *text,
148               struct mg_expansion *expansions);
149
150
151#ifdef __cplusplus
152}
153#endif // __cplusplus
154
155#endif // MONGOOSE_HEADER_INCLUDED


Previous 199869 Revisions Next


© 1997-2024 The MAME Team