1 /*
2 * webu.c
3 *
4 * Webcontrol and Streams for motion.
5 *
6 * This software is distributed under the GNU Public License Version 2
7 * See also the file 'COPYING'.
8 *
9 * Portions of code from Angel Carpintero (motiondevelop@gmail.com)
10 * from webhttpd.c Copyright 2004-2005
11 *
12 * Majority of module written by MrDave.
13 *
14 * Function naming scheme:
15 * webu* - All functions in this module have this prefix.
16 * webu_start - Entry point to start the daemon.
17 * webu_stop - Entry point to stop the daemon
18 * webu_mhd* - Functions related to libmicrohttd implementation
19 * webu_process_action - Performs most items under the action menu
20 * webu_process_config - Saves the parameter values into Motion.
21 * webu_process_track - Performs the tracking functions.
22 *
23 * Some function names are long and are not expected to contain any
24 * logger message that would display the function name to the user.
25 *
26 * Functions are generally kept to under one page in length
27 *
28 * Known Issues:
29 * The quit/restart uses signals and this should be reconsidered.
30 * The tracking is "best effort" since developer does not have tracking camera.
31 * The conf_cmdparse assumes that the pointers to the motion context for each
32 * camera are always sequential and enforcement of the pointers being sequential
33 * has not been observed in the other modules. (This is a legacy assumption)
34 */
35
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <sys/socket.h>
39
40 #include "motion.h"
41 #include "webu.h"
42 #include "webu_html.h"
43 #include "webu_text.h"
44 #include "webu_stream.h"
45 #include "translate.h"
46
47 /* Context to pass the parms to functions to start mhd */
48 struct mhdstart_ctx {
49 struct context **cnt;
50 char *tls_cert;
51 char *tls_key;
52 int ctrl;
53 int indxthrd;
54 struct MHD_OptionItem *mhd_ops;
55 int mhd_opt_nbr;
56 unsigned int mhd_flags;
57 int ipv6;
58 struct sockaddr_in lpbk_ipv4;
59 struct sockaddr_in6 lpbk_ipv6;
60 };
61
62 #if MHD_VERSION >= 0x00097002
63 typedef enum MHD_Result mymhd_retcd;
64 #else
65 typedef int mymhd_retcd;
66 #endif
67
webu_context_init(struct context ** cntlst,struct context * cnt,struct webui_ctx * webui)68 static void webu_context_init(struct context **cntlst, struct context *cnt, struct webui_ctx *webui) {
69
70 int indx;
71
72 webui->url = mymalloc(WEBUI_LEN_URLI);
73 webui->uri_camid = mymalloc(WEBUI_LEN_PARM);
74 webui->uri_cmd1 = mymalloc(WEBUI_LEN_PARM);
75 webui->uri_cmd2 = mymalloc(WEBUI_LEN_PARM);
76 webui->uri_parm1 = mymalloc(WEBUI_LEN_PARM);
77 webui->uri_value1 = mymalloc(WEBUI_LEN_PARM);
78 webui->uri_parm2 = mymalloc(WEBUI_LEN_PARM);
79 webui->uri_value2 = mymalloc(WEBUI_LEN_PARM);
80 webui->clientip = mymalloc(WEBUI_LEN_URLI);
81 webui->hostname = mymalloc(WEBUI_LEN_PARM);
82 webui->auth_denied = mymalloc(WEBUI_LEN_RESP);
83 webui->auth_opaque = mymalloc(WEBUI_LEN_PARM);
84 webui->auth_realm = mymalloc(WEBUI_LEN_PARM);
85 webui->text_eol = mymalloc(WEBUI_LEN_PARM);
86 webui->auth_user = NULL; /* Buffer to hold the user name*/
87 webui->auth_pass = NULL; /* Buffer to hold the password */
88 webui->authenticated = FALSE; /* boolean for whether we are authenticated*/
89 webui->lang = mymalloc(3); /* Two digit lang code plus null terminator */
90 webui->lang_full = mymalloc(6); /* lang code, e.g US_en */
91 webui->resp_size = WEBUI_LEN_RESP * 10; /* The size of the resp_page buffer. May get adjusted */
92 webui->resp_used = 0; /* How many bytes used so far in resp_page*/
93 webui->stream_pos = 0; /* Stream position of image being sent */
94 webui->stream_fps = 1; /* Stream rate */
95 webui->resp_page = mymalloc(webui->resp_size); /* The response being constructed */
96 webui->cntlst = cntlst; /* The list of context's for all cameras */
97 webui->cnt = cnt; /* The context pointer for a single camera */
98 webui->cnct_type = WEBUI_CNCT_UNKNOWN;
99
100 /* get the number of cameras and threads */
101 indx = 0;
102 if (webui->cntlst != NULL){
103 while (webui->cntlst[++indx]);
104 }
105 webui->cam_threads = indx;
106
107 webui->cam_count = indx;
108 if (indx > 1)
109 webui->cam_count--;
110
111 /* 1 thread, 1 camera = just motion.conf.
112 * 2 thread, 1 camera, then using motion.conf plus a separate camera file */
113 snprintf(webui->lang_full, 6,"%s", getenv("LANGUAGE"));
114 snprintf(webui->lang, 3,"%s",webui->lang_full);
115
116 memset(webui->hostname,'\0',WEBUI_LEN_PARM);
117 memset(webui->resp_page,'\0',webui->resp_size);
118
119 return;
120 }
121
webu_context_null(struct webui_ctx * webui)122 static void webu_context_null(struct webui_ctx *webui) {
123 /* Null out all the pointers in our webui context */
124 webui->url = NULL;
125 webui->hostname = NULL;
126 webui->uri_camid = NULL;
127 webui->uri_cmd1 = NULL;
128 webui->uri_cmd2 = NULL;
129 webui->uri_parm1 = NULL;
130 webui->uri_value1 = NULL;
131 webui->uri_parm2 = NULL;
132 webui->uri_value2 = NULL;
133 webui->lang = NULL;
134 webui->lang_full = NULL;
135 webui->resp_page = NULL;
136 webui->connection = NULL;
137 webui->auth_user = NULL;
138 webui->auth_pass = NULL;
139 webui->auth_denied = NULL;
140 webui->auth_opaque = NULL;
141 webui->auth_realm = NULL;
142 webui->clientip = NULL;
143 webui->text_eol = NULL;
144
145 return;
146 }
147
webu_context_free(struct webui_ctx * webui)148 static void webu_context_free(struct webui_ctx *webui) {
149
150 if (webui->hostname != NULL) free(webui->hostname);
151 if (webui->url != NULL) free(webui->url);
152 if (webui->uri_camid != NULL) free(webui->uri_camid);
153 if (webui->uri_cmd1 != NULL) free(webui->uri_cmd1);
154 if (webui->uri_cmd2 != NULL) free(webui->uri_cmd2);
155 if (webui->uri_parm1 != NULL) free(webui->uri_parm1);
156 if (webui->uri_value1 != NULL) free(webui->uri_value1);
157 if (webui->uri_parm2 != NULL) free(webui->uri_parm2);
158 if (webui->uri_value2 != NULL) free(webui->uri_value2);
159 if (webui->lang != NULL) free(webui->lang);
160 if (webui->lang_full != NULL) free(webui->lang_full);
161 if (webui->resp_page != NULL) free(webui->resp_page);
162 if (webui->auth_user != NULL) free(webui->auth_user);
163 if (webui->auth_pass != NULL) free(webui->auth_pass);
164 if (webui->auth_denied != NULL) free(webui->auth_denied);
165 if (webui->auth_opaque != NULL) free(webui->auth_opaque);
166 if (webui->auth_realm != NULL) free(webui->auth_realm);
167 if (webui->clientip != NULL) free(webui->clientip);
168 if (webui->text_eol != NULL) free(webui->text_eol);
169
170 webu_context_null(webui);
171
172 free(webui);
173
174 return;
175 }
176
webu_badreq(struct webui_ctx * webui)177 static void webu_badreq(struct webui_ctx *webui){
178 /* This function is used in this webu module as a central function when there is a bad
179 * request. Since sometimes we will be unable to determine what camera context (stream
180 * or camera) originated the request and we have NULL for cntlist and cnt, we default the
181 * response to be HTML. Otherwise, we do know the type and we send back to the user the
182 * bad request response either with or without the HTML tags.
183 */
184 if (webui->cnt != NULL) {
185 if (webui->cnt->conf.webcontrol_interface == 1){
186 webu_text_badreq(webui);
187 } else {
188 webu_html_badreq(webui);
189 }
190 } else if (webui->cntlst != NULL) {
191 if (webui->cntlst[0]->conf.webcontrol_interface == 1){
192 webu_text_badreq(webui);
193 } else {
194 webu_html_badreq(webui);
195 }
196 } else {
197 webu_html_badreq(webui);
198 }
199 }
200
webu_write(struct webui_ctx * webui,const char * buf)201 void webu_write(struct webui_ctx *webui, const char *buf) {
202 /* Copy the buf data to our response buffer. If the response buffer is not large enough to
203 * accept our new data coming in, then expand it in chunks of 10
204 */
205 int resp_len;
206 char *temp_resp;
207 size_t temp_size;
208
209 resp_len = strlen(buf);
210
211 temp_size = webui->resp_size;
212 while ((resp_len + webui->resp_used) > temp_size){
213 temp_size = temp_size + (WEBUI_LEN_RESP * 10);
214 }
215
216 if (temp_size > webui->resp_size){
217 temp_resp = mymalloc(webui->resp_size);
218 memcpy(temp_resp, webui->resp_page, webui->resp_size);
219 free(webui->resp_page);
220 webui->resp_page = mymalloc(temp_size);
221 memset(webui->resp_page,'\0',temp_size);
222 memcpy(webui->resp_page, temp_resp, webui->resp_size);
223 webui->resp_size = temp_size;
224 free(temp_resp);
225 }
226
227 memcpy(webui->resp_page + webui->resp_used, buf, resp_len);
228 webui->resp_used = webui->resp_used + resp_len;
229
230 return;
231 }
232
webu_parms_edit(struct webui_ctx * webui)233 static void webu_parms_edit(struct webui_ctx *webui) {
234
235 /* Determine the thread number provided.
236 * If no thread provided, assign it to -1
237 * Samples:
238 * http://localhost:8081/0/stream (cntlist will be populated and this function will set cnt)
239 * http://localhost:8081/stream (cntlist will be null, cnt will be populated)
240 * http://localhost:8081/ (cntlist will be null, cnt will be populated)
241 */
242 int indx, is_nbr;
243
244 if (strlen(webui->uri_camid) > 0){
245 is_nbr = TRUE;
246 for (indx=0; indx < (int)strlen(webui->uri_camid);indx++){
247 if ((webui->uri_camid[indx] > '9') || (webui->uri_camid[indx] < '0')) is_nbr = FALSE;
248 }
249 if (is_nbr){
250 webui->thread_nbr = atoi(webui->uri_camid);
251 } else {
252 webui->thread_nbr = -1;
253 }
254 } else {
255 webui->thread_nbr = -1;
256 }
257
258 /* Set the single context pointer to thread we are answering
259 * If the connection is for a single stream (legacy method of a port
260 * per stream), then the cntlist will be null and the camera context
261 * will already be assigned into webui->cnt. This is part of the
262 * init function which is called for MHD and it has the different
263 * variations depending upon how the port and cameras were specified.
264 * Also set/convert the camid into the thread number.
265 */
266
267 if (webui->cntlst != NULL){
268 if (webui->thread_nbr < 0){
269 webui->cnt = webui->cntlst[0];
270 webui->thread_nbr = 0;
271 } else {
272 indx = 0;
273 while (webui->cntlst[indx] != NULL){
274 if (webui->cntlst[indx]->camera_id == webui->thread_nbr){
275 webui->thread_nbr = indx;
276 break;
277 }
278 indx++;
279 }
280 /* This may be null, in which case we will not answer the request */
281 webui->cnt = webui->cntlst[indx];
282 }
283 }
284 }
285
webu_parseurl_parms(struct webui_ctx * webui,char * st_pos)286 static void webu_parseurl_parms(struct webui_ctx *webui, char *st_pos) {
287
288 /* Parse the parameters of the URI
289 * Earlier functions have assigned the st_pos to the slash after the action and it is
290 * pointing at the set/get when this function is invoked.
291 * Samples (MHD takes off the IP:port)
292 * /{camid}/config/set?{parm}={value1}
293 * /{camid}/config/get?query={parm}
294 * /{camid}/track/set?x={value1}&y={value2}
295 * /{camid}/track/set?pan={value1}&tilt={value2}
296 * /{camid}/{cmd1}/{cmd2}?{parm1}={value1}&{parm2}={value2}
297 */
298
299 int parm_len, last_parm;
300 char *en_pos;
301
302
303 /* First parse out the "set","get","pan","tilt","x","y"
304 * from the uri and put them into the cmd2.
305 * st_pos is at the beginning of the command
306 * If there is no ? then we are done parsing
307 * Note that each section is looking for a different
308 * delimitter. (?, =, &, =, &)
309 */
310 last_parm = FALSE;
311 en_pos = strstr(st_pos,"?");
312 if (en_pos != NULL){
313 parm_len = en_pos - st_pos + 1;
314 if (parm_len >= WEBUI_LEN_PARM) return;
315 snprintf(webui->uri_cmd2, parm_len,"%s", st_pos);
316
317 /* Get the parameter name */
318 st_pos = st_pos + parm_len; /* Move past the command */
319 en_pos = strstr(st_pos,"=");
320 if (en_pos == NULL){
321 parm_len = strlen(webui->url) - parm_len;
322 last_parm = TRUE;
323 } else {
324 parm_len = en_pos - st_pos + 1;
325 }
326 if (parm_len >= WEBUI_LEN_PARM) return;
327 snprintf(webui->uri_parm1, parm_len,"%s", st_pos);
328
329 if (!last_parm){
330 /* Get the parameter value */
331 st_pos = st_pos + parm_len; /* Move past the equals sign */
332 if (!strcasecmp(webui->uri_parm1,"x") || !strcasecmp(webui->uri_parm1,"pan") ){
333 en_pos = strstr(st_pos,"&");
334 } else {
335 en_pos = NULL;
336 }
337 if (en_pos == NULL){
338 parm_len = strlen(webui->url) - parm_len;
339 last_parm = TRUE;
340 } else {
341 parm_len = en_pos - st_pos + 1;
342 }
343 if (parm_len >= WEBUI_LEN_PARM) return;
344 snprintf(webui->uri_value1, parm_len,"%s", st_pos);
345 }
346
347 if (!last_parm){
348 /* Get the next parameter name */
349 st_pos = st_pos + parm_len; /* Move past the previous command */
350 en_pos = strstr(st_pos,"=");
351 if (en_pos == NULL){
352 parm_len = strlen(webui->url) - parm_len;
353 last_parm = TRUE;
354 } else {
355 parm_len = en_pos - st_pos + 1;
356 }
357 if (parm_len >= WEBUI_LEN_PARM) return;
358 snprintf(webui->uri_parm2, parm_len,"%s", st_pos);
359 }
360
361 if (!last_parm){
362 /* Get the next parameter value */
363 st_pos = st_pos + parm_len; /* Move past the equals sign */
364 en_pos = strstr(st_pos,"&");
365 if (en_pos == NULL){
366 parm_len = strlen(webui->url) - parm_len;
367 last_parm = TRUE;
368 } else {
369 parm_len = en_pos - st_pos + 1;
370 }
371 if (parm_len >= WEBUI_LEN_PARM) return;
372 snprintf(webui->uri_value2, parm_len,"%s", st_pos);
373 }
374
375 }
376
377 }
378
webu_parseurl_reset(struct webui_ctx * webui)379 static void webu_parseurl_reset(struct webui_ctx *webui) {
380
381 /* Reset the variables to empty strings*/
382
383 memset(webui->uri_camid,'\0',WEBUI_LEN_PARM);
384 memset(webui->uri_cmd1,'\0',WEBUI_LEN_PARM);
385 memset(webui->uri_cmd2,'\0',WEBUI_LEN_PARM);
386 memset(webui->uri_parm1,'\0',WEBUI_LEN_PARM);
387 memset(webui->uri_value1,'\0',WEBUI_LEN_PARM);
388 memset(webui->uri_parm2,'\0',WEBUI_LEN_PARM);
389 memset(webui->uri_value2,'\0',WEBUI_LEN_PARM);
390
391 }
392
webu_parseurl(struct webui_ctx * webui)393 static int webu_parseurl(struct webui_ctx *webui) {
394 /* Parse the sent URI into the commands and parameters
395 * so we can check the resulting strings in later functions
396 * and determine what actions to take.
397 * Samples
398 * /
399 * /{camid}
400 * /{camid}/config/set?log_level=6
401 * /{camid}/config/set?{parm}={value1}
402 * /{camid}/config/get?query={parm}
403 * /{camid}/track/set?x={value1}&y={value2}
404 * /{camid}/track/set?pan={value1}&tilt={value2}
405 * /{camid}/{cmd1}/{cmd2}?{parm1}={value1}&{parm2}={value2}
406 */
407
408 int retcd, parm_len, last_slash;
409 char *st_pos, *en_pos;
410
411 retcd = 0;
412
413 MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO, _("Sent url: %s"),webui->url);
414
415 webu_parseurl_reset(webui);
416
417 if (strlen(webui->url) == 0) return -1;
418
419 MHD_http_unescape(webui->url);
420
421 MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO, _("Decoded url: %s"),webui->url);
422
423 /* Home page */
424 if (strlen(webui->url) == 1) return 0;
425
426 last_slash = 0;
427
428 /* Get the camid number and which sometimes this will contain an action if the user
429 * is setting the port for a particular camera and requests the
430 * stream by using http://localhost:port/stream
431 */
432 st_pos = webui->url + 1; /* Move past the first "/" */
433 if (*st_pos == '-') return -1; /* Never allow a negative number */
434 en_pos = strstr(st_pos,"/");
435 if (en_pos == NULL){
436 parm_len = strlen(webui->url);
437 last_slash = 1;
438 } else {
439 parm_len = en_pos - st_pos + 1;
440 }
441 if (parm_len >= WEBUI_LEN_PARM) return -1; /* var was malloc'd to WEBUI_LEN_PARM */
442 snprintf(webui->uri_camid, parm_len,"%s", st_pos);
443
444 if (!last_slash){
445 /* Get cmd1 or action */
446 st_pos = st_pos + parm_len; /* Move past the camid */
447 en_pos = strstr(st_pos,"/");
448 if (en_pos == NULL){
449 parm_len = strlen(webui->url) - parm_len ;
450 last_slash = 1;
451 } else {
452 parm_len = en_pos - st_pos + 1;
453 }
454 if (parm_len >= WEBUI_LEN_PARM) return -1; /* var was malloc'd to WEBUI_LEN_PARM */
455 snprintf(webui->uri_cmd1, parm_len,"%s", st_pos);
456 }
457
458 if (!last_slash){
459 /* Get cmd2 or action */
460 st_pos = st_pos + parm_len; /* Move past the first command */
461 en_pos = strstr(st_pos,"/");
462 if (en_pos == NULL){
463 parm_len = strlen(webui->url) - parm_len;
464 last_slash = 1;
465 } else {
466 parm_len = en_pos - st_pos + 1;
467 }
468 if (parm_len >= WEBUI_LEN_PARM) return -1; /* var was malloc'd to WEBUI_LEN_PARM */
469 snprintf(webui->uri_cmd2, parm_len,"%s", st_pos);
470 }
471
472 if ((!strcmp(webui->uri_cmd1,"config") ||
473 !strcmp(webui->uri_cmd1,"track") ) &&
474 (strlen(webui->uri_cmd2) > 0)) {
475 webu_parseurl_parms(webui, st_pos);
476 }
477
478 MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO,
479 "camid: >%s< cmd1: >%s< cmd2: >%s< parm1:>%s< val1:>%s< parm2:>%s< val2:>%s<"
480 ,webui->uri_camid
481 ,webui->uri_cmd1, webui->uri_cmd2
482 ,webui->uri_parm1, webui->uri_value1
483 ,webui->uri_parm2, webui->uri_value2);
484
485
486 return retcd;
487
488 }
489
webu_process_action(struct webui_ctx * webui)490 void webu_process_action(struct webui_ctx *webui) {
491 /* Process the actions from the webcontrol that the user requested. This is used
492 * for both the html and text interface. The text interface just adds a additional
493 * response whereas the html just performs the action
494 */
495 int indx;
496
497 indx = 0;
498 if ((strcmp(webui->uri_cmd2,"makemovie") == 0) ||
499 (strcmp(webui->uri_cmd2,"eventend") == 0)) {
500 if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
501 while (webui->cntlst[++indx]){
502 webui->cntlst[indx]->event_stop = TRUE;
503 }
504 } else {
505 webui->cnt->event_stop = TRUE;
506 }
507
508 } else if (strcmp(webui->uri_cmd2,"eventstart") == 0){
509 if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
510 while (webui->cntlst[++indx]){
511 webui->cntlst[indx]->event_user = TRUE;
512 }
513 } else {
514 webui->cnt->event_user = TRUE;
515 }
516
517 } else if (!strcmp(webui->uri_cmd2,"snapshot")){
518 if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
519 while (webui->cntlst[++indx])
520 webui->cntlst[indx]->snapshot = 1;
521 } else {
522 webui->cnt->snapshot = 1;
523 }
524
525
526 } else if (!strcmp(webui->uri_cmd2,"restart")){
527 if (webui->thread_nbr == 0) {
528 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, _("Restarting all threads"));
529 webui->cntlst[0]->webcontrol_finish = TRUE;
530 kill(getpid(),SIGHUP);
531 } else {
532 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO,
533 _("Restarting thread %d"),webui->thread_nbr);
534 webui->cnt->restart = TRUE;
535 if (webui->cnt->running) {
536 webui->cnt->event_stop = TRUE;
537 webui->cnt->finish = TRUE;
538 }
539
540 }
541
542 } else if (!strcmp(webui->uri_cmd2,"quit")){
543 if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
544 while (webui->cntlst[++indx]){
545 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO,
546 _("Quitting thread %d"),webui->thread_nbr);
547 webui->cntlst[indx]->restart = FALSE;
548 webui->cntlst[indx]->event_stop = TRUE;
549 webui->cntlst[indx]->event_user = TRUE;
550 webui->cntlst[indx]->finish = TRUE;
551 }
552 } else {
553 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO,
554 _("Quitting thread %d"),webui->thread_nbr);
555 webui->cnt->restart = FALSE;
556 webui->cnt->event_stop = TRUE;
557 webui->cnt->event_user = TRUE;
558 webui->cnt->finish = TRUE;
559 }
560
561 } else if (!strcmp(webui->uri_cmd2,"end")){
562 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, _("Motion terminating"));
563 while (webui->cntlst[indx]){
564 webui->cntlst[indx]->webcontrol_finish = TRUE;
565 webui->cntlst[indx]->restart = FALSE;
566 webui->cntlst[indx]->event_stop = TRUE;
567 webui->cntlst[indx]->event_user = TRUE;
568 webui->cntlst[indx]->finish = TRUE;
569 indx++;
570 }
571
572
573 } else if (!strcmp(webui->uri_cmd2,"start")){
574 if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
575 do {
576 webui->cntlst[indx]->pause = 0;
577 } while (webui->cntlst[++indx]);
578 } else {
579 webui->cnt->pause = 0;
580 }
581 } else if (!strcmp(webui->uri_cmd2,"pause")){
582 if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
583 do {
584 webui->cntlst[indx]->pause = 1;
585 } while (webui->cntlst[++indx]);
586 } else {
587 webui->cnt->pause = 1;
588 }
589
590 } else if (!strcmp(webui->uri_cmd2,"connection")){
591 webu_text_connection(webui);
592
593 } else if (!strcmp(webui->uri_cmd2,"status")){
594 webu_text_status(webui);
595
596 } else if ((!strcmp(webui->uri_cmd2,"write")) ||
597 (!strcmp(webui->uri_cmd2,"writeyes"))){
598 conf_print(webui->cntlst);
599
600 } else {
601 MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO,
602 _("Invalid action requested: >%s< >%s< >%s<")
603 , webui->uri_camid, webui->uri_cmd1, webui->uri_cmd2);
604 return;
605 }
606 }
607
webu_process_config_set(struct webui_ctx * webui)608 static int webu_process_config_set(struct webui_ctx *webui) {
609 /* Process the request to change the configuration parameters. Used
610 * both the html and text interfaces. If the parameter was found, then
611 * we return 0 otherwise a -1 to tell the calling function whether it
612 * was a valid parm to change.
613 */
614 int indx, retcd;
615 char temp_name[WEBUI_LEN_PARM];
616
617 /* Search through the depreciated parms and if applicable,
618 * get the new parameter name so we can check its webcontrol_parms level
619 */
620 snprintf(temp_name, WEBUI_LEN_PARM, "%s", webui->uri_parm1);
621 indx=0;
622 while (dep_config_params[indx].name != NULL) {
623 if (strcmp(dep_config_params[indx].name,webui->uri_parm1) == 0){
624 snprintf(temp_name, WEBUI_LEN_PARM, "%s", dep_config_params[indx].newname);
625 break;
626 }
627 indx++;
628 }
629 /* Ignore any request to change an option that is designated above the
630 * webcontrol_parms level.
631 */
632 indx=0;
633 while (config_params[indx].param_name != NULL) {
634 if (((webui->thread_nbr != 0) && (config_params[indx].main_thread)) ||
635 (config_params[indx].webui_level > webui->cntlst[0]->conf.webcontrol_parms) ||
636 (config_params[indx].webui_level == WEBUI_LEVEL_NEVER) ) {
637 indx++;
638 continue;
639 }
640 if (!strcmp(temp_name, config_params[indx].param_name)) break;
641 indx++;
642 }
643 /* If we found the parm, assign it. If the loop above did not find the parm
644 * then we ignore the request
645 */
646 if (config_params[indx].param_name != NULL){
647 if (strlen(webui->uri_parm1) > 0){
648 /* This is legacy assumption on the pointers being sequential
649 * We send in the original parm name so it will trigger the depreciated warnings
650 * and perform any required transformations from old parm to new parm
651 */
652 conf_cmdparse(webui->cntlst + webui->thread_nbr
653 , webui->uri_parm1, webui->uri_value1);
654
655 /*If we are updating vid parms, set the flag to update the device.*/
656 if (!strcmp(config_params[indx].param_name, "vid_control_params") &&
657 (webui->cntlst[webui->thread_nbr]->vdev != NULL)){
658 webui->cntlst[webui->thread_nbr]->vdev->update_parms = TRUE;
659 }
660
661 /* If changing language, do it now */
662 if (!strcmp(config_params[indx].param_name, "native_language")){
663 nls_enabled = webui->cntlst[webui->thread_nbr]->conf.native_language;
664 if (nls_enabled){
665 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO,_("Native Language : on"));
666 } else {
667 MOTION_LOG(INF, TYPE_ALL, NO_ERRNO,_("Native Language : off"));
668 }
669 }
670 } else {
671 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO,_("Set the value to null/zero"));
672 }
673 retcd = 0;
674 } else {
675 retcd = -1;
676 }
677
678 return retcd;
679
680 }
681
webu_process_config(struct webui_ctx * webui)682 int webu_process_config(struct webui_ctx *webui) {
683
684 int retcd;
685
686 retcd = 0;
687
688 if ((!strcmp(webui->uri_cmd1,"config")) &&
689 (!strcmp(webui->uri_cmd2,"set"))) {
690 retcd = webu_process_config_set(webui);
691
692 } else if ((!strcmp(webui->uri_cmd1,"config")) &&
693 (!strcmp(webui->uri_cmd2,"get"))) {
694 webu_text_get_query(webui);
695
696 } else if ((!strcmp(webui->uri_cmd1,"config")) &&
697 (!strcmp(webui->uri_cmd2,"list"))) {
698 webu_text_list(webui);
699
700 } else {
701 MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO,
702 _("Invalid action requested: >%s< >%s< >%s<")
703 , webui->uri_camid, webui->uri_cmd1, webui->uri_cmd2);
704
705 }
706
707 return retcd;
708
709 }
710
webu_process_track(struct webui_ctx * webui)711 int webu_process_track(struct webui_ctx *webui) {
712 /* Call the tracking move functions as requested */
713 struct coord cent;
714 int retcd;
715
716 if (!strcmp(webui->uri_cmd2, "center")) {
717 webui->cntlst[webui->thread_nbr]->moved = track_center(webui->cntlst[webui->thread_nbr], 0, 1, 0, 0);
718 retcd = 0;
719 } else if (!strcmp(webui->uri_cmd2, "set")) {
720 if (!strcmp(webui->uri_parm1, "pan")) {
721 cent.width = webui->cntlst[webui->thread_nbr]->imgs.width;
722 cent.height = webui->cntlst[webui->thread_nbr]->imgs.height;
723 cent.x = atoi(webui->uri_value1);
724 cent.y = 0;
725 webui->cntlst[webui->thread_nbr]->moved = track_move(webui->cntlst[webui->thread_nbr]
726 ,webui->cntlst[webui->thread_nbr]->video_dev
727 ,¢, &webui->cntlst[webui->thread_nbr]->imgs, 1);
728
729 cent.width = webui->cntlst[webui->thread_nbr]->imgs.width;
730 cent.height = webui->cntlst[webui->thread_nbr]->imgs.height;
731 cent.x = 0;
732 cent.y = atoi(webui->uri_value2);
733 webui->cntlst[webui->thread_nbr]->moved = track_move(webui->cntlst[webui->thread_nbr]
734 ,webui->cntlst[webui->thread_nbr]->video_dev
735 ,¢, &webui->cntlst[webui->thread_nbr]->imgs, 1);
736 retcd = 0;
737 } else if (!strcasecmp(webui->uri_parm1, "x")) {
738 webui->cntlst[webui->thread_nbr]->moved = track_center(webui->cntlst[webui->thread_nbr]
739 , webui->cntlst[webui->thread_nbr]->video_dev, 1
740 , atoi(webui->uri_value1), atoi(webui->uri_value2));
741 retcd = 0;
742 } else {
743 retcd = -1;
744 }
745 } else {
746 retcd = -1;
747 }
748
749 return retcd;
750
751 }
752
webu_clientip(struct webui_ctx * webui)753 static void webu_clientip(struct webui_ctx *webui) {
754 /* Extract the IP of the client that is connecting. When the
755 * user specifies Motion to use IPV6 and a IPV4 address comes to us
756 * the IPv4 address is prepended with a ::ffff: We then trim that off
757 * so we don't confuse our users.
758 */
759 const union MHD_ConnectionInfo *con_info;
760 char client[WEBUI_LEN_URLI];
761 const char *ip_dst;
762 struct sockaddr_in6 *con_socket6;
763 struct sockaddr_in *con_socket4;
764 int is_ipv6;
765
766 is_ipv6 = FALSE;
767 if (webui->cnt != NULL ){
768 if (webui->cnt->conf.webcontrol_ipv6) is_ipv6 = TRUE;
769 } else {
770 if (webui->cntlst[0]->conf.webcontrol_ipv6) is_ipv6 = TRUE;
771 }
772
773 con_info = MHD_get_connection_info(webui->connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
774 if (is_ipv6){
775 con_socket6 = (struct sockaddr_in6 *)con_info->client_addr;
776 ip_dst = inet_ntop(AF_INET6, &con_socket6->sin6_addr, client, WEBUI_LEN_URLI);
777 if (ip_dst == NULL){
778 snprintf(webui->clientip, WEBUI_LEN_URLI, "%s", "Unknown");
779 } else {
780 if (strncmp(client,"::ffff:",7) == 0){
781 snprintf(webui->clientip, WEBUI_LEN_URLI, "%s", client + 7);
782 } else {
783 snprintf(webui->clientip, WEBUI_LEN_URLI, "%s", client);
784 }
785 }
786 } else {
787 con_socket4 = (struct sockaddr_in *)con_info->client_addr;
788 ip_dst = inet_ntop(AF_INET, &con_socket4->sin_addr, client, WEBUI_LEN_URLI);
789 if (ip_dst == NULL){
790 snprintf(webui->clientip, WEBUI_LEN_URLI, "%s", "Unknown");
791 } else {
792 snprintf(webui->clientip,WEBUI_LEN_URLI,"%s",client);
793 }
794 }
795 MOTION_LOG(INF,TYPE_ALL, NO_ERRNO, _("Connection from: %s"),webui->clientip);
796
797 }
798
webu_hostname(struct webui_ctx * webui,int ctrl)799 static void webu_hostname(struct webui_ctx *webui, int ctrl) {
800
801 /* use the hostname the browser used to connect to us when
802 * constructing links to the stream ports. If available
803 * (which it is in all modern browsers) it is more likely to
804 * work than the result of gethostname(), which is reliant on
805 * the machine we're running on having it's hostname setup
806 * correctly and corresponding DNS in place. */
807
808 const char *hdr;
809 char *en_pos;
810 int host_len;
811
812 hdr = MHD_lookup_connection_value (webui->connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST);
813 if (hdr != NULL){
814 snprintf(webui->hostname, WEBUI_LEN_PARM, "%s", hdr);
815 /* IPv6 addresses have :'s in them so special case them */
816 if (webui->hostname[0] == '['){
817 en_pos = strstr(webui->hostname, "]");
818 if (en_pos != NULL){
819 host_len = en_pos - webui->hostname + 2;
820 snprintf(webui->hostname, host_len, "%s", hdr);
821 }
822 } else {
823 en_pos = strstr(webui->hostname, ":");
824 if (en_pos != NULL){
825 host_len = en_pos - webui->hostname + 1;
826 snprintf(webui->hostname, host_len, "%s", hdr);
827 }
828 }
829 } else {
830 gethostname(webui->hostname, WEBUI_LEN_PARM - 1);
831 }
832
833 /* Assign the type of protocol that is associated with the host
834 * so we can use this protocol as we are building the html page or
835 * streams.
836 */
837 if (ctrl){
838 if (webui->cnt->conf.webcontrol_tls){
839 snprintf(webui->hostproto,6,"%s","https");
840 } else {
841 snprintf(webui->hostproto,6,"%s","http");
842 }
843 } else {
844 if (webui->cnt->conf.stream_tls){
845 snprintf(webui->hostproto,6,"%s","https");
846 } else {
847 snprintf(webui->hostproto,6,"%s","http");
848 }
849 }
850
851 return;
852 }
853
webu_mhd_digest_fail(struct webui_ctx * webui,int signal_stale)854 static int webu_mhd_digest_fail(struct webui_ctx *webui,int signal_stale) {
855 /* Create a denied response to user*/
856 struct MHD_Response *response;
857 int retcd;
858
859 webui->authenticated = FALSE;
860
861 response = MHD_create_response_from_buffer(strlen(webui->auth_denied)
862 ,(void *)webui->auth_denied, MHD_RESPMEM_PERSISTENT);
863
864 if (response == NULL) return MHD_NO;
865
866 retcd = MHD_queue_auth_fail_response(webui->connection, webui->auth_realm
867 ,webui->auth_opaque, response
868 ,(signal_stale == MHD_INVALID_NONCE) ? MHD_YES : MHD_NO);
869
870 MHD_destroy_response(response);
871
872 return retcd;
873 }
874
webu_mhd_digest(struct webui_ctx * webui)875 static int webu_mhd_digest(struct webui_ctx *webui) {
876 /* Perform the digest authentication. This function gets called a couple of
877 * times by MHD during the authentication process.
878 */
879 int retcd;
880 char *user;
881
882 /*Get username or prompt for a user/pass */
883 user = MHD_digest_auth_get_username(webui->connection);
884 if (user == NULL) {
885 return webu_mhd_digest_fail(webui, MHD_NO);
886 }
887
888 /* Check for valid user name */
889 if (strcmp(user, webui->auth_user) != 0){
890 MOTION_LOG(ALR, TYPE_STREAM, NO_ERRNO
891 ,_("Failed authentication from %s"), webui->clientip);
892 if (user != NULL) free(user);
893 return webu_mhd_digest_fail(webui, MHD_NO);
894 }
895 if (user != NULL) free(user);
896
897 /* Check the password as well*/
898 retcd = MHD_digest_auth_check(webui->connection, webui->auth_realm
899 , webui->auth_user, webui->auth_pass, 300);
900
901 if (retcd == MHD_NO) {
902 MOTION_LOG(ALR, TYPE_STREAM, NO_ERRNO
903 ,_("Failed authentication from %s"), webui->clientip);
904 }
905
906 if ( (retcd == MHD_INVALID_NONCE) || (retcd == MHD_NO) ) {
907 return webu_mhd_digest_fail(webui, retcd);
908 }
909
910 webui->authenticated = TRUE;
911 return MHD_YES;
912
913 }
914
webu_mhd_basic_fail(struct webui_ctx * webui)915 static int webu_mhd_basic_fail(struct webui_ctx *webui) {
916 /* Create a denied response to user*/
917 struct MHD_Response *response;
918 int retcd;
919
920 webui->authenticated = FALSE;
921
922 response = MHD_create_response_from_buffer(strlen(webui->auth_denied)
923 ,(void *)webui->auth_denied, MHD_RESPMEM_PERSISTENT);
924
925 if (response == NULL) return MHD_NO;
926
927 retcd = MHD_queue_basic_auth_fail_response (webui->connection, webui->auth_realm, response);
928
929 MHD_destroy_response(response);
930
931 return retcd;
932
933 }
934
webu_mhd_basic(struct webui_ctx * webui)935 static int webu_mhd_basic(struct webui_ctx *webui) {
936 /* Perform Basic Authentication. */
937 char *user, *pass;
938
939 pass = NULL;
940 user = NULL;
941
942 user = MHD_basic_auth_get_username_password (webui->connection, &pass);
943 if ((user == NULL) || (pass == NULL)){
944 if (user != NULL) free(user);
945 if (pass != NULL) free(pass);
946 return webu_mhd_basic_fail(webui);
947 }
948
949 if ((strcmp(user, webui->auth_user) != 0) || (strcmp(pass, webui->auth_pass) != 0)) {
950 MOTION_LOG(ALR, TYPE_STREAM, NO_ERRNO
951 ,_("Failed authentication from %s"),webui->clientip);
952 if (user != NULL) free(user);
953 if (pass != NULL) free(pass);
954
955 return webu_mhd_basic_fail(webui);
956 }
957
958 if (user != NULL) free(user);
959 if (pass != NULL) free(pass);
960
961 webui->authenticated = TRUE;
962 return MHD_YES;
963
964 }
965
webu_mhd_auth_parse(struct webui_ctx * webui,int ctrl)966 static void webu_mhd_auth_parse(struct webui_ctx *webui, int ctrl){
967 int auth_len;
968 char *col_pos;
969
970 /* Parse apart the user:pass provided*/
971 if (webui->auth_user != NULL) free(webui->auth_user);
972 if (webui->auth_pass != NULL) free(webui->auth_pass);
973 webui->auth_user = NULL;
974 webui->auth_pass = NULL;
975
976 if (ctrl){
977 auth_len = strlen(webui->cnt->conf.webcontrol_authentication);
978 col_pos = strstr(webui->cnt->conf.webcontrol_authentication,":");
979 if (col_pos == NULL){
980 webui->auth_user = mymalloc(auth_len+1);
981 webui->auth_pass = mymalloc(2);
982 snprintf(webui->auth_user, auth_len + 1, "%s"
983 ,webui->cnt->conf.webcontrol_authentication);
984 snprintf(webui->auth_pass, 2, "%s","");
985 } else {
986 webui->auth_user = mymalloc(auth_len - strlen(col_pos) + 1);
987 webui->auth_pass = mymalloc(strlen(col_pos));
988 snprintf(webui->auth_user, auth_len - strlen(col_pos) + 1, "%s"
989 ,webui->cnt->conf.webcontrol_authentication);
990 snprintf(webui->auth_pass, strlen(col_pos), "%s", col_pos + 1);
991 }
992 } else {
993 auth_len = strlen(webui->cnt->conf.stream_authentication);
994 col_pos = strstr(webui->cnt->conf.stream_authentication,":");
995 if (col_pos == NULL){
996 webui->auth_user = mymalloc(auth_len+1);
997 webui->auth_pass = mymalloc(2);
998 snprintf(webui->auth_user, auth_len + 1, "%s"
999 ,webui->cnt->conf.stream_authentication);
1000 snprintf(webui->auth_pass, 2, "%s","");
1001 } else {
1002 webui->auth_user = mymalloc(auth_len - strlen(col_pos) + 1);
1003 webui->auth_pass = mymalloc(strlen(col_pos));
1004 snprintf(webui->auth_user, auth_len - strlen(col_pos) + 1, "%s"
1005 ,webui->cnt->conf.stream_authentication);
1006 snprintf(webui->auth_pass, strlen(col_pos), "%s", col_pos + 1);
1007 }
1008 }
1009
1010 }
1011
webu_mhd_auth(struct webui_ctx * webui,int ctrl)1012 static int webu_mhd_auth(struct webui_ctx *webui, int ctrl){
1013
1014 /* Set everything up for calling the authentication functions */
1015 unsigned int rand1,rand2;
1016
1017 snprintf(webui->auth_denied, WEBUI_LEN_RESP, "%s"
1018 ,"<html><head><title>Access denied</title>"
1019 "</head><body>Access denied</body></html>");
1020
1021 srand(time(NULL));
1022 rand1 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
1023 rand2 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
1024 snprintf(webui->auth_opaque, WEBUI_LEN_PARM, "%08x%08x", rand1, rand2);
1025
1026 snprintf(webui->auth_realm, WEBUI_LEN_PARM, "%s","Motion");
1027
1028 if (ctrl){
1029 /* Authentication for the webcontrol*/
1030 if (webui->cnt->conf.webcontrol_authentication == NULL){
1031 webui->authenticated = TRUE;
1032 if (webui->cnt->conf.webcontrol_auth_method != 0){
1033 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("No webcontrol user:pass provided"));
1034 }
1035 return MHD_YES;
1036 }
1037
1038 if (webui->auth_user == NULL) webu_mhd_auth_parse(webui, ctrl);
1039
1040 if (webui->cnt->conf.webcontrol_auth_method == 1){
1041 return webu_mhd_basic(webui);
1042 } else if (webui->cnt->conf.webcontrol_auth_method == 2){
1043 return webu_mhd_digest(webui);
1044 }
1045
1046 } else {
1047 /* Authentication for the streams */
1048 if (webui->cnt->conf.stream_authentication == NULL){
1049 webui->authenticated = TRUE;
1050 if (webui->cnt->conf.stream_auth_method != 0){
1051 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("No stream user:pass provided"));
1052 }
1053 return MHD_YES;
1054 }
1055
1056 if (webui->auth_user == NULL) webu_mhd_auth_parse(webui, ctrl);
1057
1058 if (webui->cnt->conf.stream_auth_method == 1) {
1059 return webu_mhd_basic(webui);
1060 } else if (webui->cnt->conf.stream_auth_method == 2){
1061 return webu_mhd_digest(webui);
1062 }
1063 }
1064
1065 webui->authenticated = TRUE;
1066 return MHD_YES;
1067
1068 }
1069
webu_mhd_send(struct webui_ctx * webui,int ctrl)1070 static int webu_mhd_send(struct webui_ctx *webui, int ctrl) {
1071 /* Send the response that we created back to the user. Now if the user
1072 * provided a really bad URL, then we couldn't determine which Motion context
1073 * they were wanting. In this situation, we have a webui->cnt = NULL and we
1074 * don't know whether it came from a html or text request. In this situation
1075 * we use the MHD defaults and skip adding CORS/Content type. (There isn't any
1076 * Motion context so we can't tell where to look)
1077 * The ctrl parameter is a boolean which just says whether the request is for
1078 * the webcontrol versus stream
1079 */
1080 int retcd;
1081 struct MHD_Response *response;
1082
1083 response = MHD_create_response_from_buffer (strlen(webui->resp_page)
1084 ,(void *)webui->resp_page, MHD_RESPMEM_PERSISTENT);
1085 if (!response){
1086 MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO, _("Invalid response"));
1087 return MHD_NO;
1088 }
1089
1090 if (webui->cnt != NULL){
1091 if (ctrl){
1092 if (webui->cnt->conf.webcontrol_cors_header != NULL){
1093 MHD_add_response_header (response, MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
1094 , webui->cnt->conf.webcontrol_cors_header);
1095 }
1096 if (webui->cnt->conf.webcontrol_interface == 1){
1097 MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain;");
1098 } else {
1099 MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");
1100 }
1101 } else {
1102 if (webui->cnt->conf.stream_cors_header != NULL){
1103 MHD_add_response_header (response, MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
1104 , webui->cnt->conf.stream_cors_header);
1105 }
1106 MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");
1107 }
1108 }
1109
1110 retcd = MHD_queue_response (webui->connection, MHD_HTTP_OK, response);
1111 MHD_destroy_response (response);
1112
1113 return retcd;
1114 }
1115
webu_answer_strm_type(struct webui_ctx * webui)1116 static void webu_answer_strm_type(struct webui_ctx *webui) {
1117 /* Assign the type of stream that is being answered*/
1118
1119 if ((strcmp(webui->uri_cmd1,"stream") == 0) ||
1120 (strcmp(webui->uri_camid,"stream") == 0) ||
1121 (strlen(webui->uri_camid) == 0)) {
1122 webui->cnct_type = WEBUI_CNCT_FULL;
1123
1124 } else if ((strcmp(webui->uri_cmd1,"substream") == 0) ||
1125 (strcmp(webui->uri_camid,"substream") == 0)){
1126 webui->cnct_type = WEBUI_CNCT_SUB;
1127
1128 } else if ((strcmp(webui->uri_cmd1,"motion") == 0) ||
1129 (strcmp(webui->uri_camid,"motion") == 0)){
1130 webui->cnct_type = WEBUI_CNCT_MOTION;
1131
1132 } else if ((strcmp(webui->uri_cmd1,"source") == 0) ||
1133 (strcmp(webui->uri_camid,"source") == 0)){
1134 webui->cnct_type = WEBUI_CNCT_SOURCE;
1135
1136 } else if ((strcmp(webui->uri_cmd1,"current") == 0) ||
1137 (strcmp(webui->uri_camid,"current") == 0)){
1138 webui->cnct_type = WEBUI_CNCT_STATIC;
1139
1140 } else if ((strlen(webui->uri_camid) > 0) &&
1141 (strlen(webui->uri_cmd1) == 0)){
1142 webui->cnct_type = WEBUI_CNCT_FULL;
1143
1144 } else {
1145 webui->cnct_type = WEBUI_CNCT_UNKNOWN;
1146 }
1147
1148 }
1149
webu_answer_ctrl(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** ptr)1150 static mymhd_retcd webu_answer_ctrl(void *cls
1151 , struct MHD_Connection *connection
1152 , const char *url
1153 , const char *method
1154 , const char *version
1155 , const char *upload_data
1156 , size_t *upload_data_size
1157 , void **ptr) {
1158
1159 /* This function "answers" the request for a webcontrol.*/
1160 int retcd;
1161 struct webui_ctx *webui = *ptr;
1162
1163 /* Eliminate compiler warnings */
1164 (void)cls;
1165 (void)url;
1166 (void)version;
1167 (void)upload_data;
1168 (void)upload_data_size;
1169
1170 /* Per MHD docs, this is called twice and we should process the second call */
1171 if (webui->mhd_first) {
1172 webui->mhd_first = FALSE;
1173 return MHD_YES;
1174 }
1175
1176 if (strcmp (method, "GET") != 0){
1177 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Invalid Method requested: %s"),method);
1178 return MHD_NO;
1179 }
1180
1181 webui->cnct_type = WEBUI_CNCT_CONTROL;
1182
1183 util_threadname_set("wu", 0,NULL);
1184
1185 webui->connection = connection;
1186
1187 /* Throw bad URLS back to user*/
1188 if ((webui->cnt == NULL) || (strlen(webui->url) == 0)){
1189 webu_badreq(webui);
1190 retcd = webu_mhd_send(webui, FALSE);
1191 return retcd;
1192 }
1193
1194 if (webui->cnt->webcontrol_finish) return MHD_NO;
1195
1196 if (strlen(webui->clientip) == 0){
1197 webu_clientip(webui);
1198 }
1199
1200 webu_hostname(webui, TRUE);
1201
1202 if (!webui->authenticated) {
1203 retcd = webu_mhd_auth(webui, TRUE);
1204 if (!webui->authenticated) return retcd;
1205 }
1206
1207 if ((webui->cntlst[0]->conf.webcontrol_interface == 1) ||
1208 (webui->cntlst[0]->conf.webcontrol_interface == 2)) {
1209 webu_text_main(webui);
1210 } else {
1211 webu_html_main(webui);
1212 }
1213
1214 retcd = webu_mhd_send(webui, TRUE);
1215 if (retcd == MHD_NO){
1216 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("send page failed %d"),retcd);
1217 }
1218 return retcd;
1219
1220 }
1221
webu_answer_strm(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** ptr)1222 static mymhd_retcd webu_answer_strm(void *cls
1223 , struct MHD_Connection *connection
1224 , const char *url
1225 , const char *method
1226 , const char *version
1227 , const char *upload_data
1228 , size_t *upload_data_size
1229 , void **ptr) {
1230
1231 /* Answer the request for all the streams*/
1232 int retcd;
1233 struct webui_ctx *webui = *ptr;
1234
1235 /* Eliminate compiler warnings */
1236 (void)cls;
1237 (void)url;
1238 (void)version;
1239 (void)upload_data;
1240 (void)upload_data_size;
1241
1242 /* Per docs, this is called twice and we should process the second call */
1243 if (webui->mhd_first) {
1244 webui->mhd_first = FALSE;
1245 return MHD_YES;
1246 }
1247
1248 if (strcmp (method, "GET") != 0){
1249 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Invalid Method requested: %s"),method);
1250 return MHD_NO;
1251 }
1252
1253 util_threadname_set("st", 0,NULL);
1254
1255 webui->connection = connection;
1256
1257 /* Throw bad URLS back to user*/
1258 if ((webui->cnt == NULL) || (strlen(webui->url) == 0)){
1259 webu_badreq(webui);
1260 retcd = webu_mhd_send(webui, FALSE);
1261 return retcd;
1262 }
1263
1264 /* Do not answer a request until the motion loop has completed at least once */
1265 if (webui->cnt->passflag == 0) return MHD_NO;
1266
1267 if (webui->cnt->webcontrol_finish) return MHD_NO;
1268
1269 if (strlen(webui->clientip) == 0){
1270 webu_clientip(webui);
1271 }
1272
1273 webu_hostname(webui, FALSE);
1274
1275 if (!webui->authenticated) {
1276 retcd = webu_mhd_auth(webui, FALSE);
1277 if (!webui->authenticated) return retcd;
1278 }
1279
1280 webu_answer_strm_type(webui);
1281
1282 retcd = 0;
1283 if (webui->cnct_type == WEBUI_CNCT_STATIC){
1284 retcd = webu_stream_static(webui);
1285 if (retcd == MHD_NO){
1286 webu_badreq(webui);
1287 retcd = webu_mhd_send(webui, FALSE);
1288 }
1289 } else if (webui->cnct_type != WEBUI_CNCT_UNKNOWN) {
1290 retcd = webu_stream_mjpeg(webui);
1291 if (retcd == MHD_NO){
1292 webu_badreq(webui);
1293 retcd = webu_mhd_send(webui, FALSE);
1294 }
1295 } else {
1296 webu_badreq(webui);
1297 retcd = webu_mhd_send(webui, FALSE);
1298 }
1299
1300 if (retcd == MHD_NO){
1301 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("send page failed %d"),retcd);
1302 }
1303 return retcd;
1304
1305 }
1306
webu_mhd_init(void * cls,const char * uri,struct MHD_Connection * connection)1307 static void *webu_mhd_init(void *cls, const char *uri, struct MHD_Connection *connection) {
1308 /* This is called at the very start of getting a request before the "answer"
1309 * is processed. There are two variations of this and the difference is how
1310 * we call the webu_context_init. When we are processing for the webcontrol or
1311 * the stream port specified in the motion.conf file, we pass into the init function
1312 * the full list of all the cameras. The other version of the init is used when the
1313 * user specifies a unique port for each camera. In this situation, the full list
1314 * context is passed in as a null and the context of the camera desired is passed
1315 * instead.
1316 * When this function is processed, we basically only have the URL that the user requested
1317 * so we initialize everything and then parse out the URL to determine what the user is
1318 * asking.
1319 */
1320
1321 struct context **cnt = cls;
1322 struct webui_ctx *webui;
1323 int retcd;
1324
1325 (void)connection;
1326
1327 /* Set the thread name to connection until we know whether control or stream answers*/
1328 util_threadname_set("cn", 0,NULL);
1329
1330 webui = malloc(sizeof(struct webui_ctx));
1331
1332 webu_context_init(cnt, NULL, webui);
1333 webui->mhd_first = TRUE;
1334
1335 snprintf(webui->url,WEBUI_LEN_URLI,"%s",uri);
1336
1337 retcd = webu_parseurl(webui);
1338 if (retcd != 0){
1339 webu_parseurl_reset(webui);
1340 memset(webui->url,'\0',WEBUI_LEN_URLI);
1341 }
1342
1343 webu_parms_edit(webui);
1344
1345 return webui;
1346 }
1347
webu_mhd_init_one(void * cls,const char * uri,struct MHD_Connection * connection)1348 static void *webu_mhd_init_one(void *cls, const char *uri, struct MHD_Connection *connection) {
1349 /* This function initializes all the webui variables as we are getting a request. This
1350 * variation of the init is the one used when the user has specified a unique port number
1351 * for each camera. The variation is in how the webu_context_init is invoked. This passes
1352 * in a NULL for the full context list (webui->cntlist) and instead assigns the particular
1353 * camera context to webui->cnt
1354 */
1355 struct context *cnt = cls;
1356 struct webui_ctx *webui;
1357 int retcd;
1358
1359 (void)connection;
1360
1361 /* Set the thread name to connection until we know whether control or stream answers*/
1362 util_threadname_set("cn", 0,NULL);
1363
1364 webui = malloc(sizeof(struct webui_ctx));
1365
1366 webu_context_init(NULL, cnt, webui);
1367 webui->mhd_first = TRUE;
1368
1369 snprintf(webui->url,WEBUI_LEN_URLI,"%s",uri);
1370
1371 retcd = webu_parseurl(webui);
1372 if (retcd != 0){
1373 webu_parseurl_reset(webui);
1374 memset(webui->url,'\0',WEBUI_LEN_URLI);
1375 }
1376
1377 webu_parms_edit(webui);
1378
1379 return webui;
1380 }
1381
webu_mhd_deinit(void * cls,struct MHD_Connection * connection,void ** con_cls,enum MHD_RequestTerminationCode toe)1382 static void webu_mhd_deinit(void *cls
1383 , struct MHD_Connection *connection
1384 , void **con_cls
1385 , enum MHD_RequestTerminationCode toe) {
1386 /* This is the function called as the connection is closed so we free our webui variables*/
1387 struct webui_ctx *webui = *con_cls;
1388
1389 /* Eliminate compiler warnings */
1390 (void)connection;
1391 (void)cls;
1392 (void)toe;
1393
1394 if (webui->cnct_type == WEBUI_CNCT_FULL ){
1395 pthread_mutex_lock(&webui->cnt->mutex_stream);
1396 webui->cnt->stream_norm.cnct_count--;
1397 pthread_mutex_unlock(&webui->cnt->mutex_stream);
1398
1399 } else if (webui->cnct_type == WEBUI_CNCT_SUB ){
1400 pthread_mutex_lock(&webui->cnt->mutex_stream);
1401 webui->cnt->stream_sub.cnct_count--;
1402 pthread_mutex_unlock(&webui->cnt->mutex_stream);
1403
1404 } else if (webui->cnct_type == WEBUI_CNCT_MOTION ){
1405 pthread_mutex_lock(&webui->cnt->mutex_stream);
1406 webui->cnt->stream_motion.cnct_count--;
1407 pthread_mutex_unlock(&webui->cnt->mutex_stream);
1408
1409 } else if (webui->cnct_type == WEBUI_CNCT_SOURCE ){
1410 pthread_mutex_lock(&webui->cnt->mutex_stream);
1411 webui->cnt->stream_source.cnct_count--;
1412 pthread_mutex_unlock(&webui->cnt->mutex_stream);
1413
1414 } else if (webui->cnct_type == WEBUI_CNCT_STATIC ){
1415 pthread_mutex_lock(&webui->cnt->mutex_stream);
1416 webui->cnt->stream_norm.cnct_count--;
1417 pthread_mutex_unlock(&webui->cnt->mutex_stream);
1418
1419 }
1420
1421 webu_context_free(webui);
1422
1423 return;
1424 }
1425
webu_mhd_features_basic(struct mhdstart_ctx * mhdst)1426 static void webu_mhd_features_basic(struct mhdstart_ctx *mhdst){
1427 /* Use the MHD function to see what features it supports*/
1428 #if MHD_VERSION < 0x00094400
1429 (void)mhdst;
1430 #else
1431 int retcd;
1432 retcd = MHD_is_feature_supported (MHD_FEATURE_BASIC_AUTH);
1433 if (retcd == MHD_YES){
1434 MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO ,_("Basic authentication: available"));
1435 } else {
1436 if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_auth_method == 1)){
1437 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Basic authentication: disabled"));
1438 mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_auth_method = 0;
1439 } else if ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_auth_method == 1)){
1440 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Basic authentication: disabled"));
1441 mhdst->cnt[mhdst->indxthrd]->conf.stream_auth_method = 0;
1442 } else {
1443 MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("Basic authentication: disabled"));
1444 }
1445 }
1446 #endif
1447 }
1448
webu_mhd_features_digest(struct mhdstart_ctx * mhdst)1449 static void webu_mhd_features_digest(struct mhdstart_ctx *mhdst){
1450 /* Use the MHD function to see what features it supports*/
1451 #if MHD_VERSION < 0x00094400
1452 (void)mhdst;
1453 #else
1454 int retcd;
1455 retcd = MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH);
1456 if (retcd == MHD_YES){
1457 MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO ,_("Digest authentication: available"));
1458 } else {
1459 if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_auth_method == 2)){
1460 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Digest authentication: disabled"));
1461 mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_auth_method = 0;
1462 } else if ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_auth_method == 2)){
1463 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Digest authentication: disabled"));
1464 mhdst->cnt[mhdst->indxthrd]->conf.stream_auth_method = 0;
1465 } else {
1466 MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("Digest authentication: disabled"));
1467 }
1468 }
1469 #endif
1470 }
1471
webu_mhd_features_ipv6(struct mhdstart_ctx * mhdst)1472 static void webu_mhd_features_ipv6(struct mhdstart_ctx *mhdst){
1473 /* Use the MHD function to see what features it supports
1474 * If we have a really old version of MHD, then we will just support
1475 * IPv4
1476 */
1477 #if MHD_VERSION < 0x00094400
1478 if (mhdst->ipv6){
1479 MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("libmicrohttpd libary too old ipv6 disabled"));
1480 if (mhdst->ipv6) mhdst->ipv6 = 0;
1481 }
1482 #else
1483 int retcd;
1484 retcd = MHD_is_feature_supported (MHD_FEATURE_IPv6);
1485 if (retcd == MHD_YES){
1486 MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO ,_("IPV6: available"));
1487 } else {
1488 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("IPV6: disabled"));
1489 if (mhdst->ipv6) mhdst->ipv6 = 0;
1490 }
1491 #endif
1492 }
1493
webu_mhd_features_tls(struct mhdstart_ctx * mhdst)1494 static void webu_mhd_features_tls(struct mhdstart_ctx *mhdst){
1495 /* Use the MHD function to see what features it supports
1496 * If we have a really old version of MHD, then we will will not
1497 * support the ssl/tls request.
1498 */
1499 #if MHD_VERSION < 0x00094400
1500 if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls)){
1501 MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("libmicrohttpd libary too old SSL/TLS disabled"));
1502 mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls = 0;
1503 } else if ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_tls)) {
1504 MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("libmicrohttpd libary too old SSL/TLS disabled"));
1505 mhdst->cnt[mhdst->indxthrd]->conf.stream_tls = 0;
1506 }
1507 #else
1508 int retcd;
1509 retcd = MHD_is_feature_supported (MHD_FEATURE_SSL);
1510 if (retcd == MHD_YES){
1511 MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO ,_("SSL/TLS: available"));
1512 } else {
1513 if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls)){
1514 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("SSL/TLS: disabled"));
1515 mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls = 0;
1516 } else if ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_tls)){
1517 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("SSL/TLS: disabled"));
1518 mhdst->cnt[mhdst->indxthrd]->conf.stream_tls = 0;
1519 } else {
1520 MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("SSL/TLS: disabled"));
1521 }
1522 }
1523 #endif
1524 }
1525
webu_mhd_features(struct mhdstart_ctx * mhdst)1526 static void webu_mhd_features(struct mhdstart_ctx *mhdst){
1527 /* This function goes through at least a few of the MHD features
1528 * and adjusts the user parameters from the configuration as
1529 * needed to reflect what MHD can do
1530 */
1531
1532 webu_mhd_features_basic(mhdst);
1533
1534 webu_mhd_features_digest(mhdst);
1535
1536 webu_mhd_features_ipv6(mhdst);
1537
1538 webu_mhd_features_tls(mhdst);
1539
1540 }
1541
webu_mhd_loadfile(const char * fname)1542 static char *webu_mhd_loadfile(const char *fname){
1543 /* This function loads the requested certificate and key files into memory so we
1544 * can use them as needed if the user wants ssl/tls support. If the user did not
1545 * specify a file in the configuration, then we return NULL.
1546 */
1547 FILE *infile;
1548 size_t file_size, read_size;
1549 char * file_char;
1550
1551 if (fname == NULL) {
1552 file_char = NULL;
1553 } else {
1554 infile = fopen(fname, "rb");
1555 if (infile != NULL){
1556 fseek(infile, 0, SEEK_END);
1557 file_size = ftell(infile);
1558 if (file_size > 0 ){
1559 file_char = mymalloc(file_size +1);
1560 fseek(infile, 0, SEEK_SET);
1561 read_size = fread(file_char, file_size, 1, infile);
1562 if (read_size > 0 ){
1563 file_char[file_size] = 0;
1564 } else {
1565 free(file_char);
1566 file_char = NULL;
1567 MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO
1568 ,_("Error reading file for SSL/TLS support."));
1569 }
1570 } else {
1571 file_char = NULL;
1572 }
1573 fclose(infile);
1574 } else {
1575 file_char = NULL;
1576 }
1577 }
1578 return file_char;
1579 }
1580
webu_mhd_checktls(struct mhdstart_ctx * mhdst)1581 static void webu_mhd_checktls(struct mhdstart_ctx *mhdst){
1582 /* This function validates that if the user requested a SSL/TLS connection, then
1583 * they also need to provide a certificate and key file. If those are not provided
1584 * then we revise the configuration request for ssl/tls
1585 */
1586 if (mhdst->ctrl){
1587 if (mhdst->cnt[0]->conf.webcontrol_tls){
1588 if ((mhdst->cnt[0]->conf.webcontrol_cert == NULL) || (mhdst->tls_cert == NULL)) {
1589 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1590 ,_("SSL/TLS requested but no cert file provided. SSL/TLS disabled"));
1591 mhdst->cnt[0]->conf.webcontrol_tls = 0;
1592 }
1593 if ((mhdst->cnt[0]->conf.webcontrol_key == NULL) || (mhdst->tls_key == NULL)) {
1594 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1595 ,_("SSL/TLS requested but no key file provided. SSL/TLS disabled"));
1596 mhdst->cnt[0]->conf.webcontrol_tls = 0;
1597 }
1598 }
1599 } else {
1600 if (mhdst->cnt[mhdst->indxthrd]->conf.stream_tls){
1601 if ((mhdst->cnt[0]->conf.webcontrol_cert == NULL) || (mhdst->tls_cert == NULL)) {
1602 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1603 ,_("SSL/TLS requested but no cert file provided. SSL/TLS disabled"));
1604 mhdst->cnt[mhdst->indxthrd]->conf.stream_tls = 0;
1605 }
1606 if ((mhdst->cnt[0]->conf.webcontrol_key == NULL) || (mhdst->tls_key == NULL)) {
1607 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1608 ,_("SSL/TLS requested but no key file provided. SSL/TLS disabled"));
1609 mhdst->cnt[mhdst->indxthrd]->conf.stream_tls = 0;
1610 }
1611 }
1612 }
1613
1614 }
1615
webu_mhd_opts_init(struct mhdstart_ctx * mhdst)1616 static void webu_mhd_opts_init(struct mhdstart_ctx *mhdst){
1617 /* This function sets the init function to use for the MHD connection. If
1618 * the connection is related to the webcontrol or the stream specified in the
1619 * motion.conf file, then we pass in the full context list of all cameras. If
1620 * the MHD connection is only going to be for a single camera (a unique port for
1621 * each camera), then we call a different init function which only wants the single
1622 * motion context for that particular camera.
1623 */
1624 if ((!mhdst->ctrl) && (mhdst->indxthrd != 0)){
1625 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_URI_LOG_CALLBACK;
1626 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = (intptr_t)webu_mhd_init_one;
1627 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->cnt[mhdst->indxthrd];
1628 mhdst->mhd_opt_nbr++;
1629 } else {
1630 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_URI_LOG_CALLBACK;
1631 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = (intptr_t)webu_mhd_init;
1632 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->cnt;
1633 mhdst->mhd_opt_nbr++;
1634 }
1635
1636 }
1637
webu_mhd_opts_deinit(struct mhdstart_ctx * mhdst)1638 static void webu_mhd_opts_deinit(struct mhdstart_ctx *mhdst){
1639 /* Set the MHD option on the function to call when the connection closes */
1640 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_NOTIFY_COMPLETED;
1641 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = (intptr_t)webu_mhd_deinit;
1642 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = NULL;
1643 mhdst->mhd_opt_nbr++;
1644
1645 }
1646
webu_mhd_opts_localhost(struct mhdstart_ctx * mhdst)1647 static void webu_mhd_opts_localhost(struct mhdstart_ctx *mhdst){
1648 /* Set the MHD option on the acceptable connections. This is used to handle the
1649 * motion configuation option of localhost only.
1650 */
1651
1652 if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_localhost)){
1653 if (mhdst->ipv6){
1654 memset(&mhdst->lpbk_ipv6, 0, sizeof(struct sockaddr_in6));
1655 mhdst->lpbk_ipv6.sin6_family = AF_INET6;
1656 mhdst->lpbk_ipv6.sin6_port = htons(mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_port);
1657 mhdst->lpbk_ipv6.sin6_addr = in6addr_loopback;
1658
1659 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_SOCK_ADDR;
1660 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
1661 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = (struct sosockaddr *)(&mhdst->lpbk_ipv6);
1662 mhdst->mhd_opt_nbr++;
1663
1664 } else {
1665 memset(&mhdst->lpbk_ipv4, 0, sizeof(struct sockaddr_in));
1666 mhdst->lpbk_ipv4.sin_family = AF_INET;
1667 mhdst->lpbk_ipv4.sin_port = htons(mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_port);
1668 mhdst->lpbk_ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1669
1670 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_SOCK_ADDR;
1671 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
1672 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = (struct sockaddr *)(&mhdst->lpbk_ipv4);
1673 mhdst->mhd_opt_nbr++;
1674 }
1675 } else if((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_localhost)){
1676 if (mhdst->ipv6){
1677 memset(&mhdst->lpbk_ipv6, 0, sizeof(struct sockaddr_in6));
1678 mhdst->lpbk_ipv6.sin6_family = AF_INET6;
1679 mhdst->lpbk_ipv6.sin6_port = htons(mhdst->cnt[mhdst->indxthrd]->conf.stream_port);
1680 mhdst->lpbk_ipv6.sin6_addr = in6addr_loopback;
1681
1682 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_SOCK_ADDR;
1683 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
1684 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = (struct sosockaddr *)(&mhdst->lpbk_ipv6);
1685 mhdst->mhd_opt_nbr++;
1686 } else {
1687 memset(&mhdst->lpbk_ipv4, 0, sizeof(struct sockaddr_in));
1688 mhdst->lpbk_ipv4.sin_family = AF_INET;
1689 mhdst->lpbk_ipv4.sin_port = htons(mhdst->cnt[mhdst->indxthrd]->conf.stream_port);
1690 mhdst->lpbk_ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1691
1692 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_SOCK_ADDR;
1693 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
1694 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = (struct sockaddr *)(&mhdst->lpbk_ipv4);
1695 mhdst->mhd_opt_nbr++;
1696 }
1697 }
1698
1699 }
1700
webu_mhd_opts_digest(struct mhdstart_ctx * mhdst)1701 static void webu_mhd_opts_digest(struct mhdstart_ctx *mhdst){
1702 /* Set the MHD option for the type of authentication that we will be using. This
1703 * function is when we are wanting to use digest authentication
1704 */
1705
1706 if (((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_auth_method == 2)) ||
1707 ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_auth_method == 2))) {
1708
1709 if (mhdst->ctrl) {
1710 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_DIGEST_AUTH_RANDOM;
1711 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = sizeof(mhdst->cnt[mhdst->indxthrd]->webcontrol_digest_rand);
1712 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->cnt[mhdst->indxthrd]->webcontrol_digest_rand;
1713 mhdst->mhd_opt_nbr++;
1714 } else {
1715 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_DIGEST_AUTH_RANDOM;
1716 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = sizeof(mhdst->cnt[mhdst->indxthrd]->webstream_digest_rand);
1717 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->cnt[mhdst->indxthrd]->webstream_digest_rand;
1718 mhdst->mhd_opt_nbr++;
1719 }
1720
1721 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_NONCE_NC_SIZE;
1722 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 300;
1723 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = NULL;
1724 mhdst->mhd_opt_nbr++;
1725
1726 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_CONNECTION_TIMEOUT;
1727 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = (unsigned int) 120;
1728 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = NULL;
1729 mhdst->mhd_opt_nbr++;
1730 }
1731
1732 }
1733
webu_mhd_opts_tls(struct mhdstart_ctx * mhdst)1734 static void webu_mhd_opts_tls(struct mhdstart_ctx *mhdst){
1735 /* Set the MHD options needed when we want TLS connections */
1736 if ((( mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls)) ||
1737 ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_tls))) {
1738
1739 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_HTTPS_MEM_CERT;
1740 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
1741 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->tls_cert;
1742 mhdst->mhd_opt_nbr++;
1743
1744 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_HTTPS_MEM_KEY;
1745 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
1746 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->tls_key;
1747 mhdst->mhd_opt_nbr++;
1748 }
1749
1750 }
1751
webu_mhd_opts(struct mhdstart_ctx * mhdst)1752 static void webu_mhd_opts(struct mhdstart_ctx *mhdst){
1753 /* Set all the options we need based upon the motion configuration parameters*/
1754
1755 mhdst->mhd_opt_nbr = 0;
1756
1757 webu_mhd_checktls(mhdst);
1758
1759 webu_mhd_opts_deinit(mhdst);
1760
1761 webu_mhd_opts_init(mhdst);
1762
1763 webu_mhd_opts_localhost(mhdst);
1764
1765 webu_mhd_opts_digest(mhdst);
1766
1767 webu_mhd_opts_tls(mhdst);
1768
1769 mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_END;
1770 mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
1771 mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = NULL;
1772 mhdst->mhd_opt_nbr++;
1773
1774 }
1775
webu_mhd_flags(struct mhdstart_ctx * mhdst)1776 static void webu_mhd_flags(struct mhdstart_ctx *mhdst){
1777
1778 /* This sets the MHD startup flags based upon what user put into configuration */
1779 mhdst->mhd_flags = MHD_USE_THREAD_PER_CONNECTION;
1780
1781 if (mhdst->ipv6) mhdst->mhd_flags = mhdst->mhd_flags | MHD_USE_DUAL_STACK;
1782
1783 if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls)){
1784 mhdst->mhd_flags = mhdst->mhd_flags | MHD_USE_SSL;
1785 } else if ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_tls)){
1786 mhdst->mhd_flags = mhdst->mhd_flags | MHD_USE_SSL;
1787 }
1788
1789 }
1790
webu_start_ctrl(struct context ** cnt)1791 static void webu_start_ctrl(struct context **cnt){
1792 /* This is the function that actually starts the MHD daemon for handling the webcontrol.
1793 * There are many options for MHD and they will vary depending upon what our Motion user
1794 * has requested in the configuration. There are many functions in this module to assign
1795 * these options and they are passed in a pointer to the mhdst variable so that they can
1796 * assign the correct values for MHD start up. Since this function is doing the webcontrol
1797 * we are only using thread 0 values.
1798 */
1799
1800 struct mhdstart_ctx mhdst;
1801 unsigned int randnbr;
1802
1803 mhdst.tls_cert = webu_mhd_loadfile(cnt[0]->conf.webcontrol_cert);
1804 mhdst.tls_key = webu_mhd_loadfile(cnt[0]->conf.webcontrol_key);
1805 mhdst.ctrl = TRUE;
1806 mhdst.indxthrd = 0;
1807 mhdst.cnt = cnt;
1808 mhdst.ipv6 = cnt[0]->conf.webcontrol_ipv6;
1809
1810 /* Set the rand number for webcontrol digest if needed */
1811 srand(time(NULL));
1812 randnbr = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
1813 snprintf(cnt[0]->webcontrol_digest_rand
1814 ,sizeof(cnt[0]->webcontrol_digest_rand),"%d",randnbr);
1815
1816 cnt[0]->webcontrol_daemon = NULL;
1817 if (cnt[0]->conf.webcontrol_port != 0 ){
1818 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1819 ,_("Starting webcontrol on port %d")
1820 ,cnt[0]->conf.webcontrol_port);
1821
1822 mhdst.mhd_ops = malloc(sizeof(struct MHD_OptionItem)*WEBUI_MHD_OPTS);
1823 webu_mhd_features(&mhdst);
1824 webu_mhd_opts(&mhdst);
1825 webu_mhd_flags(&mhdst);
1826
1827 cnt[0]->webcontrol_daemon = MHD_start_daemon (mhdst.mhd_flags
1828 ,cnt[0]->conf.webcontrol_port
1829 ,NULL, NULL
1830 ,&webu_answer_ctrl, cnt
1831 ,MHD_OPTION_ARRAY, mhdst.mhd_ops
1832 ,MHD_OPTION_END);
1833 free(mhdst.mhd_ops);
1834 if (cnt[0]->webcontrol_daemon == NULL){
1835 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Unable to start MHD"));
1836 } else {
1837 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1838 ,_("Started webcontrol on port %d")
1839 ,cnt[0]->conf.webcontrol_port);
1840 }
1841 }
1842
1843 if (mhdst.tls_cert != NULL) free(mhdst.tls_cert);
1844 if (mhdst.tls_key != NULL) free(mhdst.tls_key);
1845
1846 return;
1847 }
1848
webu_strm_ntc(struct context ** cnt,int indxthrd)1849 static void webu_strm_ntc(struct context **cnt, int indxthrd){
1850 int indx;
1851
1852 if (indxthrd == 0 ){
1853 if (cnt[1] != NULL) {
1854 indx = 1;
1855 while (cnt[indx] != NULL){
1856 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1857 ,_("Started camera %d stream on port/camera_id %d/%d")
1858 ,cnt[indx]->camera_id
1859 ,cnt[indxthrd]->conf.stream_port
1860 ,cnt[indx]->camera_id);
1861 indx++;
1862 }
1863 } else {
1864 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1865 ,_("Started camera %d stream on port %d")
1866 ,cnt[indxthrd]->camera_id,cnt[indxthrd]->conf.stream_port);
1867 }
1868 } else {
1869 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1870 ,_("Started camera %d stream on port %d")
1871 ,cnt[indxthrd]->camera_id,cnt[indxthrd]->conf.stream_port);
1872 }
1873 }
1874
webu_start_strm(struct context ** cnt)1875 static void webu_start_strm(struct context **cnt){
1876 /* This function starts up the daemon for the streams. It loops through
1877 * all of the camera context's provided and starts streams as requested. If
1878 * the thread number is zero, then it starts the full list stream context
1879 */
1880
1881 struct mhdstart_ctx mhdst;
1882 unsigned int randnbr;
1883
1884 mhdst.tls_cert = webu_mhd_loadfile(cnt[0]->conf.webcontrol_cert);
1885 mhdst.tls_key = webu_mhd_loadfile(cnt[0]->conf.webcontrol_key);
1886 mhdst.ctrl = FALSE;
1887 mhdst.indxthrd = 0;
1888 mhdst.cnt = cnt;
1889 mhdst.ipv6 = cnt[0]->conf.webcontrol_ipv6;
1890
1891 /* Set the rand number for webcontrol digest if needed */
1892 srand(time(NULL));
1893 randnbr = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
1894 snprintf(cnt[0]->webstream_digest_rand
1895 ,sizeof(cnt[0]->webstream_digest_rand),"%d",randnbr);
1896
1897 while (cnt[mhdst.indxthrd] != NULL){
1898 cnt[mhdst.indxthrd]->webstream_daemon = NULL;
1899 if (cnt[mhdst.indxthrd]->conf.stream_port != 0 ){
1900 if (mhdst.indxthrd == 0){
1901 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1902 ,_("Starting all camera streams on port %d")
1903 ,cnt[mhdst.indxthrd]->conf.stream_port);
1904 } else {
1905 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1906 ,_("Starting camera %d stream on port %d")
1907 ,cnt[mhdst.indxthrd]->camera_id
1908 ,cnt[mhdst.indxthrd]->conf.stream_port);
1909 }
1910
1911 mhdst.mhd_ops= malloc(sizeof(struct MHD_OptionItem)*WEBUI_MHD_OPTS);
1912 webu_mhd_features(&mhdst);
1913 webu_mhd_opts(&mhdst);
1914 webu_mhd_flags(&mhdst);
1915 if (mhdst.indxthrd == 0){
1916 cnt[mhdst.indxthrd]->webstream_daemon = MHD_start_daemon (mhdst.mhd_flags
1917 ,cnt[mhdst.indxthrd]->conf.stream_port
1918 ,NULL, NULL
1919 ,&webu_answer_strm, cnt
1920 ,MHD_OPTION_ARRAY, mhdst.mhd_ops
1921 ,MHD_OPTION_END);
1922 } else {
1923 cnt[mhdst.indxthrd]->webstream_daemon = MHD_start_daemon (mhdst.mhd_flags
1924 ,cnt[mhdst.indxthrd]->conf.stream_port
1925 ,NULL, NULL
1926 ,&webu_answer_strm, cnt[mhdst.indxthrd]
1927 ,MHD_OPTION_ARRAY, mhdst.mhd_ops
1928 ,MHD_OPTION_END);
1929 }
1930 free(mhdst.mhd_ops);
1931 if (cnt[mhdst.indxthrd]->webstream_daemon == NULL){
1932 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1933 ,_("Unable to start stream for camera %d")
1934 ,cnt[mhdst.indxthrd]->camera_id);
1935 } else {
1936 webu_strm_ntc(cnt,mhdst.indxthrd);
1937 }
1938 }
1939 mhdst.indxthrd++;
1940 }
1941 if (mhdst.tls_cert != NULL) free(mhdst.tls_cert);
1942 if (mhdst.tls_key != NULL) free(mhdst.tls_key);
1943
1944 return;
1945 }
1946
webu_start_ports(struct context ** cnt)1947 static void webu_start_ports(struct context **cnt){
1948 /* Perform check for duplicate ports being specified. The config loading will
1949 * duplicate ports from the motion.conf file to all the cameras so we do not
1950 * log these duplicates to the user and instead just silently set them to zero
1951 */
1952 int indx, indx2;
1953
1954 if (cnt[0]->conf.webcontrol_port != 0){
1955 indx = 0;
1956 while (cnt[indx] != NULL){
1957 if ((cnt[0]->conf.webcontrol_port == cnt[indx]->conf.webcontrol_port) && (indx > 0)){
1958 cnt[indx]->conf.webcontrol_port = 0;
1959 }
1960
1961 if (cnt[0]->conf.webcontrol_port == cnt[indx]->conf.stream_port){
1962 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1963 ,_("Duplicate port requested %d")
1964 ,cnt[indx]->conf.stream_port);
1965 cnt[indx]->conf.stream_port = 0;
1966 }
1967
1968 indx++;
1969 }
1970 }
1971
1972 /* Now check on the stream ports */
1973 indx = 0;
1974 while (cnt[indx] != NULL){
1975 if (cnt[indx]->conf.stream_port != 0){
1976 indx2 = indx + 1;
1977 while (cnt[indx2] != NULL){
1978 if (cnt[indx]->conf.stream_port == cnt[indx2]->conf.stream_port){
1979 if (indx != 0){
1980 MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
1981 ,_("Duplicate port requested %d")
1982 ,cnt[indx2]->conf.stream_port);
1983 }
1984 cnt[indx2]->conf.stream_port = 0;
1985 }
1986 indx2++;
1987 }
1988 }
1989 indx++;
1990 }
1991 }
1992
webu_stop(struct context ** cnt)1993 void webu_stop(struct context **cnt) {
1994 /* This function is called from the main Motion loop to shutdown the
1995 * various MHD connections
1996 */
1997 int indxthrd;
1998
1999 if (cnt[0]->webcontrol_daemon != NULL){
2000 cnt[0]->webcontrol_finish = TRUE;
2001 MHD_stop_daemon (cnt[0]->webcontrol_daemon);
2002 }
2003
2004
2005 indxthrd = 0;
2006 while (cnt[indxthrd] != NULL){
2007 if (cnt[indxthrd]->webstream_daemon != NULL){
2008 cnt[indxthrd]->webcontrol_finish = TRUE;
2009 MHD_stop_daemon (cnt[indxthrd]->webstream_daemon);
2010 }
2011 cnt[indxthrd]->webstream_daemon = NULL;
2012 cnt[indxthrd]->webcontrol_daemon = NULL;
2013 indxthrd++;
2014 }
2015 }
2016
webu_start(struct context ** cnt)2017 void webu_start(struct context **cnt) {
2018 /* This function is called from the main motion thread to start up the
2019 * webcontrol and streams. We need to block some signals otherwise MHD
2020 * will not function correctly.
2021 */
2022 struct sigaction act;
2023 int indxthrd;
2024
2025 /* set signal handlers TO IGNORE */
2026 memset(&act, 0, sizeof(act));
2027 sigemptyset(&act.sa_mask);
2028 act.sa_handler = SIG_IGN;
2029 sigaction(SIGPIPE, &act, NULL);
2030 sigaction(SIGCHLD, &act, NULL);
2031
2032
2033 indxthrd = 0;
2034 while (cnt[indxthrd] != NULL){
2035 cnt[indxthrd]->webstream_daemon = NULL;
2036 cnt[indxthrd]->webcontrol_daemon = NULL;
2037 cnt[indxthrd]->webcontrol_finish = FALSE;
2038 indxthrd++;
2039 }
2040
2041 if (cnt[0]->conf.stream_preview_method != 99){
2042 webu_start_ports(cnt);
2043
2044 webu_start_strm(cnt);
2045 }
2046
2047 webu_start_ctrl(cnt);
2048
2049 return;
2050
2051 }
2052
2053
2054