1 /* Copyright (c) 2013-2017 the Civetweb developers
2 * Copyright (c) 2013 No Face Press, LLC
3 *
4 * License http://opensource.org/licenses/mit-license.php MIT License
5 */
6
7 #include "CivetServer.h"
8
9 #include <assert.h>
10 #include <stdexcept>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #ifndef UNUSED_PARAMETER
15 #define UNUSED_PARAMETER(x) (void)(x)
16 #endif
17
18 #ifndef MAX_PARAM_BODY_LENGTH
19 // Set a default limit for parameters in a form body: 2 MB
20 #define MAX_PARAM_BODY_LENGTH (1024 * 1024 * 2)
21 #endif
22
23 bool
handleGet(CivetServer * server,struct mg_connection * conn)24 CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn)
25 {
26 UNUSED_PARAMETER(server);
27 UNUSED_PARAMETER(conn);
28 return false;
29 }
30
31 bool
handlePost(CivetServer * server,struct mg_connection * conn)32 CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn)
33 {
34 UNUSED_PARAMETER(server);
35 UNUSED_PARAMETER(conn);
36 return false;
37 }
38
39 bool
handleHead(CivetServer * server,struct mg_connection * conn)40 CivetHandler::handleHead(CivetServer *server, struct mg_connection *conn)
41 {
42 UNUSED_PARAMETER(server);
43 UNUSED_PARAMETER(conn);
44 return false;
45 }
46
47 bool
handlePut(CivetServer * server,struct mg_connection * conn)48 CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn)
49 {
50 UNUSED_PARAMETER(server);
51 UNUSED_PARAMETER(conn);
52 return false;
53 }
54
55 bool
handlePatch(CivetServer * server,struct mg_connection * conn)56 CivetHandler::handlePatch(CivetServer *server, struct mg_connection *conn)
57 {
58 UNUSED_PARAMETER(server);
59 UNUSED_PARAMETER(conn);
60 return false;
61 }
62
63 bool
handleDelete(CivetServer * server,struct mg_connection * conn)64 CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn)
65 {
66 UNUSED_PARAMETER(server);
67 UNUSED_PARAMETER(conn);
68 return false;
69 }
70
71 bool
handleOptions(CivetServer * server,struct mg_connection * conn)72 CivetHandler::handleOptions(CivetServer *server, struct mg_connection *conn)
73 {
74 UNUSED_PARAMETER(server);
75 UNUSED_PARAMETER(conn);
76 return false;
77 }
78
79 bool
handleConnection(CivetServer * server,const struct mg_connection * conn)80 CivetWebSocketHandler::handleConnection(CivetServer *server,
81 const struct mg_connection *conn)
82 {
83 UNUSED_PARAMETER(server);
84 UNUSED_PARAMETER(conn);
85 return true;
86 }
87
88 void
handleReadyState(CivetServer * server,struct mg_connection * conn)89 CivetWebSocketHandler::handleReadyState(CivetServer *server,
90 struct mg_connection *conn)
91 {
92 UNUSED_PARAMETER(server);
93 UNUSED_PARAMETER(conn);
94 return;
95 }
96
97 bool
handleData(CivetServer * server,struct mg_connection * conn,int bits,char * data,size_t data_len)98 CivetWebSocketHandler::handleData(CivetServer *server,
99 struct mg_connection *conn,
100 int bits,
101 char *data,
102 size_t data_len)
103 {
104 UNUSED_PARAMETER(server);
105 UNUSED_PARAMETER(conn);
106 UNUSED_PARAMETER(bits);
107 UNUSED_PARAMETER(data);
108 UNUSED_PARAMETER(data_len);
109 return true;
110 }
111
112 void
handleClose(CivetServer * server,const struct mg_connection * conn)113 CivetWebSocketHandler::handleClose(CivetServer *server,
114 const struct mg_connection *conn)
115 {
116 UNUSED_PARAMETER(server);
117 UNUSED_PARAMETER(conn);
118 return;
119 }
120
121 int
requestHandler(struct mg_connection * conn,void * cbdata)122 CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
123 {
124 const struct mg_request_info *request_info = mg_get_request_info(conn);
125 assert(request_info != NULL);
126 CivetServer *me = (CivetServer *)(request_info->user_data);
127 assert(me != NULL);
128
129 // Happens when a request hits the server before the context is saved
130 if (me->context == NULL)
131 return 0;
132
133 mg_lock_context(me->context);
134 me->connections[conn] = CivetConnection();
135 mg_unlock_context(me->context);
136
137 CivetHandler *handler = (CivetHandler *)cbdata;
138
139 if (handler) {
140 if (strcmp(request_info->request_method, "GET") == 0) {
141 return handler->handleGet(me, conn) ? 1 : 0;
142 } else if (strcmp(request_info->request_method, "POST") == 0) {
143 return handler->handlePost(me, conn) ? 1 : 0;
144 } else if (strcmp(request_info->request_method, "HEAD") == 0) {
145 return handler->handleHead(me, conn) ? 1 : 0;
146 } else if (strcmp(request_info->request_method, "PUT") == 0) {
147 return handler->handlePut(me, conn) ? 1 : 0;
148 } else if (strcmp(request_info->request_method, "DELETE") == 0) {
149 return handler->handleDelete(me, conn) ? 1 : 0;
150 } else if (strcmp(request_info->request_method, "OPTIONS") == 0) {
151 return handler->handleOptions(me, conn) ? 1 : 0;
152 } else if (strcmp(request_info->request_method, "PATCH") == 0) {
153 return handler->handlePatch(me, conn) ? 1 : 0;
154 }
155 }
156
157 return 0; // No handler found
158 }
159
160 int
authHandler(struct mg_connection * conn,void * cbdata)161 CivetServer::authHandler(struct mg_connection *conn, void *cbdata)
162 {
163 const struct mg_request_info *request_info = mg_get_request_info(conn);
164 assert(request_info != NULL);
165 CivetServer *me = (CivetServer *)(request_info->user_data);
166 assert(me != NULL);
167
168 // Happens when a request hits the server before the context is saved
169 if (me->context == NULL)
170 return 0;
171
172 mg_lock_context(me->context);
173 me->connections[conn] = CivetConnection();
174 mg_unlock_context(me->context);
175
176 CivetAuthHandler *handler = (CivetAuthHandler *)cbdata;
177
178 if (handler) {
179 return handler->authorize(me, conn) ? 1 : 0;
180 }
181
182 return 0; // No handler found
183 }
184
185 int
webSocketConnectionHandler(const struct mg_connection * conn,void * cbdata)186 CivetServer::webSocketConnectionHandler(const struct mg_connection *conn,
187 void *cbdata)
188 {
189 const struct mg_request_info *request_info = mg_get_request_info(conn);
190 assert(request_info != NULL);
191 CivetServer *me = (CivetServer *)(request_info->user_data);
192 assert(me != NULL);
193
194 // Happens when a request hits the server before the context is saved
195 if (me->context == NULL)
196 return 0;
197
198 CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
199
200 if (handler) {
201 return handler->handleConnection(me, conn) ? 0 : 1;
202 }
203
204 return 1; // No handler found, close connection
205 }
206
207 void
webSocketReadyHandler(struct mg_connection * conn,void * cbdata)208 CivetServer::webSocketReadyHandler(struct mg_connection *conn, void *cbdata)
209 {
210 const struct mg_request_info *request_info = mg_get_request_info(conn);
211 assert(request_info != NULL);
212 CivetServer *me = (CivetServer *)(request_info->user_data);
213 assert(me != NULL);
214
215 // Happens when a request hits the server before the context is saved
216 if (me->context == NULL)
217 return;
218
219 CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
220
221 if (handler) {
222 handler->handleReadyState(me, conn);
223 }
224 }
225
226 int
webSocketDataHandler(struct mg_connection * conn,int bits,char * data,size_t data_len,void * cbdata)227 CivetServer::webSocketDataHandler(struct mg_connection *conn,
228 int bits,
229 char *data,
230 size_t data_len,
231 void *cbdata)
232 {
233 const struct mg_request_info *request_info = mg_get_request_info(conn);
234 assert(request_info != NULL);
235 CivetServer *me = (CivetServer *)(request_info->user_data);
236 assert(me != NULL);
237
238 // Happens when a request hits the server before the context is saved
239 if (me->context == NULL)
240 return 0;
241
242 CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
243
244 if (handler) {
245 return handler->handleData(me, conn, bits, data, data_len) ? 1 : 0;
246 }
247
248 return 1; // No handler found
249 }
250
251 void
webSocketCloseHandler(const struct mg_connection * conn,void * cbdata)252 CivetServer::webSocketCloseHandler(const struct mg_connection *conn,
253 void *cbdata)
254 {
255 const struct mg_request_info *request_info = mg_get_request_info(conn);
256 assert(request_info != NULL);
257 CivetServer *me = (CivetServer *)(request_info->user_data);
258 assert(me != NULL);
259
260 // Happens when a request hits the server before the context is saved
261 if (me->context == NULL)
262 return;
263
264 CivetWebSocketHandler *handler = (CivetWebSocketHandler *)cbdata;
265
266 if (handler) {
267 handler->handleClose(me, conn);
268 }
269 }
270
CivetCallbacks()271 CivetCallbacks::CivetCallbacks()
272 {
273 memset(this, 0, sizeof(*this));
274 }
275
CivetServer(const char ** options,const struct CivetCallbacks * _callbacks,const void * UserContextIn)276 CivetServer::CivetServer(const char **options,
277 const struct CivetCallbacks *_callbacks,
278 const void *UserContextIn)
279 : context(0)
280 {
281 struct CivetCallbacks callbacks;
282
283 UserContext = UserContextIn;
284
285 if (_callbacks) {
286 callbacks = *_callbacks;
287 userCloseHandler = _callbacks->connection_close;
288 } else {
289 userCloseHandler = NULL;
290 }
291 callbacks.connection_close = closeHandler;
292 context = mg_start(&callbacks, this, options);
293 if (context == NULL)
294 throw CivetException("null context when constructing CivetServer. "
295 "Possible problem binding to port.");
296 }
297
CivetServer(std::vector<std::string> options,const struct CivetCallbacks * _callbacks,const void * UserContextIn)298 CivetServer::CivetServer(std::vector<std::string> options,
299 const struct CivetCallbacks *_callbacks,
300 const void *UserContextIn)
301 : context(0)
302 {
303 struct CivetCallbacks callbacks;
304
305 UserContext = UserContextIn;
306
307 if (_callbacks) {
308 callbacks = *_callbacks;
309 userCloseHandler = _callbacks->connection_close;
310 } else {
311 userCloseHandler = NULL;
312 }
313 callbacks.connection_close = closeHandler;
314
315 std::vector<const char *> pointers(options.size());
316 for (size_t i = 0; i < options.size(); i++) {
317 pointers[i] = (options[i].c_str());
318 }
319 pointers.push_back(0);
320
321 context = mg_start(&callbacks, this, &pointers[0]);
322 if (context == NULL)
323 throw CivetException("null context when constructing CivetServer. "
324 "Possible problem binding to port.");
325 }
326
~CivetServer()327 CivetServer::~CivetServer()
328 {
329 close();
330 }
331
332 void
closeHandler(const struct mg_connection * conn)333 CivetServer::closeHandler(const struct mg_connection *conn)
334 {
335 CivetServer *me = (CivetServer *)mg_get_user_data(mg_get_context(conn));
336 assert(me != NULL);
337
338 // Happens when a request hits the server before the context is saved
339 if (me->context == NULL)
340 return;
341
342 if (me->userCloseHandler) {
343 me->userCloseHandler(conn);
344 }
345 mg_lock_context(me->context);
346 me->connections.erase(const_cast<struct mg_connection *>(conn));
347 mg_unlock_context(me->context);
348 }
349
350 void
addHandler(const std::string & uri,CivetHandler * handler)351 CivetServer::addHandler(const std::string &uri, CivetHandler *handler)
352 {
353 mg_set_request_handler(context, uri.c_str(), requestHandler, handler);
354 }
355
356 void
addWebSocketHandler(const std::string & uri,CivetWebSocketHandler * handler)357 CivetServer::addWebSocketHandler(const std::string &uri,
358 CivetWebSocketHandler *handler)
359 {
360 mg_set_websocket_handler(context,
361 uri.c_str(),
362 webSocketConnectionHandler,
363 webSocketReadyHandler,
364 webSocketDataHandler,
365 webSocketCloseHandler,
366 handler);
367 }
368
369 void
addAuthHandler(const std::string & uri,CivetAuthHandler * handler)370 CivetServer::addAuthHandler(const std::string &uri, CivetAuthHandler *handler)
371 {
372 mg_set_auth_handler(context, uri.c_str(), authHandler, handler);
373 }
374
375 void
removeHandler(const std::string & uri)376 CivetServer::removeHandler(const std::string &uri)
377 {
378 mg_set_request_handler(context, uri.c_str(), NULL, NULL);
379 }
380
381 void
removeWebSocketHandler(const std::string & uri)382 CivetServer::removeWebSocketHandler(const std::string &uri)
383 {
384 mg_set_websocket_handler(
385 context, uri.c_str(), NULL, NULL, NULL, NULL, NULL);
386 }
387
388 void
removeAuthHandler(const std::string & uri)389 CivetServer::removeAuthHandler(const std::string &uri)
390 {
391 mg_set_auth_handler(context, uri.c_str(), NULL, NULL);
392 }
393
394 void
close()395 CivetServer::close()
396 {
397 if (context) {
398 mg_stop(context);
399 context = 0;
400 }
401 }
402
403 int
getCookie(struct mg_connection * conn,const std::string & cookieName,std::string & cookieValue)404 CivetServer::getCookie(struct mg_connection *conn,
405 const std::string &cookieName,
406 std::string &cookieValue)
407 {
408 // Maximum cookie length as per microsoft is 4096.
409 // http://msdn.microsoft.com/en-us/library/ms178194.aspx
410 char _cookieValue[4096];
411 const char *cookie = mg_get_header(conn, "Cookie");
412 int lRead = mg_get_cookie(cookie,
413 cookieName.c_str(),
414 _cookieValue,
415 sizeof(_cookieValue));
416 cookieValue.clear();
417 cookieValue.append(_cookieValue);
418 return lRead;
419 }
420
421 const char *
getHeader(struct mg_connection * conn,const std::string & headerName)422 CivetServer::getHeader(struct mg_connection *conn,
423 const std::string &headerName)
424 {
425 return mg_get_header(conn, headerName.c_str());
426 }
427
428 void
urlDecode(const char * src,std::string & dst,bool is_form_url_encoded)429 CivetServer::urlDecode(const char *src,
430 std::string &dst,
431 bool is_form_url_encoded)
432 {
433 urlDecode(src, strlen(src), dst, is_form_url_encoded);
434 }
435
436 void
urlDecode(const char * src,size_t src_len,std::string & dst,bool is_form_url_encoded)437 CivetServer::urlDecode(const char *src,
438 size_t src_len,
439 std::string &dst,
440 bool is_form_url_encoded)
441 {
442 int i, j, a, b;
443 #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
444
445 dst.clear();
446 for (i = j = 0; i < (int)src_len; i++, j++) {
447 if (i < (int)src_len - 2 && src[i] == '%'
448 && isxdigit((unsigned char)src[i + 1])
449 && isxdigit((unsigned char)src[i + 2])) {
450 a = tolower((unsigned char)src[i + 1]);
451 b = tolower((unsigned char)src[i + 2]);
452 dst.push_back((char)((HEXTOI(a) << 4) | HEXTOI(b)));
453 i += 2;
454 } else if (is_form_url_encoded && src[i] == '+') {
455 dst.push_back(' ');
456 } else {
457 dst.push_back(src[i]);
458 }
459 }
460 }
461
462 bool
getParam(struct mg_connection * conn,const char * name,std::string & dst,size_t occurrence)463 CivetServer::getParam(struct mg_connection *conn,
464 const char *name,
465 std::string &dst,
466 size_t occurrence)
467 {
468 const char *formParams = NULL;
469 const char *queryString = NULL;
470 const struct mg_request_info *ri = mg_get_request_info(conn);
471 assert(ri != NULL);
472 CivetServer *me = (CivetServer *)(ri->user_data);
473 assert(me != NULL);
474 mg_lock_context(me->context);
475 CivetConnection &conobj = me->connections[conn];
476 mg_lock_connection(conn);
477 mg_unlock_context(me->context);
478
479 if (conobj.postData != NULL) {
480 // check if form parameter are already stored
481 formParams = conobj.postData;
482 } else {
483 // otherwise, check if there is a request body
484 const char *con_len_str = mg_get_header(conn, "Content-Length");
485 if (con_len_str) {
486 char *end = 0;
487 unsigned long con_len = strtoul(con_len_str, &end, 10);
488 if ((end == NULL) || (*end != 0)) {
489 // malformed header
490 mg_unlock_connection(conn);
491 return false;
492 }
493 if ((con_len > 0) && (con_len <= MAX_PARAM_BODY_LENGTH)) {
494 // Body is within a reasonable range
495
496 // Allocate memory:
497 // Add one extra character: in case the post-data is a text, it
498 // is required as 0-termination.
499 // Do not increment con_len, since the 0 terminating is not part
500 // of the content (text or binary).
501 conobj.postData = (char *)malloc(con_len + 1);
502 if (conobj.postData != NULL) {
503 // malloc may fail for huge requests
504 mg_read(conn, conobj.postData, con_len);
505 conobj.postData[con_len] = 0;
506 formParams = conobj.postData;
507 conobj.postDataLen = con_len;
508 }
509 }
510 if (conobj.postData == NULL) {
511 // we cannot store the body
512 mg_unlock_connection(conn);
513 return false;
514 }
515 }
516 }
517
518 if (ri->query_string != NULL) {
519 // get requests do store html <form> field values in the http
520 // query_string
521 queryString = ri->query_string;
522 }
523
524 mg_unlock_connection(conn);
525
526 bool get_param_success = false;
527 if (formParams != NULL) {
528 get_param_success =
529 getParam(formParams, strlen(formParams), name, dst, occurrence);
530 }
531 if (!get_param_success && queryString != NULL) {
532 get_param_success =
533 getParam(queryString, strlen(queryString), name, dst, occurrence);
534 }
535
536 return get_param_success;
537 }
538
539 bool
getParam(const char * data,size_t data_len,const char * name,std::string & dst,size_t occurrence)540 CivetServer::getParam(const char *data,
541 size_t data_len,
542 const char *name,
543 std::string &dst,
544 size_t occurrence)
545 {
546 const char *p, *e, *s;
547 size_t name_len;
548
549 dst.clear();
550 if (data == NULL || name == NULL || data_len == 0) {
551 return false;
552 }
553 name_len = strlen(name);
554 e = data + data_len;
555
556 // data is "var1=val1&var2=val2...". Find variable first
557 for (p = data; p + name_len < e; p++) {
558 if ((p == data || p[-1] == '&') && p[name_len] == '='
559 && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
560
561 // Point p to variable value
562 p += name_len + 1;
563
564 // Point s to the end of the value
565 s = (const char *)memchr(p, '&', (size_t)(e - p));
566 if (s == NULL) {
567 s = e;
568 }
569 assert(s >= p);
570
571 // Decode variable into destination buffer
572 urlDecode(p, (s - p), dst, true);
573 return true;
574 }
575 }
576 return false;
577 }
578
579 std::string
getPostData(struct mg_connection * conn)580 CivetServer::getPostData(struct mg_connection *conn)
581 {
582 mg_lock_connection(conn);
583 std::string postdata;
584 char buf[2048];
585 int r = mg_read(conn, buf, sizeof(buf));
586 while (r > 0) {
587 postdata += std::string(buf, r);
588 r = mg_read(conn, buf, sizeof(buf));
589 }
590 mg_unlock_connection(conn);
591 return postdata;
592 }
593
594 void
urlEncode(const char * src,std::string & dst,bool append)595 CivetServer::urlEncode(const char *src, std::string &dst, bool append)
596 {
597 urlEncode(src, strlen(src), dst, append);
598 }
599
600 void
urlEncode(const char * src,size_t src_len,std::string & dst,bool append)601 CivetServer::urlEncode(const char *src,
602 size_t src_len,
603 std::string &dst,
604 bool append)
605 {
606 static const char *dont_escape = "._-$,;~()";
607 static const char *hex = "0123456789abcdef";
608
609 if (!append)
610 dst.clear();
611
612 for (; src_len > 0; src++, src_len--) {
613 if (isalnum((unsigned char)*src) || strchr(dont_escape, *src) != NULL) {
614 dst.push_back(*src);
615 } else {
616 dst.push_back('%');
617 dst.push_back(hex[(unsigned char)*src >> 4]);
618 dst.push_back(hex[(unsigned char)*src & 0xf]);
619 }
620 }
621 }
622
623 std::vector<int>
getListeningPorts()624 CivetServer::getListeningPorts()
625 {
626 std::vector<struct mg_server_port> server_ports = getListeningPortsFull();
627
628 std::vector<int> ports(server_ports.size());
629 for (size_t i = 0; i < server_ports.size(); i++) {
630 ports[i] = server_ports[i].port;
631 }
632
633 return ports;
634 }
635
636 std::vector<struct mg_server_port>
getListeningPortsFull()637 CivetServer::getListeningPortsFull()
638 {
639 std::vector<struct mg_server_port> server_ports(50);
640 int size = mg_get_server_ports(context,
641 (int)server_ports.size(),
642 &server_ports[0]);
643 if (size <= 0) {
644 server_ports.resize(0);
645 return server_ports;
646 }
647 server_ports.resize(size);
648 return server_ports;
649 }
650
CivetConnection()651 CivetServer::CivetConnection::CivetConnection()
652 {
653 postData = NULL;
654 postDataLen = 0;
655 }
656
~CivetConnection()657 CivetServer::CivetConnection::~CivetConnection()
658 {
659 free(postData);
660 }
661