"Fossies" - the Fresh Open Source Software Archive

Member "motion-Release-4.3.0/src/webu.c" (14 Jan 2020, 76421 Bytes) of package /linux/misc/motion-Release-4.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "webu.c" see the Fossies "Dox" file reference documentation.

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