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                 ,&cent, &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                 ,&cent, &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