]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* Copyright (c) 2013-2014 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 <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, | |
272 | const struct CivetCallbacks *_callbacks) | |
273 | : context(0) | |
274 | { | |
275 | struct CivetCallbacks callbacks; | |
276 | ||
277 | if (_callbacks) { | |
278 | callbacks = *_callbacks; | |
279 | userCloseHandler = _callbacks->connection_close; | |
280 | } else { | |
281 | userCloseHandler = NULL; | |
282 | } | |
283 | callbacks.connection_close = closeHandler; | |
284 | context = mg_start(&callbacks, this, options); | |
285 | if (context == NULL) | |
286 | throw CivetException("null context when constructing CivetServer. " | |
287 | "Possible problem binding to port."); | |
288 | } | |
289 | ||
290 | CivetServer::CivetServer(std::vector<std::string> options, | |
291 | const struct CivetCallbacks *_callbacks) | |
292 | : context(0) | |
293 | { | |
294 | struct CivetCallbacks callbacks; | |
295 | ||
296 | if (_callbacks) { | |
297 | callbacks = *_callbacks; | |
298 | userCloseHandler = _callbacks->connection_close; | |
299 | } else { | |
300 | userCloseHandler = NULL; | |
301 | } | |
302 | callbacks.connection_close = closeHandler; | |
303 | ||
304 | std::vector<const char *> pointers(options.size()); | |
305 | for (size_t i = 0; i < options.size(); i++) { | |
306 | pointers[i] = (options[i].c_str()); | |
307 | } | |
308 | pointers.push_back(0); | |
309 | ||
310 | context = mg_start(&callbacks, this, &pointers[0]); | |
311 | if (context == NULL) | |
312 | throw CivetException("null context when constructing CivetServer. " | |
313 | "Possible problem binding to port."); | |
314 | } | |
315 | ||
316 | CivetServer::~CivetServer() | |
317 | { | |
318 | close(); | |
319 | } | |
320 | ||
321 | void | |
322 | CivetServer::closeHandler(const struct mg_connection *conn) | |
323 | { | |
324 | const struct mg_request_info *request_info = mg_get_request_info(conn); | |
325 | assert(request_info != NULL); | |
326 | CivetServer *me = (CivetServer *)(request_info->user_data); | |
327 | assert(me != NULL); | |
328 | ||
329 | // Happens when a request hits the server before the context is saved | |
330 | if (me->context == NULL) | |
331 | return; | |
332 | ||
333 | if (me->userCloseHandler) | |
334 | me->userCloseHandler(conn); | |
335 | mg_lock_context(me->context); | |
336 | me->connections.erase(const_cast<struct mg_connection *>(conn)); | |
337 | mg_unlock_context(me->context); | |
338 | } | |
339 | ||
340 | void | |
341 | CivetServer::addHandler(const std::string &uri, CivetHandler *handler) | |
342 | { | |
343 | mg_set_request_handler(context, uri.c_str(), requestHandler, handler); | |
344 | } | |
345 | ||
346 | void | |
347 | CivetServer::addWebSocketHandler(const std::string &uri, | |
348 | CivetWebSocketHandler *handler) | |
349 | { | |
350 | mg_set_websocket_handler(context, | |
351 | uri.c_str(), | |
352 | webSocketConnectionHandler, | |
353 | webSocketReadyHandler, | |
354 | webSocketDataHandler, | |
355 | webSocketCloseHandler, | |
356 | handler); | |
357 | } | |
358 | ||
359 | void | |
360 | CivetServer::addAuthHandler(const std::string &uri, CivetAuthHandler *handler) | |
361 | { | |
362 | mg_set_auth_handler(context, uri.c_str(), authHandler, handler); | |
363 | } | |
364 | ||
365 | void | |
366 | CivetServer::removeHandler(const std::string &uri) | |
367 | { | |
368 | mg_set_request_handler(context, uri.c_str(), NULL, NULL); | |
369 | } | |
370 | ||
371 | void | |
372 | CivetServer::removeWebSocketHandler(const std::string &uri) | |
373 | { | |
374 | mg_set_websocket_handler( | |
375 | context, uri.c_str(), NULL, NULL, NULL, NULL, NULL); | |
376 | } | |
377 | ||
378 | void | |
379 | CivetServer::removeAuthHandler(const std::string &uri) | |
380 | { | |
381 | mg_set_auth_handler(context, uri.c_str(), NULL, NULL); | |
382 | } | |
383 | ||
384 | void | |
385 | CivetServer::close() | |
386 | { | |
387 | if (context) { | |
388 | mg_stop(context); | |
389 | context = 0; | |
390 | } | |
391 | } | |
392 | ||
393 | int | |
394 | CivetServer::getCookie(struct mg_connection *conn, | |
395 | const std::string &cookieName, | |
396 | std::string &cookieValue) | |
397 | { | |
398 | // Maximum cookie length as per microsoft is 4096. | |
399 | // http://msdn.microsoft.com/en-us/library/ms178194.aspx | |
400 | char _cookieValue[4096]; | |
401 | const char *cookie = mg_get_header(conn, "Cookie"); | |
402 | int lRead = mg_get_cookie(cookie, | |
403 | cookieName.c_str(), | |
404 | _cookieValue, | |
405 | sizeof(_cookieValue)); | |
406 | cookieValue.clear(); | |
407 | cookieValue.append(_cookieValue); | |
408 | return lRead; | |
409 | } | |
410 | ||
411 | const char * | |
412 | CivetServer::getHeader(struct mg_connection *conn, | |
413 | const std::string &headerName) | |
414 | { | |
415 | return mg_get_header(conn, headerName.c_str()); | |
416 | } | |
417 | ||
418 | void | |
419 | CivetServer::urlDecode(const char *src, | |
420 | std::string &dst, | |
421 | bool is_form_url_encoded) | |
422 | { | |
423 | urlDecode(src, strlen(src), dst, is_form_url_encoded); | |
424 | } | |
425 | ||
426 | void | |
427 | CivetServer::urlDecode(const char *src, | |
428 | size_t src_len, | |
429 | std::string &dst, | |
430 | bool is_form_url_encoded) | |
431 | { | |
432 | int i, j, a, b; | |
433 | #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') | |
434 | ||
435 | dst.clear(); | |
436 | for (i = j = 0; i < (int)src_len; i++, j++) { | |
437 | if (i < (int)src_len - 2 && src[i] == '%' | |
438 | && isxdigit(*(const unsigned char *)(src + i + 1)) | |
439 | && isxdigit(*(const unsigned char *)(src + i + 2))) { | |
440 | a = tolower(*(const unsigned char *)(src + i + 1)); | |
441 | b = tolower(*(const unsigned char *)(src + i + 2)); | |
442 | dst.push_back((char)((HEXTOI(a) << 4) | HEXTOI(b))); | |
443 | i += 2; | |
444 | } else if (is_form_url_encoded && src[i] == '+') { | |
445 | dst.push_back(' '); | |
446 | } else { | |
447 | dst.push_back(src[i]); | |
448 | } | |
449 | } | |
450 | } | |
451 | ||
452 | bool | |
453 | CivetServer::getParam(struct mg_connection *conn, | |
454 | const char *name, | |
455 | std::string &dst, | |
456 | size_t occurrence) | |
457 | { | |
458 | const char *formParams = NULL; | |
459 | const struct mg_request_info *ri = mg_get_request_info(conn); | |
460 | assert(ri != NULL); | |
461 | CivetServer *me = (CivetServer *)(ri->user_data); | |
462 | assert(me != NULL); | |
463 | mg_lock_context(me->context); | |
464 | CivetConnection &conobj = me->connections[conn]; | |
465 | mg_lock_connection(conn); | |
466 | mg_unlock_context(me->context); | |
467 | ||
468 | if (conobj.postData != NULL) { | |
469 | formParams = conobj.postData; | |
470 | } else { | |
471 | const char *con_len_str = mg_get_header(conn, "Content-Length"); | |
472 | if (con_len_str) { | |
473 | unsigned long con_len = atoi(con_len_str); | |
474 | if (con_len > 0) { | |
475 | // Add one extra character: in case the post-data is a text, it | |
476 | // is required as 0-termination. | |
477 | // Do not increment con_len, since the 0 terminating is not part | |
478 | // of the content (text or binary). | |
479 | conobj.postData = (char *)malloc(con_len + 1); | |
480 | if (conobj.postData != NULL) { | |
481 | // malloc may fail for huge requests | |
482 | mg_read(conn, conobj.postData, con_len); | |
483 | conobj.postData[con_len] = 0; | |
484 | formParams = conobj.postData; | |
485 | conobj.postDataLen = con_len; | |
486 | } | |
487 | } | |
488 | } | |
489 | } | |
490 | if (formParams == NULL) { | |
491 | // get requests do store html <form> field values in the http | |
492 | // query_string | |
493 | formParams = ri->query_string; | |
494 | } | |
495 | mg_unlock_connection(conn); | |
496 | ||
497 | if (formParams != NULL) { | |
498 | return getParam(formParams, strlen(formParams), name, dst, occurrence); | |
499 | } | |
500 | ||
501 | return false; | |
502 | } | |
503 | ||
504 | bool | |
505 | CivetServer::getParam(const char *data, | |
506 | size_t data_len, | |
507 | const char *name, | |
508 | std::string &dst, | |
509 | size_t occurrence) | |
510 | { | |
511 | const char *p, *e, *s; | |
512 | size_t name_len; | |
513 | ||
514 | dst.clear(); | |
515 | if (data == NULL || name == NULL || data_len == 0) { | |
516 | return false; | |
517 | } | |
518 | name_len = strlen(name); | |
519 | e = data + data_len; | |
520 | ||
521 | // data is "var1=val1&var2=val2...". Find variable first | |
522 | for (p = data; p + name_len < e; p++) { | |
523 | if ((p == data || p[-1] == '&') && p[name_len] == '=' | |
524 | && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) { | |
525 | ||
526 | // Point p to variable value | |
527 | p += name_len + 1; | |
528 | ||
529 | // Point s to the end of the value | |
530 | s = (const char *)memchr(p, '&', (size_t)(e - p)); | |
531 | if (s == NULL) { | |
532 | s = e; | |
533 | } | |
534 | assert(s >= p); | |
535 | ||
536 | // Decode variable into destination buffer | |
537 | urlDecode(p, (int)(s - p), dst, true); | |
538 | return true; | |
539 | } | |
540 | } | |
541 | return false; | |
542 | } | |
543 | ||
544 | void | |
545 | CivetServer::urlEncode(const char *src, std::string &dst, bool append) | |
546 | { | |
547 | urlEncode(src, strlen(src), dst, append); | |
548 | } | |
549 | ||
550 | void | |
551 | CivetServer::urlEncode(const char *src, | |
552 | size_t src_len, | |
553 | std::string &dst, | |
554 | bool append) | |
555 | { | |
556 | static const char *dont_escape = "._-$,;~()"; | |
557 | static const char *hex = "0123456789abcdef"; | |
558 | ||
559 | if (!append) | |
560 | dst.clear(); | |
561 | ||
562 | for (; src_len > 0; src++, src_len--) { | |
563 | if (isalnum(*(const unsigned char *)src) | |
564 | || strchr(dont_escape, *(const unsigned char *)src) != NULL) { | |
565 | dst.push_back(*src); | |
566 | } else { | |
567 | dst.push_back('%'); | |
568 | dst.push_back(hex[(*(const unsigned char *)src) >> 4]); | |
569 | dst.push_back(hex[(*(const unsigned char *)src) & 0xf]); | |
570 | } | |
571 | } | |
572 | } | |
573 | ||
574 | std::vector<int> | |
575 | CivetServer::getListeningPorts() | |
576 | { | |
577 | std::vector<int> ports(10); | |
578 | std::vector<int> ssl(10); | |
579 | size_t size = mg_get_ports(context, ports.size(), &ports[0], &ssl[0]); | |
580 | ports.resize(size); | |
581 | ssl.resize(size); | |
582 | return ports; | |
583 | } | |
584 | ||
585 | CivetServer::CivetConnection::CivetConnection() | |
586 | { | |
587 | postData = NULL; | |
588 | postDataLen = 0; | |
589 | } | |
590 | ||
591 | CivetServer::CivetConnection::~CivetConnection() | |
592 | { | |
593 | free(postData); | |
594 | } |