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