mirror of
https://github.com/Motion-Project/motion.git
synced 2026-02-04 12:01:33 -05:00
2106 lines
74 KiB
C
2106 lines
74 KiB
C
/*
|
|
* webu.c
|
|
*
|
|
* Webcontrol and Streams for motion.
|
|
*
|
|
* This software is distributed under the GNU Public License Version 2
|
|
* See also the file 'COPYING'.
|
|
*
|
|
* Portions of code from Angel Carpintero (motiondevelop@gmail.com)
|
|
* from webhttpd.c Copyright 2004-2005
|
|
*
|
|
* Majority of module written by MrDave.
|
|
*
|
|
* Function naming scheme:
|
|
* webu* - All functions in this module have this prefix.
|
|
* webu_start - Entry point to start the daemon.
|
|
* webu_stop - Entry point to stop the daemon
|
|
* webu_mhd* - Functions related to libmicrohttd implementation
|
|
* webu_process_action - Performs most items under the action menu
|
|
* webu_process_config - Saves the parameter values into Motion.
|
|
* webu_process_track - Performs the tracking functions.
|
|
*
|
|
* Some function names are long and are not expected to contain any
|
|
* logger message that would display the function name to the user.
|
|
*
|
|
* Functions are generally kept to under one page in length
|
|
*
|
|
* Known Issues:
|
|
* The quit/restart uses signals and this should be reconsidered.
|
|
* The tracking is "best effort" since developer does not have tracking camera.
|
|
* The conf_cmdparse assumes that the pointers to the motion context for each
|
|
* camera are always sequential and enforcement of the pointers being sequential
|
|
* has not been observed in the other modules. (This is a legacy assumption)
|
|
*/
|
|
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include "motion.h"
|
|
#include "webu.h"
|
|
#include "webu_html.h"
|
|
#include "webu_text.h"
|
|
#include "webu_stream.h"
|
|
#include "translate.h"
|
|
|
|
/* Context to pass the parms to functions to start mhd */
|
|
struct mhdstart_ctx {
|
|
struct context **cnt;
|
|
char *tls_cert;
|
|
char *tls_key;
|
|
int ctrl;
|
|
int indxthrd;
|
|
struct MHD_OptionItem *mhd_ops;
|
|
int mhd_opt_nbr;
|
|
unsigned int mhd_flags;
|
|
int ipv6;
|
|
struct sockaddr_in lpbk_ipv4;
|
|
struct sockaddr_in6 lpbk_ipv6;
|
|
};
|
|
|
|
|
|
static void webu_context_init(struct context **cntlst, struct context *cnt, struct webui_ctx *webui) {
|
|
|
|
int indx;
|
|
|
|
webui->url = mymalloc(WEBUI_LEN_URLI);
|
|
webui->uri_camid = mymalloc(WEBUI_LEN_PARM);
|
|
webui->uri_cmd1 = mymalloc(WEBUI_LEN_PARM);
|
|
webui->uri_cmd2 = mymalloc(WEBUI_LEN_PARM);
|
|
webui->uri_parm1 = mymalloc(WEBUI_LEN_PARM);
|
|
webui->uri_value1 = mymalloc(WEBUI_LEN_PARM);
|
|
webui->uri_parm2 = mymalloc(WEBUI_LEN_PARM);
|
|
webui->uri_value2 = mymalloc(WEBUI_LEN_PARM);
|
|
webui->clientip = mymalloc(WEBUI_LEN_URLI);
|
|
webui->hostname = mymalloc(WEBUI_LEN_PARM);
|
|
webui->auth_denied = mymalloc(WEBUI_LEN_RESP);
|
|
webui->auth_opaque = mymalloc(WEBUI_LEN_PARM);
|
|
webui->auth_realm = mymalloc(WEBUI_LEN_PARM);
|
|
webui->text_eol = mymalloc(WEBUI_LEN_PARM);
|
|
webui->auth_user = NULL; /* Buffer to hold the user name*/
|
|
webui->auth_pass = NULL; /* Buffer to hold the password */
|
|
webui->authenticated = FALSE; /* boolean for whether we are authenticated*/
|
|
webui->lang = mymalloc(3); /* Two digit lang code plus null terminator */
|
|
webui->lang_full = mymalloc(6); /* lang code, e.g US_en */
|
|
webui->resp_size = WEBUI_LEN_RESP * 10; /* The size of the resp_page buffer. May get adjusted */
|
|
webui->resp_used = 0; /* How many bytes used so far in resp_page*/
|
|
webui->stream_pos = 0; /* Stream position of image being sent */
|
|
webui->stream_fps = 1; /* Stream rate */
|
|
webui->resp_page = mymalloc(webui->resp_size); /* The response being constructed */
|
|
webui->cntlst = cntlst; /* The list of context's for all cameras */
|
|
webui->cnt = cnt; /* The context pointer for a single camera */
|
|
webui->cnct_type = WEBUI_CNCT_UNKNOWN;
|
|
|
|
/* get the number of cameras and threads */
|
|
indx = 0;
|
|
if (webui->cntlst != NULL){
|
|
while (webui->cntlst[++indx]);
|
|
}
|
|
webui->cam_threads = indx;
|
|
|
|
webui->cam_count = indx;
|
|
if (indx > 1)
|
|
webui->cam_count--;
|
|
|
|
/* 1 thread, 1 camera = just motion.conf.
|
|
* 2 thread, 1 camera, then using motion.conf plus a separate camera file */
|
|
snprintf(webui->lang_full, 6,"%s", getenv("LANGUAGE"));
|
|
snprintf(webui->lang, 3,"%s",webui->lang_full);
|
|
|
|
memset(webui->hostname,'\0',WEBUI_LEN_PARM);
|
|
memset(webui->resp_page,'\0',webui->resp_size);
|
|
|
|
return;
|
|
}
|
|
|
|
static void webu_context_null(struct webui_ctx *webui) {
|
|
/* Null out all the pointers in our webui context */
|
|
webui->url = NULL;
|
|
webui->hostname = NULL;
|
|
webui->uri_camid = NULL;
|
|
webui->uri_cmd1 = NULL;
|
|
webui->uri_cmd2 = NULL;
|
|
webui->uri_parm1 = NULL;
|
|
webui->uri_value1 = NULL;
|
|
webui->uri_parm2 = NULL;
|
|
webui->uri_value2 = NULL;
|
|
webui->lang = NULL;
|
|
webui->lang_full = NULL;
|
|
webui->resp_page = NULL;
|
|
webui->connection = NULL;
|
|
webui->auth_user = NULL;
|
|
webui->auth_pass = NULL;
|
|
webui->auth_denied = NULL;
|
|
webui->auth_opaque = NULL;
|
|
webui->auth_realm = NULL;
|
|
webui->clientip = NULL;
|
|
webui->text_eol = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
static void webu_context_free(struct webui_ctx *webui) {
|
|
|
|
if (webui->hostname != NULL) free(webui->hostname);
|
|
if (webui->url != NULL) free(webui->url);
|
|
if (webui->uri_camid != NULL) free(webui->uri_camid);
|
|
if (webui->uri_cmd1 != NULL) free(webui->uri_cmd1);
|
|
if (webui->uri_cmd2 != NULL) free(webui->uri_cmd2);
|
|
if (webui->uri_parm1 != NULL) free(webui->uri_parm1);
|
|
if (webui->uri_value1 != NULL) free(webui->uri_value1);
|
|
if (webui->uri_parm2 != NULL) free(webui->uri_parm2);
|
|
if (webui->uri_value2 != NULL) free(webui->uri_value2);
|
|
if (webui->lang != NULL) free(webui->lang);
|
|
if (webui->lang_full != NULL) free(webui->lang_full);
|
|
if (webui->resp_page != NULL) free(webui->resp_page);
|
|
if (webui->auth_user != NULL) free(webui->auth_user);
|
|
if (webui->auth_pass != NULL) free(webui->auth_pass);
|
|
if (webui->auth_denied != NULL) free(webui->auth_denied);
|
|
if (webui->auth_opaque != NULL) free(webui->auth_opaque);
|
|
if (webui->auth_realm != NULL) free(webui->auth_realm);
|
|
if (webui->clientip != NULL) free(webui->clientip);
|
|
if (webui->text_eol != NULL) free(webui->text_eol);
|
|
|
|
webu_context_null(webui);
|
|
|
|
free(webui);
|
|
|
|
return;
|
|
}
|
|
|
|
static void webu_badreq(struct webui_ctx *webui){
|
|
/* This function is used in this webu module as a central function when there is a bad
|
|
* request. Since sometimes we will be unable to determine what camera context (stream
|
|
* or camera) originated the request and we have NULL for cntlist and cnt, we default the
|
|
* response to be HTML. Otherwise, we do know the type and we send back to the user the
|
|
* bad request response either with or without the HTML tags.
|
|
*/
|
|
if (webui->cnt != NULL) {
|
|
if (webui->cnt->conf.webcontrol_interface == 1){
|
|
webu_text_badreq(webui);
|
|
} else {
|
|
webu_html_badreq(webui);
|
|
}
|
|
} else if (webui->cntlst != NULL) {
|
|
if (webui->cntlst[0]->conf.webcontrol_interface == 1){
|
|
webu_text_badreq(webui);
|
|
} else {
|
|
webu_html_badreq(webui);
|
|
}
|
|
} else {
|
|
webu_html_badreq(webui);
|
|
}
|
|
}
|
|
|
|
void webu_write(struct webui_ctx *webui, const char *buf) {
|
|
/* Copy the buf data to our response buffer. If the response buffer is not large enough to
|
|
* accept our new data coming in, then expand it in chunks of 10
|
|
*/
|
|
int resp_len;
|
|
char *temp_resp;
|
|
size_t temp_size;
|
|
|
|
resp_len = strlen(buf);
|
|
|
|
temp_size = webui->resp_size;
|
|
while ((resp_len + webui->resp_used) > temp_size){
|
|
temp_size = temp_size + (WEBUI_LEN_RESP * 10);
|
|
}
|
|
|
|
if (temp_size > webui->resp_size){
|
|
temp_resp = mymalloc(webui->resp_size);
|
|
memcpy(temp_resp, webui->resp_page, webui->resp_size);
|
|
free(webui->resp_page);
|
|
webui->resp_page = mymalloc(temp_size);
|
|
memset(webui->resp_page,'\0',temp_size);
|
|
memcpy(webui->resp_page, temp_resp, webui->resp_size);
|
|
webui->resp_size = temp_size;
|
|
free(temp_resp);
|
|
}
|
|
|
|
memcpy(webui->resp_page + webui->resp_used, buf, resp_len);
|
|
webui->resp_used = webui->resp_used + resp_len;
|
|
|
|
return;
|
|
}
|
|
|
|
static int webu_url_decode(char *urlencoded, size_t length) {
|
|
/* We are sent a URI encoded string and this decodes it to characters
|
|
* If the sent URL that isn't valid, then we clear out the URL
|
|
* so it is not processed in further functions. The "answer" functions
|
|
* look for empty urls and answer with bad request
|
|
*/
|
|
char *data = urlencoded;
|
|
char *urldecoded = urlencoded;
|
|
int scan_rslt;
|
|
size_t origlen;
|
|
|
|
origlen = length;
|
|
|
|
if (urlencoded[0] != '/'){
|
|
MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO, _("Invalid url: %s"),urlencoded);
|
|
memset(urlencoded,'\0',origlen);
|
|
return -1;
|
|
}
|
|
|
|
while (length > 0) {
|
|
if (*data == '%') {
|
|
char c[3];
|
|
int i;
|
|
data++;
|
|
length--;
|
|
c[0] = *data++;
|
|
length--;
|
|
c[1] = *data;
|
|
c[2] = 0;
|
|
|
|
scan_rslt = sscanf(c, "%x", &i);
|
|
if (scan_rslt < 1){
|
|
MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO,_("Error decoding url"));
|
|
memset(urlencoded,'\0',origlen);
|
|
return -1;
|
|
}
|
|
|
|
if (i < 128) {
|
|
*urldecoded++ = (char)i;
|
|
} else {
|
|
*urldecoded++ = '%';
|
|
*urldecoded++ = c[0];
|
|
*urldecoded++ = c[1];
|
|
}
|
|
|
|
} else if (*data == '<' || *data == '+' || *data == '>') {
|
|
*urldecoded++ = ' ';
|
|
} else {
|
|
*urldecoded++ = *data;
|
|
}
|
|
|
|
data++;
|
|
length--;
|
|
}
|
|
*urldecoded = '\0';
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static void webu_parms_edit(struct webui_ctx *webui) {
|
|
|
|
/* Determine the thread number provided.
|
|
* If no thread provided, assign it to -1
|
|
* Samples:
|
|
* http://localhost:8081/0/stream (cntlist will be populated and this function will set cnt)
|
|
* http://localhost:8081/stream (cntlist will be null, cnt will be populated)
|
|
* http://localhost:8081/ (cntlist will be null, cnt will be populated)
|
|
*/
|
|
int indx, is_nbr;
|
|
|
|
if (strlen(webui->uri_camid) > 0){
|
|
is_nbr = TRUE;
|
|
for (indx=0; indx < (int)strlen(webui->uri_camid);indx++){
|
|
if ((webui->uri_camid[indx] > '9') || (webui->uri_camid[indx] < '0')) is_nbr = FALSE;
|
|
}
|
|
if (is_nbr){
|
|
webui->thread_nbr = atoi(webui->uri_camid);
|
|
} else {
|
|
webui->thread_nbr = -1;
|
|
}
|
|
} else {
|
|
webui->thread_nbr = -1;
|
|
}
|
|
|
|
/* Set the single context pointer to thread we are answering
|
|
* If the connection is for a single stream (legacy method of a port
|
|
* per stream), then the cntlist will be null and the camera context
|
|
* will already be assigned into webui->cnt. This is part of the
|
|
* init function which is called for MHD and it has the different
|
|
* variations depending upon how the port and cameras were specified.
|
|
* Also set/convert the camid into the thread number.
|
|
*/
|
|
|
|
if (webui->cntlst != NULL){
|
|
if (webui->thread_nbr < 0){
|
|
webui->cnt = webui->cntlst[0];
|
|
webui->thread_nbr = 0;
|
|
} else {
|
|
indx = 0;
|
|
while (webui->cntlst[indx] != NULL){
|
|
if (webui->cntlst[indx]->camera_id == webui->thread_nbr){
|
|
webui->thread_nbr = indx;
|
|
break;
|
|
}
|
|
indx++;
|
|
}
|
|
/* This may be null, in which case we will not answer the request */
|
|
webui->cnt = webui->cntlst[indx];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void webu_parseurl_parms(struct webui_ctx *webui, char *st_pos) {
|
|
|
|
/* Parse the parameters of the URI
|
|
* Earlier functions have assigned the st_pos to the slash after the action and it is
|
|
* pointing at the set/get when this function is invoked.
|
|
* Samples (MHD takes off the IP:port)
|
|
* /{camid}/config/set?{parm}={value1}
|
|
* /{camid}/config/get?query={parm}
|
|
* /{camid}/track/set?x={value1}&y={value2}
|
|
* /{camid}/track/set?pan={value1}&tilt={value2}
|
|
* /{camid}/{cmd1}/{cmd2}?{parm1}={value1}&{parm2}={value2}
|
|
*/
|
|
|
|
int parm_len, last_parm;
|
|
char *en_pos;
|
|
|
|
|
|
/* First parse out the "set","get","pan","tilt","x","y"
|
|
* from the uri and put them into the cmd2.
|
|
* st_pos is at the beginning of the command
|
|
* If there is no ? then we are done parsing
|
|
* Note that each section is looking for a different
|
|
* delimitter. (?, =, &, =, &)
|
|
*/
|
|
last_parm = FALSE;
|
|
en_pos = strstr(st_pos,"?");
|
|
if (en_pos != NULL){
|
|
parm_len = en_pos - st_pos + 1;
|
|
if (parm_len >= WEBUI_LEN_PARM) return;
|
|
snprintf(webui->uri_cmd2, parm_len,"%s", st_pos);
|
|
|
|
/* Get the parameter name */
|
|
st_pos = st_pos + parm_len; /* Move past the command */
|
|
en_pos = strstr(st_pos,"=");
|
|
if (en_pos == NULL){
|
|
parm_len = strlen(webui->url) - parm_len;
|
|
last_parm = TRUE;
|
|
} else {
|
|
parm_len = en_pos - st_pos + 1;
|
|
}
|
|
if (parm_len >= WEBUI_LEN_PARM) return;
|
|
snprintf(webui->uri_parm1, parm_len,"%s", st_pos);
|
|
|
|
if (!last_parm){
|
|
/* Get the parameter value */
|
|
st_pos = st_pos + parm_len; /* Move past the equals sign */
|
|
en_pos = strstr(st_pos,"&");
|
|
if (en_pos == NULL){
|
|
parm_len = strlen(webui->url) - parm_len;
|
|
last_parm = TRUE;
|
|
} else {
|
|
parm_len = en_pos - st_pos + 1;
|
|
}
|
|
if (parm_len >= WEBUI_LEN_PARM) return;
|
|
snprintf(webui->uri_value1, parm_len,"%s", st_pos);
|
|
}
|
|
|
|
if (!last_parm){
|
|
/* Get the next parameter name */
|
|
st_pos = st_pos + parm_len; /* Move past the previous command */
|
|
en_pos = strstr(st_pos,"=");
|
|
if (en_pos == NULL){
|
|
parm_len = strlen(webui->url) - parm_len;
|
|
last_parm = TRUE;
|
|
} else {
|
|
parm_len = en_pos - st_pos + 1;
|
|
}
|
|
if (parm_len >= WEBUI_LEN_PARM) return;
|
|
snprintf(webui->uri_parm2, parm_len,"%s", st_pos);
|
|
}
|
|
|
|
if (!last_parm){
|
|
/* Get the next parameter value */
|
|
st_pos = st_pos + parm_len; /* Move past the equals sign */
|
|
en_pos = strstr(st_pos,"&");
|
|
if (en_pos == NULL){
|
|
parm_len = strlen(webui->url) - parm_len;
|
|
last_parm = TRUE;
|
|
} else {
|
|
parm_len = en_pos - st_pos + 1;
|
|
}
|
|
if (parm_len >= WEBUI_LEN_PARM) return;
|
|
snprintf(webui->uri_value2, parm_len,"%s", st_pos);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void webu_parseurl_reset(struct webui_ctx *webui) {
|
|
|
|
/* Reset the variables to empty strings*/
|
|
|
|
memset(webui->uri_camid,'\0',WEBUI_LEN_PARM);
|
|
memset(webui->uri_cmd1,'\0',WEBUI_LEN_PARM);
|
|
memset(webui->uri_cmd2,'\0',WEBUI_LEN_PARM);
|
|
memset(webui->uri_parm1,'\0',WEBUI_LEN_PARM);
|
|
memset(webui->uri_value1,'\0',WEBUI_LEN_PARM);
|
|
memset(webui->uri_parm2,'\0',WEBUI_LEN_PARM);
|
|
memset(webui->uri_value2,'\0',WEBUI_LEN_PARM);
|
|
|
|
}
|
|
|
|
static int webu_parseurl(struct webui_ctx *webui) {
|
|
/* Parse the sent URI into the commands and parameters
|
|
* so we can check the resulting strings in later functions
|
|
* and determine what actions to take.
|
|
* Samples
|
|
* /
|
|
* /{camid}
|
|
* /{camid}/config/set?log_level=6
|
|
* /{camid}/config/set?{parm}={value1}
|
|
* /{camid}/config/get?query={parm}
|
|
* /{camid}/track/set?x={value1}&y={value2}
|
|
* /{camid}/track/set?pan={value1}&tilt={value2}
|
|
* /{camid}/{cmd1}/{cmd2}?{parm1}={value1}&{parm2}={value2}
|
|
*/
|
|
|
|
int retcd, parm_len, last_slash;
|
|
char *st_pos, *en_pos;
|
|
|
|
retcd = 0;
|
|
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO, _("Sent url: %s"),webui->url);
|
|
|
|
webu_parseurl_reset(webui);
|
|
|
|
if (strlen(webui->url) == 0) return -1;
|
|
|
|
retcd = webu_url_decode(webui->url, strlen(webui->url));
|
|
if (retcd != 0) return retcd;
|
|
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO, _("Decoded url: %s"),webui->url);
|
|
|
|
/* Home page */
|
|
if (strlen(webui->url) == 1) return 0;
|
|
|
|
last_slash = 0;
|
|
|
|
/* Get the camid number and which sometimes this will contain an action if the user
|
|
* is setting the port for a particular camera and requests the
|
|
* stream by using http://localhost:port/stream
|
|
*/
|
|
st_pos = webui->url + 1; /* Move past the first "/" */
|
|
if (*st_pos == '-') return -1; /* Never allow a negative number */
|
|
en_pos = strstr(st_pos,"/");
|
|
if (en_pos == NULL){
|
|
parm_len = strlen(webui->url);
|
|
last_slash = 1;
|
|
} else {
|
|
parm_len = en_pos - st_pos + 1;
|
|
}
|
|
if (parm_len >= WEBUI_LEN_PARM) return -1; /* var was malloc'd to WEBUI_LEN_PARM */
|
|
snprintf(webui->uri_camid, parm_len,"%s", st_pos);
|
|
|
|
if (!last_slash){
|
|
/* Get cmd1 or action */
|
|
st_pos = st_pos + parm_len; /* Move past the camid */
|
|
en_pos = strstr(st_pos,"/");
|
|
if (en_pos == NULL){
|
|
parm_len = strlen(webui->url) - parm_len ;
|
|
last_slash = 1;
|
|
} else {
|
|
parm_len = en_pos - st_pos + 1;
|
|
}
|
|
if (parm_len >= WEBUI_LEN_PARM) return -1; /* var was malloc'd to WEBUI_LEN_PARM */
|
|
snprintf(webui->uri_cmd1, parm_len,"%s", st_pos);
|
|
}
|
|
|
|
if (!last_slash){
|
|
/* Get cmd2 or action */
|
|
st_pos = st_pos + parm_len; /* Move past the first command */
|
|
en_pos = strstr(st_pos,"/");
|
|
if (en_pos == NULL){
|
|
parm_len = strlen(webui->url) - parm_len;
|
|
last_slash = 1;
|
|
} else {
|
|
parm_len = en_pos - st_pos + 1;
|
|
}
|
|
if (parm_len >= WEBUI_LEN_PARM) return -1; /* var was malloc'd to WEBUI_LEN_PARM */
|
|
snprintf(webui->uri_cmd2, parm_len,"%s", st_pos);
|
|
}
|
|
|
|
if ((!strcmp(webui->uri_cmd1,"config") ||
|
|
!strcmp(webui->uri_cmd1,"track") ) &&
|
|
(strlen(webui->uri_cmd2) > 0)) {
|
|
webu_parseurl_parms(webui, st_pos);
|
|
}
|
|
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO,
|
|
"camid: >%s< cmd1: >%s< cmd2: >%s< parm1:>%s< val1:>%s< parm2:>%s< val2:>%s<"
|
|
,webui->uri_camid
|
|
,webui->uri_cmd1, webui->uri_cmd2
|
|
,webui->uri_parm1, webui->uri_value1
|
|
,webui->uri_parm2, webui->uri_value2);
|
|
|
|
|
|
return retcd;
|
|
|
|
}
|
|
|
|
void webu_process_action(struct webui_ctx *webui) {
|
|
/* Process the actions from the webcontrol that the user requested. This is used
|
|
* for both the html and text interface. The text interface just adds a additional
|
|
* response whereas the html just performs the action
|
|
*/
|
|
int indx;
|
|
|
|
indx = 0;
|
|
if ((strcmp(webui->uri_cmd2,"makemovie") == 0) ||
|
|
(strcmp(webui->uri_cmd2,"eventend") == 0)) {
|
|
if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
|
|
while (webui->cntlst[++indx]){
|
|
webui->cntlst[indx]->event_stop = TRUE;
|
|
}
|
|
} else {
|
|
webui->cnt->event_stop = TRUE;
|
|
}
|
|
|
|
} else if (strcmp(webui->uri_cmd2,"eventstart") == 0){
|
|
if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
|
|
while (webui->cntlst[++indx]){
|
|
webui->cntlst[indx]->event_user = TRUE;
|
|
}
|
|
} else {
|
|
webui->cnt->event_user = TRUE;
|
|
}
|
|
|
|
} else if (!strcmp(webui->uri_cmd2,"snapshot")){
|
|
if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
|
|
while (webui->cntlst[++indx])
|
|
webui->cntlst[indx]->snapshot = 1;
|
|
} else {
|
|
webui->cnt->snapshot = 1;
|
|
}
|
|
|
|
|
|
} else if (!strcmp(webui->uri_cmd2,"restart")){
|
|
if (webui->thread_nbr == 0) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, _("Restarting all threads"));
|
|
webui->cntlst[0]->webcontrol_finish = TRUE;
|
|
kill(getpid(),SIGHUP);
|
|
} else {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO,
|
|
_("Restarting thread %d"),webui->thread_nbr);
|
|
webui->cnt->restart = TRUE;
|
|
if (webui->cnt->running) {
|
|
webui->cnt->event_stop = TRUE;
|
|
webui->cnt->finish = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
} else if (!strcmp(webui->uri_cmd2,"quit")){
|
|
if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
|
|
while (webui->cntlst[++indx]){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO,
|
|
_("Quitting thread %d"),webui->thread_nbr);
|
|
webui->cntlst[indx]->restart = FALSE;
|
|
webui->cntlst[indx]->event_stop = TRUE;
|
|
webui->cntlst[indx]->event_user = TRUE;
|
|
webui->cntlst[indx]->finish = TRUE;
|
|
}
|
|
} else {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO,
|
|
_("Quitting thread %d"),webui->thread_nbr);
|
|
webui->cnt->restart = FALSE;
|
|
webui->cnt->event_stop = TRUE;
|
|
webui->cnt->event_user = TRUE;
|
|
webui->cnt->finish = TRUE;
|
|
}
|
|
|
|
} else if (!strcmp(webui->uri_cmd2,"end")){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, _("Motion terminating"));
|
|
while (webui->cntlst[indx]){
|
|
webui->cntlst[indx]->webcontrol_finish = TRUE;
|
|
webui->cntlst[indx]->restart = FALSE;
|
|
webui->cntlst[indx]->event_stop = TRUE;
|
|
webui->cntlst[indx]->event_user = TRUE;
|
|
webui->cntlst[indx]->finish = TRUE;
|
|
indx++;
|
|
}
|
|
|
|
|
|
} else if (!strcmp(webui->uri_cmd2,"start")){
|
|
if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
|
|
do {
|
|
webui->cntlst[indx]->pause = 0;
|
|
} while (webui->cntlst[++indx]);
|
|
} else {
|
|
webui->cnt->pause = 0;
|
|
}
|
|
} else if (!strcmp(webui->uri_cmd2,"pause")){
|
|
if (webui->thread_nbr == 0 && webui->cam_threads > 1) {
|
|
do {
|
|
webui->cntlst[indx]->pause = 1;
|
|
} while (webui->cntlst[++indx]);
|
|
} else {
|
|
webui->cnt->pause = 1;
|
|
}
|
|
|
|
} else if (!strcmp(webui->uri_cmd2,"connection")){
|
|
webu_text_connection(webui);
|
|
|
|
} else if (!strcmp(webui->uri_cmd2,"status")){
|
|
webu_text_status(webui);
|
|
|
|
} else if ((!strcmp(webui->uri_cmd2,"write")) ||
|
|
(!strcmp(webui->uri_cmd2,"writeyes"))){
|
|
conf_print(webui->cntlst);
|
|
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO,
|
|
_("Invalid action requested: >%s< >%s< >%s<")
|
|
, webui->uri_camid, webui->uri_cmd1, webui->uri_cmd2);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static int webu_process_config_set(struct webui_ctx *webui) {
|
|
/* Process the request to change the configuration parameters. Used
|
|
* both the html and text interfaces. If the parameter was found, then
|
|
* we return 0 otherwise a -1 to tell the calling function whether it
|
|
* was a valid parm to change.
|
|
*/
|
|
int indx, retcd;
|
|
char temp_name[WEBUI_LEN_PARM];
|
|
|
|
/* Search through the depreciated parms and if applicable,
|
|
* get the new parameter name so we can check its webcontrol_parms level
|
|
*/
|
|
snprintf(temp_name, WEBUI_LEN_PARM, "%s", webui->uri_parm1);
|
|
indx=0;
|
|
while (dep_config_params[indx].name != NULL) {
|
|
if (strcmp(dep_config_params[indx].name,webui->uri_parm1) == 0){
|
|
snprintf(temp_name, WEBUI_LEN_PARM, "%s", dep_config_params[indx].newname);
|
|
break;
|
|
}
|
|
indx++;
|
|
}
|
|
/* Ignore any request to change an option that is designated above the
|
|
* webcontrol_parms level.
|
|
*/
|
|
indx=0;
|
|
while (config_params[indx].param_name != NULL) {
|
|
if (((webui->thread_nbr != 0) && (config_params[indx].main_thread)) ||
|
|
(config_params[indx].webui_level > webui->cntlst[0]->conf.webcontrol_parms) ||
|
|
(config_params[indx].webui_level == WEBUI_LEVEL_NEVER) ) {
|
|
indx++;
|
|
continue;
|
|
}
|
|
if (!strcmp(temp_name, config_params[indx].param_name)) break;
|
|
indx++;
|
|
}
|
|
/* If we found the parm, assign it. If the loop above did not find the parm
|
|
* then we ignore the request
|
|
*/
|
|
if (config_params[indx].param_name != NULL){
|
|
if (strlen(webui->uri_parm1) > 0){
|
|
/* This is legacy assumption on the pointers being sequential
|
|
* We send in the original parm name so it will trigger the depreciated warnings
|
|
* and perform any required transformations from old parm to new parm
|
|
*/
|
|
conf_cmdparse(webui->cntlst + webui->thread_nbr
|
|
, webui->uri_parm1, webui->uri_value1);
|
|
|
|
/*If we are updating vid parms, set the flag to update the device.*/
|
|
if (!strcmp(config_params[indx].param_name, "vid_control_params") &&
|
|
(webui->cntlst[webui->thread_nbr]->vdev != NULL)){
|
|
webui->cntlst[webui->thread_nbr]->vdev->update_parms = TRUE;
|
|
}
|
|
|
|
/* If changing language, do it now */
|
|
if (!strcmp(config_params[indx].param_name, "native_language")){
|
|
nls_enabled = webui->cntlst[webui->thread_nbr]->conf.native_language;
|
|
if (nls_enabled){
|
|
MOTION_LOG(INF, TYPE_ALL, NO_ERRNO,_("Native Language : on"));
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_ALL, NO_ERRNO,_("Native Language : off"));
|
|
}
|
|
}
|
|
} else {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO,_("Set the value to null/zero"));
|
|
}
|
|
retcd = 0;
|
|
} else {
|
|
retcd = -1;
|
|
}
|
|
|
|
return retcd;
|
|
|
|
}
|
|
|
|
int webu_process_config(struct webui_ctx *webui) {
|
|
|
|
int retcd;
|
|
|
|
retcd = 0;
|
|
|
|
if ((!strcmp(webui->uri_cmd1,"config")) &&
|
|
(!strcmp(webui->uri_cmd2,"set"))) {
|
|
retcd = webu_process_config_set(webui);
|
|
|
|
} else if ((!strcmp(webui->uri_cmd1,"config")) &&
|
|
(!strcmp(webui->uri_cmd2,"get"))) {
|
|
webu_text_get_query(webui);
|
|
|
|
} else if ((!strcmp(webui->uri_cmd1,"config")) &&
|
|
(!strcmp(webui->uri_cmd2,"list"))) {
|
|
webu_text_list(webui);
|
|
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO,
|
|
_("Invalid action requested: >%s< >%s< >%s<")
|
|
, webui->uri_camid, webui->uri_cmd1, webui->uri_cmd2);
|
|
|
|
}
|
|
|
|
return retcd;
|
|
|
|
}
|
|
|
|
int webu_process_track(struct webui_ctx *webui) {
|
|
/* Call the tracking move functions as requested */
|
|
struct coord cent;
|
|
int retcd;
|
|
|
|
if (!strcmp(webui->uri_cmd2, "center")) {
|
|
webui->cntlst[webui->thread_nbr]->moved = track_center(webui->cntlst[webui->thread_nbr], 0, 1, 0, 0);
|
|
retcd = 0;
|
|
} else if (!strcmp(webui->uri_cmd2, "set")) {
|
|
if (!strcmp(webui->uri_parm1, "pan")) {
|
|
cent.width = webui->cntlst[webui->thread_nbr]->imgs.width;
|
|
cent.height = webui->cntlst[webui->thread_nbr]->imgs.height;
|
|
cent.x = atoi(webui->uri_value1);
|
|
cent.y = 0;
|
|
webui->cntlst[webui->thread_nbr]->moved = track_move(webui->cntlst[webui->thread_nbr]
|
|
,webui->cntlst[webui->thread_nbr]->video_dev
|
|
,¢, &webui->cntlst[webui->thread_nbr]->imgs, 1);
|
|
|
|
cent.width = webui->cntlst[webui->thread_nbr]->imgs.width;
|
|
cent.height = webui->cntlst[webui->thread_nbr]->imgs.height;
|
|
cent.x = 0;
|
|
cent.y = atoi(webui->uri_value2);
|
|
webui->cntlst[webui->thread_nbr]->moved = track_move(webui->cntlst[webui->thread_nbr]
|
|
,webui->cntlst[webui->thread_nbr]->video_dev
|
|
,¢, &webui->cntlst[webui->thread_nbr]->imgs, 1);
|
|
retcd = 0;
|
|
} else if (!strcasecmp(webui->uri_parm1, "x")) {
|
|
webui->cntlst[webui->thread_nbr]->moved = track_center(webui->cntlst[webui->thread_nbr]
|
|
, webui->cntlst[webui->thread_nbr]->video_dev, 1
|
|
, atoi(webui->uri_value1), atoi(webui->uri_value2));
|
|
retcd = 0;
|
|
} else {
|
|
retcd = -1;
|
|
}
|
|
} else {
|
|
retcd = -1;
|
|
}
|
|
|
|
return retcd;
|
|
|
|
}
|
|
|
|
static void webu_clientip(struct webui_ctx *webui) {
|
|
/* Extract the IP of the client that is connecting. When the
|
|
* user specifies Motion to use IPV6 and a IPV4 address comes to us
|
|
* the IPv4 address is prepended with a ::ffff: We then trim that off
|
|
* so we don't confuse our users.
|
|
*/
|
|
const union MHD_ConnectionInfo *con_info;
|
|
char client[WEBUI_LEN_URLI];
|
|
const char *ip_dst;
|
|
struct sockaddr_in6 *con_socket6;
|
|
struct sockaddr_in *con_socket4;
|
|
int is_ipv6;
|
|
|
|
is_ipv6 = FALSE;
|
|
if (webui->cnt != NULL ){
|
|
if (webui->cnt->conf.webcontrol_ipv6) is_ipv6 = TRUE;
|
|
} else {
|
|
if (webui->cntlst[0]->conf.webcontrol_ipv6) is_ipv6 = TRUE;
|
|
}
|
|
|
|
con_info = MHD_get_connection_info(webui->connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
|
|
if (is_ipv6){
|
|
con_socket6 = (struct sockaddr_in6 *)con_info->client_addr;
|
|
ip_dst = inet_ntop(AF_INET6, &con_socket6->sin6_addr, client, WEBUI_LEN_URLI);
|
|
if (ip_dst == NULL){
|
|
snprintf(webui->clientip, WEBUI_LEN_URLI, "%s", "Unknown");
|
|
} else {
|
|
if (strncmp(client,"::ffff:",7) == 0){
|
|
snprintf(webui->clientip, WEBUI_LEN_URLI, "%s", client + 7);
|
|
} else {
|
|
snprintf(webui->clientip, WEBUI_LEN_URLI, "%s", client);
|
|
}
|
|
}
|
|
} else {
|
|
con_socket4 = (struct sockaddr_in *)con_info->client_addr;
|
|
ip_dst = inet_ntop(AF_INET, &con_socket4->sin_addr, client, WEBUI_LEN_URLI);
|
|
if (ip_dst == NULL){
|
|
snprintf(webui->clientip, WEBUI_LEN_URLI, "%s", "Unknown");
|
|
} else {
|
|
snprintf(webui->clientip,WEBUI_LEN_URLI,"%s",client);
|
|
}
|
|
}
|
|
MOTION_LOG(INF,TYPE_ALL, NO_ERRNO, _("Connection from: %s"),webui->clientip);
|
|
|
|
}
|
|
|
|
static void webu_hostname(struct webui_ctx *webui, int ctrl) {
|
|
|
|
/* use the hostname the browser used to connect to us when
|
|
* constructing links to the stream ports. If available
|
|
* (which it is in all modern browsers) it is more likely to
|
|
* work than the result of gethostname(), which is reliant on
|
|
* the machine we're running on having it's hostname setup
|
|
* correctly and corresponding DNS in place. */
|
|
|
|
const char *hdr;
|
|
char *en_pos;
|
|
int host_len;
|
|
|
|
hdr = MHD_lookup_connection_value (webui->connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST);
|
|
if (hdr != NULL){
|
|
snprintf(webui->hostname, WEBUI_LEN_PARM, "%s", hdr);
|
|
/* IPv6 addresses have :'s in them so special case them */
|
|
if (webui->hostname[0] == '['){
|
|
en_pos = strstr(webui->hostname, "]");
|
|
if (en_pos != NULL){
|
|
host_len = en_pos - webui->hostname + 2;
|
|
snprintf(webui->hostname, host_len, "%s", hdr);
|
|
}
|
|
} else {
|
|
en_pos = strstr(webui->hostname, ":");
|
|
if (en_pos != NULL){
|
|
host_len = en_pos - webui->hostname + 1;
|
|
snprintf(webui->hostname, host_len, "%s", hdr);
|
|
}
|
|
}
|
|
} else {
|
|
gethostname(webui->hostname, WEBUI_LEN_PARM - 1);
|
|
}
|
|
|
|
/* Assign the type of protocol that is associated with the host
|
|
* so we can use this protocol as we are building the html page or
|
|
* streams.
|
|
*/
|
|
if (ctrl){
|
|
if (webui->cnt->conf.webcontrol_tls){
|
|
snprintf(webui->hostproto,6,"%s","https");
|
|
} else {
|
|
snprintf(webui->hostproto,6,"%s","http");
|
|
}
|
|
} else {
|
|
if (webui->cnt->conf.stream_tls){
|
|
snprintf(webui->hostproto,6,"%s","https");
|
|
} else {
|
|
snprintf(webui->hostproto,6,"%s","http");
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int webu_mhd_digest_fail(struct webui_ctx *webui,int signal_stale) {
|
|
/* Create a denied response to user*/
|
|
struct MHD_Response *response;
|
|
int retcd;
|
|
|
|
webui->authenticated = FALSE;
|
|
|
|
response = MHD_create_response_from_buffer(strlen(webui->auth_denied)
|
|
,(void *)webui->auth_denied, MHD_RESPMEM_PERSISTENT);
|
|
|
|
if (response == NULL) return MHD_NO;
|
|
|
|
retcd = MHD_queue_auth_fail_response(webui->connection, webui->auth_realm
|
|
,webui->auth_opaque, response
|
|
,(signal_stale == MHD_INVALID_NONCE) ? MHD_YES : MHD_NO);
|
|
|
|
MHD_destroy_response(response);
|
|
|
|
return retcd;
|
|
}
|
|
|
|
static int webu_mhd_digest(struct webui_ctx *webui) {
|
|
/* Perform the digest authentication. This function gets called a couple of
|
|
* times by MHD during the authentication process.
|
|
*/
|
|
int retcd;
|
|
char *user;
|
|
|
|
/*Get username or prompt for a user/pass */
|
|
user = MHD_digest_auth_get_username(webui->connection);
|
|
if (user == NULL) {
|
|
return webu_mhd_digest_fail(webui, MHD_NO);
|
|
}
|
|
|
|
/* Check for valid user name */
|
|
if (strcmp(user, webui->auth_user) != 0){
|
|
MOTION_LOG(ALR, TYPE_STREAM, NO_ERRNO
|
|
,_("Failed authentication from %s"), webui->clientip);
|
|
if (user != NULL) free(user);
|
|
return webu_mhd_digest_fail(webui, MHD_NO);
|
|
}
|
|
if (user != NULL) free(user);
|
|
|
|
/* Check the password as well*/
|
|
retcd = MHD_digest_auth_check(webui->connection, webui->auth_realm
|
|
, webui->auth_user, webui->auth_pass, 300);
|
|
|
|
if (retcd == MHD_NO) {
|
|
MOTION_LOG(ALR, TYPE_STREAM, NO_ERRNO
|
|
,_("Failed authentication from %s"), webui->clientip);
|
|
}
|
|
|
|
if ( (retcd == MHD_INVALID_NONCE) || (retcd == MHD_NO) ) {
|
|
return webu_mhd_digest_fail(webui, retcd);
|
|
}
|
|
|
|
webui->authenticated = TRUE;
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
static int webu_mhd_basic_fail(struct webui_ctx *webui) {
|
|
/* Create a denied response to user*/
|
|
struct MHD_Response *response;
|
|
int retcd;
|
|
|
|
webui->authenticated = FALSE;
|
|
|
|
response = MHD_create_response_from_buffer(strlen(webui->auth_denied)
|
|
,(void *)webui->auth_denied, MHD_RESPMEM_PERSISTENT);
|
|
|
|
if (response == NULL) return MHD_NO;
|
|
|
|
retcd = MHD_queue_basic_auth_fail_response (webui->connection, webui->auth_realm, response);
|
|
|
|
MHD_destroy_response(response);
|
|
|
|
return retcd;
|
|
|
|
}
|
|
|
|
static int webu_mhd_basic(struct webui_ctx *webui) {
|
|
/* Perform Basic Authentication. */
|
|
char *user, *pass;
|
|
|
|
pass = NULL;
|
|
user = NULL;
|
|
|
|
user = MHD_basic_auth_get_username_password (webui->connection, &pass);
|
|
if ((user == NULL) || (pass == NULL)){
|
|
if (user != NULL) free(user);
|
|
if (pass != NULL) free(pass);
|
|
return webu_mhd_basic_fail(webui);
|
|
}
|
|
|
|
if ((strcmp(user, webui->auth_user) != 0) || (strcmp(pass, webui->auth_pass) != 0)) {
|
|
MOTION_LOG(ALR, TYPE_STREAM, NO_ERRNO
|
|
,_("Failed authentication from %s"),webui->clientip);
|
|
if (user != NULL) free(user);
|
|
if (pass != NULL) free(pass);
|
|
|
|
return webu_mhd_basic_fail(webui);
|
|
}
|
|
|
|
if (user != NULL) free(user);
|
|
if (pass != NULL) free(pass);
|
|
|
|
webui->authenticated = TRUE;
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
static void webu_mhd_auth_parse(struct webui_ctx *webui, int ctrl){
|
|
int auth_len;
|
|
char *col_pos;
|
|
|
|
/* Parse apart the user:pass provided*/
|
|
if (webui->auth_user != NULL) free(webui->auth_user);
|
|
if (webui->auth_pass != NULL) free(webui->auth_pass);
|
|
webui->auth_user = NULL;
|
|
webui->auth_pass = NULL;
|
|
|
|
if (ctrl){
|
|
auth_len = strlen(webui->cnt->conf.webcontrol_authentication);
|
|
col_pos = strstr(webui->cnt->conf.webcontrol_authentication,":");
|
|
if (col_pos == NULL){
|
|
webui->auth_user = mymalloc(auth_len+1);
|
|
webui->auth_pass = mymalloc(2);
|
|
snprintf(webui->auth_user, auth_len + 1, "%s"
|
|
,webui->cnt->conf.webcontrol_authentication);
|
|
snprintf(webui->auth_pass, 2, "%s","");
|
|
} else {
|
|
webui->auth_user = mymalloc(auth_len - strlen(col_pos) + 1);
|
|
webui->auth_pass = mymalloc(strlen(col_pos));
|
|
snprintf(webui->auth_user, auth_len - strlen(col_pos) + 1, "%s"
|
|
,webui->cnt->conf.webcontrol_authentication);
|
|
snprintf(webui->auth_pass, strlen(col_pos), "%s", col_pos + 1);
|
|
}
|
|
} else {
|
|
auth_len = strlen(webui->cnt->conf.stream_authentication);
|
|
col_pos = strstr(webui->cnt->conf.stream_authentication,":");
|
|
if (col_pos == NULL){
|
|
webui->auth_user = mymalloc(auth_len+1);
|
|
webui->auth_pass = mymalloc(2);
|
|
snprintf(webui->auth_user, auth_len + 1, "%s"
|
|
,webui->cnt->conf.stream_authentication);
|
|
snprintf(webui->auth_pass, 2, "%s","");
|
|
} else {
|
|
webui->auth_user = mymalloc(auth_len - strlen(col_pos) + 1);
|
|
webui->auth_pass = mymalloc(strlen(col_pos));
|
|
snprintf(webui->auth_user, auth_len - strlen(col_pos) + 1, "%s"
|
|
,webui->cnt->conf.stream_authentication);
|
|
snprintf(webui->auth_pass, strlen(col_pos), "%s", col_pos + 1);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static int webu_mhd_auth(struct webui_ctx *webui, int ctrl){
|
|
|
|
/* Set everything up for calling the authentication functions */
|
|
unsigned int rand1,rand2;
|
|
|
|
snprintf(webui->auth_denied, WEBUI_LEN_RESP, "%s"
|
|
,"<html><head><title>Access denied</title>"
|
|
"</head><body>Access denied</body></html>");
|
|
|
|
srand(time(NULL));
|
|
rand1 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
|
|
rand2 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
|
|
snprintf(webui->auth_opaque, WEBUI_LEN_PARM, "%08x%08x", rand1, rand2);
|
|
|
|
snprintf(webui->auth_realm, WEBUI_LEN_PARM, "%s","Motion");
|
|
|
|
if (ctrl){
|
|
/* Authentication for the webcontrol*/
|
|
if (webui->cnt->conf.webcontrol_authentication == NULL){
|
|
webui->authenticated = TRUE;
|
|
if (webui->cnt->conf.webcontrol_auth_method != 0){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("No webcontrol user:pass provided"));
|
|
}
|
|
return MHD_YES;
|
|
}
|
|
|
|
if (webui->auth_user == NULL) webu_mhd_auth_parse(webui, ctrl);
|
|
|
|
if (webui->cnt->conf.webcontrol_auth_method == 1){
|
|
return webu_mhd_basic(webui);
|
|
} else if (webui->cnt->conf.webcontrol_auth_method == 2){
|
|
return webu_mhd_digest(webui);
|
|
}
|
|
|
|
} else {
|
|
/* Authentication for the streams */
|
|
if (webui->cnt->conf.stream_authentication == NULL){
|
|
webui->authenticated = TRUE;
|
|
if (webui->cnt->conf.stream_auth_method != 0){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("No stream user:pass provided"));
|
|
}
|
|
return MHD_YES;
|
|
}
|
|
|
|
if (webui->auth_user == NULL) webu_mhd_auth_parse(webui, ctrl);
|
|
|
|
if (webui->cnt->conf.stream_auth_method == 1) {
|
|
return webu_mhd_basic(webui);
|
|
} else if (webui->cnt->conf.stream_auth_method == 2){
|
|
return webu_mhd_digest(webui);
|
|
}
|
|
}
|
|
|
|
webui->authenticated = TRUE;
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
static int webu_mhd_send(struct webui_ctx *webui, int ctrl) {
|
|
/* Send the response that we created back to the user. Now if the user
|
|
* provided a really bad URL, then we couldn't determine which Motion context
|
|
* they were wanting. In this situation, we have a webui->cnt = NULL and we
|
|
* don't know whether it came from a html or text request. In this situation
|
|
* we use the MHD defaults and skip adding CORS/Content type. (There isn't any
|
|
* Motion context so we can't tell where to look)
|
|
* The ctrl parameter is a boolean which just says whether the request is for
|
|
* the webcontrol versus stream
|
|
*/
|
|
int retcd;
|
|
struct MHD_Response *response;
|
|
|
|
response = MHD_create_response_from_buffer (strlen(webui->resp_page)
|
|
,(void *)webui->resp_page, MHD_RESPMEM_PERSISTENT);
|
|
if (!response){
|
|
MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO, _("Invalid response"));
|
|
return MHD_NO;
|
|
}
|
|
|
|
if (webui->cnt != NULL){
|
|
if (ctrl){
|
|
if (webui->cnt->conf.webcontrol_cors_header != NULL){
|
|
MHD_add_response_header (response, MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
|
|
, webui->cnt->conf.webcontrol_cors_header);
|
|
}
|
|
if (webui->cnt->conf.webcontrol_interface == 1){
|
|
MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain;");
|
|
} else {
|
|
MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");
|
|
}
|
|
} else {
|
|
if (webui->cnt->conf.stream_cors_header != NULL){
|
|
MHD_add_response_header (response, MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
|
|
, webui->cnt->conf.stream_cors_header);
|
|
}
|
|
MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html");
|
|
}
|
|
}
|
|
|
|
retcd = MHD_queue_response (webui->connection, MHD_HTTP_OK, response);
|
|
MHD_destroy_response (response);
|
|
|
|
return retcd;
|
|
}
|
|
|
|
static void webu_answer_strm_type(struct webui_ctx *webui) {
|
|
/* Assign the type of stream that is being answered*/
|
|
|
|
if ((strcmp(webui->uri_cmd1,"stream") == 0) ||
|
|
(strcmp(webui->uri_camid,"stream") == 0) ||
|
|
(strlen(webui->uri_camid) == 0)) {
|
|
webui->cnct_type = WEBUI_CNCT_FULL;
|
|
|
|
} else if ((strcmp(webui->uri_cmd1,"substream") == 0) ||
|
|
(strcmp(webui->uri_camid,"substream") == 0)){
|
|
webui->cnct_type = WEBUI_CNCT_SUB;
|
|
|
|
} else if ((strcmp(webui->uri_cmd1,"motion") == 0) ||
|
|
(strcmp(webui->uri_camid,"motion") == 0)){
|
|
webui->cnct_type = WEBUI_CNCT_MOTION;
|
|
|
|
} else if ((strcmp(webui->uri_cmd1,"source") == 0) ||
|
|
(strcmp(webui->uri_camid,"source") == 0)){
|
|
webui->cnct_type = WEBUI_CNCT_SOURCE;
|
|
|
|
} else if ((strcmp(webui->uri_cmd1,"current") == 0) ||
|
|
(strcmp(webui->uri_camid,"current") == 0)){
|
|
webui->cnct_type = WEBUI_CNCT_STATIC;
|
|
|
|
} else if ((strlen(webui->uri_camid) > 0) &&
|
|
(strlen(webui->uri_cmd1) == 0)){
|
|
webui->cnct_type = WEBUI_CNCT_FULL;
|
|
|
|
} else {
|
|
webui->cnct_type = WEBUI_CNCT_UNKNOWN;
|
|
}
|
|
|
|
}
|
|
|
|
static int 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) {
|
|
|
|
/* This function "answers" the request for a webcontrol.*/
|
|
int retcd;
|
|
struct webui_ctx *webui = *ptr;
|
|
|
|
/* Eliminate compiler warnings */
|
|
(void)cls;
|
|
(void)url;
|
|
(void)version;
|
|
(void)upload_data;
|
|
(void)upload_data_size;
|
|
|
|
/* Per MHD docs, this is called twice and we should process the second call */
|
|
if (webui->mhd_first) {
|
|
webui->mhd_first = FALSE;
|
|
return MHD_YES;
|
|
}
|
|
|
|
if (strcmp (method, "GET") != 0){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Invalid Method requested: %s"),method);
|
|
return MHD_NO;
|
|
}
|
|
|
|
webui->cnct_type = WEBUI_CNCT_CONTROL;
|
|
|
|
util_threadname_set("wu", 0,NULL);
|
|
|
|
webui->connection = connection;
|
|
|
|
/* Throw bad URLS back to user*/
|
|
if ((webui->cnt == NULL) || (strlen(webui->url) == 0)){
|
|
webu_badreq(webui);
|
|
retcd = webu_mhd_send(webui, FALSE);
|
|
return retcd;
|
|
}
|
|
|
|
if (webui->cnt->webcontrol_finish) return MHD_NO;
|
|
|
|
if (strlen(webui->clientip) == 0){
|
|
webu_clientip(webui);
|
|
}
|
|
|
|
webu_hostname(webui, TRUE);
|
|
|
|
if (!webui->authenticated) {
|
|
retcd = webu_mhd_auth(webui, TRUE);
|
|
if (!webui->authenticated) return retcd;
|
|
}
|
|
|
|
if ((webui->cntlst[0]->conf.webcontrol_interface == 1) ||
|
|
(webui->cntlst[0]->conf.webcontrol_interface == 2)) {
|
|
webu_text_main(webui);
|
|
} else {
|
|
webu_html_main(webui);
|
|
}
|
|
|
|
retcd = webu_mhd_send(webui, TRUE);
|
|
if (retcd == MHD_NO){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("send page failed %d"),retcd);
|
|
}
|
|
return retcd;
|
|
|
|
}
|
|
|
|
static int 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) {
|
|
|
|
/* Answer the request for all the streams*/
|
|
int retcd;
|
|
struct webui_ctx *webui = *ptr;
|
|
|
|
/* Eliminate compiler warnings */
|
|
(void)cls;
|
|
(void)url;
|
|
(void)version;
|
|
(void)upload_data;
|
|
(void)upload_data_size;
|
|
|
|
/* Per docs, this is called twice and we should process the second call */
|
|
if (webui->mhd_first) {
|
|
webui->mhd_first = FALSE;
|
|
return MHD_YES;
|
|
}
|
|
|
|
/* Do not answer a request until the motion loop has completed at least once */
|
|
if (webui->cnt->passflag == 0) return MHD_NO;
|
|
|
|
if (strcmp (method, "GET") != 0){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Invalid Method requested: %s"),method);
|
|
return MHD_NO;
|
|
}
|
|
|
|
util_threadname_set("st", 0,NULL);
|
|
|
|
webui->connection = connection;
|
|
|
|
/* Throw bad URLS back to user*/
|
|
if ((webui->cnt == NULL) || (strlen(webui->url) == 0)){
|
|
webu_badreq(webui);
|
|
retcd = webu_mhd_send(webui, FALSE);
|
|
return retcd;
|
|
}
|
|
|
|
if (webui->cnt->webcontrol_finish) return MHD_NO;
|
|
|
|
if (strlen(webui->clientip) == 0){
|
|
webu_clientip(webui);
|
|
}
|
|
|
|
webu_hostname(webui, FALSE);
|
|
|
|
if (!webui->authenticated) {
|
|
retcd = webu_mhd_auth(webui, FALSE);
|
|
if (!webui->authenticated) return retcd;
|
|
}
|
|
|
|
webu_answer_strm_type(webui);
|
|
|
|
retcd = 0;
|
|
if (webui->cnct_type == WEBUI_CNCT_STATIC){
|
|
retcd = webu_stream_static(webui);
|
|
if (retcd == MHD_NO){
|
|
webu_badreq(webui);
|
|
retcd = webu_mhd_send(webui, FALSE);
|
|
}
|
|
} else if (webui->cnct_type != WEBUI_CNCT_UNKNOWN) {
|
|
retcd = webu_stream_mjpeg(webui);
|
|
if (retcd == MHD_NO){
|
|
webu_badreq(webui);
|
|
retcd = webu_mhd_send(webui, FALSE);
|
|
}
|
|
} else {
|
|
webu_badreq(webui);
|
|
retcd = webu_mhd_send(webui, FALSE);
|
|
}
|
|
|
|
if (retcd == MHD_NO){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("send page failed %d"),retcd);
|
|
}
|
|
return retcd;
|
|
|
|
}
|
|
|
|
static void *webu_mhd_init(void *cls, const char *uri, struct MHD_Connection *connection) {
|
|
/* This is called at the very start of getting a request before the "answer"
|
|
* is processed. There are two variations of this and the difference is how
|
|
* we call the webu_context_init. When we are processing for the webcontrol or
|
|
* the stream port specified in the motion.conf file, we pass into the init function
|
|
* the full list of all the cameras. The other version of the init is used when the
|
|
* user specifies a unique port for each camera. In this situation, the full list
|
|
* context is passed in as a null and the context of the camera desired is passed
|
|
* instead.
|
|
* When this function is processed, we basically only have the URL that the user requested
|
|
* so we initialize everything and then parse out the URL to determine what the user is
|
|
* asking.
|
|
*/
|
|
|
|
struct context **cnt = cls;
|
|
struct webui_ctx *webui;
|
|
int retcd;
|
|
|
|
(void)connection;
|
|
|
|
/* Set the thread name to connection until we know whether control or stream answers*/
|
|
util_threadname_set("cn", 0,NULL);
|
|
|
|
webui = malloc(sizeof(struct webui_ctx));
|
|
|
|
webu_context_init(cnt, NULL, webui);
|
|
webui->mhd_first = TRUE;
|
|
|
|
snprintf(webui->url,WEBUI_LEN_URLI,"%s",uri);
|
|
|
|
retcd = webu_parseurl(webui);
|
|
if (retcd != 0){
|
|
webu_parseurl_reset(webui);
|
|
memset(webui->url,'\0',WEBUI_LEN_URLI);
|
|
}
|
|
|
|
webu_parms_edit(webui);
|
|
|
|
return webui;
|
|
}
|
|
|
|
static void *webu_mhd_init_one(void *cls, const char *uri, struct MHD_Connection *connection) {
|
|
/* This function initializes all the webui variables as we are getting a request. This
|
|
* variation of the init is the one used when the user has specified a unique port number
|
|
* for each camera. The variation is in how the webu_context_init is invoked. This passes
|
|
* in a NULL for the full context list (webui->cntlist) and instead assigns the particular
|
|
* camera context to webui->cnt
|
|
*/
|
|
struct context *cnt = cls;
|
|
struct webui_ctx *webui;
|
|
int retcd;
|
|
|
|
(void)connection;
|
|
|
|
/* Set the thread name to connection until we know whether control or stream answers*/
|
|
util_threadname_set("cn", 0,NULL);
|
|
|
|
webui = malloc(sizeof(struct webui_ctx));
|
|
|
|
webu_context_init(NULL, cnt, webui);
|
|
webui->mhd_first = TRUE;
|
|
|
|
snprintf(webui->url,WEBUI_LEN_URLI,"%s",uri);
|
|
|
|
retcd = webu_parseurl(webui);
|
|
if (retcd != 0){
|
|
webu_parseurl_reset(webui);
|
|
memset(webui->url,'\0',WEBUI_LEN_URLI);
|
|
}
|
|
|
|
webu_parms_edit(webui);
|
|
|
|
return webui;
|
|
}
|
|
|
|
static void webu_mhd_deinit(void *cls
|
|
, struct MHD_Connection *connection
|
|
, void **con_cls
|
|
, enum MHD_RequestTerminationCode toe) {
|
|
/* This is the function called as the connection is closed so we free our webui variables*/
|
|
struct webui_ctx *webui = *con_cls;
|
|
|
|
/* Eliminate compiler warnings */
|
|
(void)connection;
|
|
(void)cls;
|
|
(void)toe;
|
|
|
|
if (webui->cnct_type == WEBUI_CNCT_FULL ){
|
|
pthread_mutex_lock(&webui->cnt->mutex_stream);
|
|
webui->cnt->stream_norm.cnct_count--;
|
|
pthread_mutex_unlock(&webui->cnt->mutex_stream);
|
|
|
|
} else if (webui->cnct_type == WEBUI_CNCT_SUB ){
|
|
pthread_mutex_lock(&webui->cnt->mutex_stream);
|
|
webui->cnt->stream_sub.cnct_count--;
|
|
pthread_mutex_unlock(&webui->cnt->mutex_stream);
|
|
|
|
} else if (webui->cnct_type == WEBUI_CNCT_MOTION ){
|
|
pthread_mutex_lock(&webui->cnt->mutex_stream);
|
|
webui->cnt->stream_motion.cnct_count--;
|
|
pthread_mutex_unlock(&webui->cnt->mutex_stream);
|
|
|
|
} else if (webui->cnct_type == WEBUI_CNCT_SOURCE ){
|
|
pthread_mutex_lock(&webui->cnt->mutex_stream);
|
|
webui->cnt->stream_source.cnct_count--;
|
|
pthread_mutex_unlock(&webui->cnt->mutex_stream);
|
|
|
|
} else if (webui->cnct_type == WEBUI_CNCT_STATIC ){
|
|
pthread_mutex_lock(&webui->cnt->mutex_stream);
|
|
webui->cnt->stream_norm.cnct_count--;
|
|
pthread_mutex_unlock(&webui->cnt->mutex_stream);
|
|
|
|
}
|
|
|
|
webu_context_free(webui);
|
|
|
|
return;
|
|
}
|
|
|
|
static void webu_mhd_features_basic(struct mhdstart_ctx *mhdst){
|
|
/* Use the MHD function to see what features it supports*/
|
|
#if MHD_VERSION < 0x00094400
|
|
(void)mhdst;
|
|
#else
|
|
int retcd;
|
|
retcd = MHD_is_feature_supported (MHD_FEATURE_BASIC_AUTH);
|
|
if (retcd == MHD_YES){
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO ,_("Basic authentication: available"));
|
|
} else {
|
|
if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_auth_method == 1)){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Basic authentication: disabled"));
|
|
mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_auth_method = 0;
|
|
} else if ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_auth_method == 1)){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Basic authentication: disabled"));
|
|
mhdst->cnt[mhdst->indxthrd]->conf.stream_auth_method = 0;
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("Basic authentication: disabled"));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void webu_mhd_features_digest(struct mhdstart_ctx *mhdst){
|
|
/* Use the MHD function to see what features it supports*/
|
|
#if MHD_VERSION < 0x00094400
|
|
(void)mhdst;
|
|
#else
|
|
int retcd;
|
|
retcd = MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH);
|
|
if (retcd == MHD_YES){
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO ,_("Digest authentication: available"));
|
|
} else {
|
|
if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_auth_method == 2)){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Digest authentication: disabled"));
|
|
mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_auth_method = 0;
|
|
} else if ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_auth_method == 2)){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Digest authentication: disabled"));
|
|
mhdst->cnt[mhdst->indxthrd]->conf.stream_auth_method = 0;
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("Digest authentication: disabled"));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void webu_mhd_features_ipv6(struct mhdstart_ctx *mhdst){
|
|
/* Use the MHD function to see what features it supports
|
|
* If we have a really old version of MHD, then we will just support
|
|
* IPv4
|
|
*/
|
|
#if MHD_VERSION < 0x00094400
|
|
if (mhdst->ipv6){
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("libmicrohttpd libary too old ipv6 disabled"));
|
|
if (mhdst->ipv6) mhdst->ipv6 = 0;
|
|
}
|
|
#else
|
|
int retcd;
|
|
retcd = MHD_is_feature_supported (MHD_FEATURE_IPv6);
|
|
if (retcd == MHD_YES){
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO ,_("IPV6: available"));
|
|
} else {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("IPV6: disabled"));
|
|
if (mhdst->ipv6) mhdst->ipv6 = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void webu_mhd_features_tls(struct mhdstart_ctx *mhdst){
|
|
/* Use the MHD function to see what features it supports
|
|
* If we have a really old version of MHD, then we will will not
|
|
* support the ssl/tls request.
|
|
*/
|
|
#if MHD_VERSION < 0x00094400
|
|
if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls)){
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("libmicrohttpd libary too old SSL/TLS disabled"));
|
|
mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls = 0;
|
|
} else if ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_tls)) {
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("libmicrohttpd libary too old SSL/TLS disabled"));
|
|
mhdst->cnt[mhdst->indxthrd]->conf.stream_tls = 0;
|
|
}
|
|
#else
|
|
int retcd;
|
|
retcd = MHD_is_feature_supported (MHD_FEATURE_SSL);
|
|
if (retcd == MHD_YES){
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO ,_("SSL/TLS: available"));
|
|
} else {
|
|
if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls)){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("SSL/TLS: disabled"));
|
|
mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls = 0;
|
|
} else if ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_tls)){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("SSL/TLS: disabled"));
|
|
mhdst->cnt[mhdst->indxthrd]->conf.stream_tls = 0;
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("SSL/TLS: disabled"));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void webu_mhd_features(struct mhdstart_ctx *mhdst){
|
|
/* This function goes through at least a few of the MHD features
|
|
* and adjusts the user parameters from the configuration as
|
|
* needed to reflect what MHD can do
|
|
*/
|
|
|
|
webu_mhd_features_basic(mhdst);
|
|
|
|
webu_mhd_features_digest(mhdst);
|
|
|
|
webu_mhd_features_ipv6(mhdst);
|
|
|
|
webu_mhd_features_tls(mhdst);
|
|
|
|
}
|
|
|
|
static char *webu_mhd_loadfile(const char *fname){
|
|
/* This function loads the requested certificate and key files into memory so we
|
|
* can use them as needed if the user wants ssl/tls support. If the user did not
|
|
* specify a file in the configuration, then we return NULL.
|
|
*/
|
|
FILE *infile;
|
|
size_t file_size, read_size;
|
|
char * file_char;
|
|
|
|
if (fname == NULL) {
|
|
file_char = NULL;
|
|
} else {
|
|
infile = fopen(fname, "rb");
|
|
if (infile != NULL){
|
|
fseek(infile, 0, SEEK_END);
|
|
file_size = ftell(infile);
|
|
if (file_size > 0 ){
|
|
file_char = mymalloc(file_size +1);
|
|
fseek(infile, 0, SEEK_SET);
|
|
read_size = fread(file_char, file_size, 1, infile);
|
|
if (read_size > 0 ){
|
|
file_char[file_size] = 0;
|
|
} else {
|
|
free(file_char);
|
|
file_char = NULL;
|
|
MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO
|
|
,_("Error reading file for SSL/TLS support."));
|
|
}
|
|
} else {
|
|
file_char = NULL;
|
|
}
|
|
fclose(infile);
|
|
} else {
|
|
file_char = NULL;
|
|
}
|
|
}
|
|
return file_char;
|
|
}
|
|
|
|
static void webu_mhd_checktls(struct mhdstart_ctx *mhdst){
|
|
/* This function validates that if the user requested a SSL/TLS connection, then
|
|
* they also need to provide a certificate and key file. If those are not provided
|
|
* then we revise the configuration request for ssl/tls
|
|
*/
|
|
if (mhdst->ctrl){
|
|
if (mhdst->cnt[0]->conf.webcontrol_tls){
|
|
if ((mhdst->cnt[0]->conf.webcontrol_cert == NULL) || (mhdst->tls_cert == NULL)) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("SSL/TLS requested but no cert file provided. SSL/TLS disabled"));
|
|
mhdst->cnt[0]->conf.webcontrol_tls = 0;
|
|
}
|
|
if ((mhdst->cnt[0]->conf.webcontrol_key == NULL) || (mhdst->tls_key == NULL)) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("SSL/TLS requested but no key file provided. SSL/TLS disabled"));
|
|
mhdst->cnt[0]->conf.webcontrol_tls = 0;
|
|
}
|
|
}
|
|
} else {
|
|
if (mhdst->cnt[mhdst->indxthrd]->conf.stream_tls){
|
|
if ((mhdst->cnt[0]->conf.webcontrol_cert == NULL) || (mhdst->tls_cert == NULL)) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("SSL/TLS requested but no cert file provided. SSL/TLS disabled"));
|
|
mhdst->cnt[mhdst->indxthrd]->conf.stream_tls = 0;
|
|
}
|
|
if ((mhdst->cnt[0]->conf.webcontrol_key == NULL) || (mhdst->tls_key == NULL)) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("SSL/TLS requested but no key file provided. SSL/TLS disabled"));
|
|
mhdst->cnt[mhdst->indxthrd]->conf.stream_tls = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void webu_mhd_opts_init(struct mhdstart_ctx *mhdst){
|
|
/* This function sets the init function to use for the MHD connection. If
|
|
* the connection is related to the webcontrol or the stream specified in the
|
|
* motion.conf file, then we pass in the full context list of all cameras. If
|
|
* the MHD connection is only going to be for a single camera (a unique port for
|
|
* each camera), then we call a different init function which only wants the single
|
|
* motion context for that particular camera.
|
|
*/
|
|
if ((!mhdst->ctrl) && (mhdst->indxthrd != 0)){
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_URI_LOG_CALLBACK;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = (intptr_t)webu_mhd_init_one;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->cnt[mhdst->indxthrd];
|
|
mhdst->mhd_opt_nbr++;
|
|
} else {
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_URI_LOG_CALLBACK;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = (intptr_t)webu_mhd_init;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->cnt;
|
|
mhdst->mhd_opt_nbr++;
|
|
}
|
|
|
|
}
|
|
|
|
static void webu_mhd_opts_deinit(struct mhdstart_ctx *mhdst){
|
|
/* Set the MHD option on the function to call when the connection closes */
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_NOTIFY_COMPLETED;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = (intptr_t)webu_mhd_deinit;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = NULL;
|
|
mhdst->mhd_opt_nbr++;
|
|
|
|
}
|
|
|
|
static void webu_mhd_opts_localhost(struct mhdstart_ctx *mhdst){
|
|
/* Set the MHD option on the acceptable connections. This is used to handle the
|
|
* motion configuation option of localhost only.
|
|
*/
|
|
|
|
if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_localhost)){
|
|
if (mhdst->ipv6){
|
|
memset(&mhdst->lpbk_ipv6, 0, sizeof(struct sockaddr_in6));
|
|
mhdst->lpbk_ipv6.sin6_family = AF_INET6;
|
|
mhdst->lpbk_ipv6.sin6_port = htons(mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_port);
|
|
mhdst->lpbk_ipv6.sin6_addr = in6addr_loopback;
|
|
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_SOCK_ADDR;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = (struct sosockaddr *)(&mhdst->lpbk_ipv6);
|
|
mhdst->mhd_opt_nbr++;
|
|
|
|
} else {
|
|
memset(&mhdst->lpbk_ipv4, 0, sizeof(struct sockaddr_in));
|
|
mhdst->lpbk_ipv4.sin_family = AF_INET;
|
|
mhdst->lpbk_ipv4.sin_port = htons(mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_port);
|
|
mhdst->lpbk_ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_SOCK_ADDR;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = (struct sockaddr *)(&mhdst->lpbk_ipv4);
|
|
mhdst->mhd_opt_nbr++;
|
|
}
|
|
} else if((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_localhost)){
|
|
if (mhdst->ipv6){
|
|
memset(&mhdst->lpbk_ipv6, 0, sizeof(struct sockaddr_in6));
|
|
mhdst->lpbk_ipv6.sin6_family = AF_INET6;
|
|
mhdst->lpbk_ipv6.sin6_port = htons(mhdst->cnt[mhdst->indxthrd]->conf.stream_port);
|
|
mhdst->lpbk_ipv6.sin6_addr = in6addr_loopback;
|
|
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_SOCK_ADDR;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = (struct sosockaddr *)(&mhdst->lpbk_ipv6);
|
|
mhdst->mhd_opt_nbr++;
|
|
} else {
|
|
memset(&mhdst->lpbk_ipv4, 0, sizeof(struct sockaddr_in));
|
|
mhdst->lpbk_ipv4.sin_family = AF_INET;
|
|
mhdst->lpbk_ipv4.sin_port = htons(mhdst->cnt[mhdst->indxthrd]->conf.stream_port);
|
|
mhdst->lpbk_ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_SOCK_ADDR;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = (struct sockaddr *)(&mhdst->lpbk_ipv4);
|
|
mhdst->mhd_opt_nbr++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void webu_mhd_opts_digest(struct mhdstart_ctx *mhdst){
|
|
/* Set the MHD option for the type of authentication that we will be using. This
|
|
* function is when we are wanting to use digest authentication
|
|
*/
|
|
|
|
if (((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_auth_method == 2)) ||
|
|
((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_auth_method == 2))) {
|
|
|
|
if (mhdst->ctrl) {
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_DIGEST_AUTH_RANDOM;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = sizeof(mhdst->cnt[mhdst->indxthrd]->webcontrol_digest_rand);
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->cnt[mhdst->indxthrd]->webcontrol_digest_rand;
|
|
mhdst->mhd_opt_nbr++;
|
|
} else {
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_DIGEST_AUTH_RANDOM;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = sizeof(mhdst->cnt[mhdst->indxthrd]->webstream_digest_rand);
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->cnt[mhdst->indxthrd]->webstream_digest_rand;
|
|
mhdst->mhd_opt_nbr++;
|
|
}
|
|
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_NONCE_NC_SIZE;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 300;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = NULL;
|
|
mhdst->mhd_opt_nbr++;
|
|
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_CONNECTION_TIMEOUT;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = (unsigned int) 120;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = NULL;
|
|
mhdst->mhd_opt_nbr++;
|
|
}
|
|
|
|
}
|
|
|
|
static void webu_mhd_opts_tls(struct mhdstart_ctx *mhdst){
|
|
/* Set the MHD options needed when we want TLS connections */
|
|
if ((( mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls)) ||
|
|
((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_tls))) {
|
|
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_HTTPS_MEM_CERT;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->tls_cert;
|
|
mhdst->mhd_opt_nbr++;
|
|
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_HTTPS_MEM_KEY;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->tls_key;
|
|
mhdst->mhd_opt_nbr++;
|
|
}
|
|
|
|
}
|
|
|
|
static void webu_mhd_opts(struct mhdstart_ctx *mhdst){
|
|
/* Set all the options we need based upon the motion configuration parameters*/
|
|
|
|
mhdst->mhd_opt_nbr = 0;
|
|
|
|
webu_mhd_checktls(mhdst);
|
|
|
|
webu_mhd_opts_deinit(mhdst);
|
|
|
|
webu_mhd_opts_init(mhdst);
|
|
|
|
webu_mhd_opts_localhost(mhdst);
|
|
|
|
webu_mhd_opts_digest(mhdst);
|
|
|
|
webu_mhd_opts_tls(mhdst);
|
|
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_END;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = 0;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = NULL;
|
|
mhdst->mhd_opt_nbr++;
|
|
|
|
}
|
|
|
|
static void webu_mhd_flags(struct mhdstart_ctx *mhdst){
|
|
|
|
/* This sets the MHD startup flags based upon what user put into configuration */
|
|
mhdst->mhd_flags = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL| MHD_USE_SELECT_INTERNALLY;
|
|
|
|
if (mhdst->ipv6) mhdst->mhd_flags = mhdst->mhd_flags | MHD_USE_DUAL_STACK;
|
|
|
|
if ((mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.webcontrol_tls)){
|
|
mhdst->mhd_flags = mhdst->mhd_flags | MHD_USE_SSL;
|
|
} else if ((!mhdst->ctrl) && (mhdst->cnt[mhdst->indxthrd]->conf.stream_tls)){
|
|
mhdst->mhd_flags = mhdst->mhd_flags | MHD_USE_SSL;
|
|
}
|
|
|
|
}
|
|
|
|
static void webu_start_ctrl(struct context **cnt){
|
|
/* This is the function that actually starts the MHD daemon for handling the webcontrol.
|
|
* There are many options for MHD and they will vary depending upon what our Motion user
|
|
* has requested in the configuration. There are many functions in this module to assign
|
|
* these options and they are passed in a pointer to the mhdst variable so that they can
|
|
* assign the correct values for MHD start up. Since this function is doing the webcontrol
|
|
* we are only using thread 0 values.
|
|
*/
|
|
|
|
struct mhdstart_ctx mhdst;
|
|
unsigned int randnbr;
|
|
|
|
mhdst.tls_cert = webu_mhd_loadfile(cnt[0]->conf.webcontrol_cert);
|
|
mhdst.tls_key = webu_mhd_loadfile(cnt[0]->conf.webcontrol_key);
|
|
mhdst.ctrl = TRUE;
|
|
mhdst.indxthrd = 0;
|
|
mhdst.cnt = cnt;
|
|
mhdst.ipv6 = cnt[0]->conf.webcontrol_ipv6;
|
|
|
|
/* Set the rand number for webcontrol digest if needed */
|
|
srand(time(NULL));
|
|
randnbr = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
|
|
snprintf(cnt[0]->webcontrol_digest_rand
|
|
,sizeof(cnt[0]->webcontrol_digest_rand),"%d",randnbr);
|
|
|
|
cnt[0]->webcontrol_daemon = NULL;
|
|
if (cnt[0]->conf.webcontrol_port != 0 ){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("Starting webcontrol on port %d")
|
|
,cnt[0]->conf.webcontrol_port);
|
|
|
|
mhdst.mhd_ops = malloc(sizeof(struct MHD_OptionItem)*WEBUI_MHD_OPTS);
|
|
webu_mhd_features(&mhdst);
|
|
webu_mhd_opts(&mhdst);
|
|
webu_mhd_flags(&mhdst);
|
|
|
|
cnt[0]->webcontrol_daemon = MHD_start_daemon (mhdst.mhd_flags
|
|
,cnt[0]->conf.webcontrol_port
|
|
,NULL, NULL
|
|
,&webu_answer_ctrl, cnt
|
|
,MHD_OPTION_ARRAY, mhdst.mhd_ops
|
|
,MHD_OPTION_END);
|
|
free(mhdst.mhd_ops);
|
|
if (cnt[0]->webcontrol_daemon == NULL){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Unable to start MHD"));
|
|
} else {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("Started webcontrol on port %d")
|
|
,cnt[0]->conf.webcontrol_port);
|
|
}
|
|
}
|
|
|
|
if (mhdst.tls_cert != NULL) free(mhdst.tls_cert);
|
|
if (mhdst.tls_key != NULL) free(mhdst.tls_key);
|
|
|
|
return;
|
|
}
|
|
|
|
static void webu_strm_ntc(struct context **cnt, int indxthrd){
|
|
int indx;
|
|
|
|
if (indxthrd == 0 ){
|
|
if (cnt[1] != NULL) {
|
|
indx = 1;
|
|
while (cnt[indx] != NULL){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("Started camera %d stream on port/camera_id %d/%d")
|
|
,cnt[indx]->camera_id
|
|
,cnt[indxthrd]->conf.stream_port
|
|
,cnt[indx]->camera_id);
|
|
indx++;
|
|
}
|
|
} else {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("Started camera %d stream on port %d")
|
|
,cnt[indxthrd]->camera_id,cnt[indxthrd]->conf.stream_port);
|
|
}
|
|
} else {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("Started camera %d stream on port %d")
|
|
,cnt[indxthrd]->camera_id,cnt[indxthrd]->conf.stream_port);
|
|
}
|
|
}
|
|
|
|
static void webu_start_strm(struct context **cnt){
|
|
/* This function starts up the daemon for the streams. It loops through
|
|
* all of the camera context's provided and starts streams as requested. If
|
|
* the thread number is zero, then it starts the full list stream context
|
|
*/
|
|
|
|
struct mhdstart_ctx mhdst;
|
|
unsigned int randnbr;
|
|
|
|
mhdst.tls_cert = webu_mhd_loadfile(cnt[0]->conf.webcontrol_cert);
|
|
mhdst.tls_key = webu_mhd_loadfile(cnt[0]->conf.webcontrol_key);
|
|
mhdst.ctrl = FALSE;
|
|
mhdst.indxthrd = 0;
|
|
mhdst.cnt = cnt;
|
|
mhdst.ipv6 = cnt[0]->conf.webcontrol_ipv6;
|
|
|
|
/* Set the rand number for webcontrol digest if needed */
|
|
srand(time(NULL));
|
|
randnbr = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0));
|
|
snprintf(cnt[0]->webstream_digest_rand
|
|
,sizeof(cnt[0]->webstream_digest_rand),"%d",randnbr);
|
|
|
|
while (cnt[mhdst.indxthrd] != NULL){
|
|
cnt[mhdst.indxthrd]->webstream_daemon = NULL;
|
|
if (cnt[mhdst.indxthrd]->conf.stream_port != 0 ){
|
|
if (mhdst.indxthrd == 0){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("Starting all camera streams on port %d")
|
|
,cnt[mhdst.indxthrd]->conf.stream_port);
|
|
} else {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("Starting camera %d stream on port %d")
|
|
,cnt[mhdst.indxthrd]->camera_id
|
|
,cnt[mhdst.indxthrd]->conf.stream_port);
|
|
}
|
|
|
|
mhdst.mhd_ops= malloc(sizeof(struct MHD_OptionItem)*WEBUI_MHD_OPTS);
|
|
webu_mhd_features(&mhdst);
|
|
webu_mhd_opts(&mhdst);
|
|
webu_mhd_flags(&mhdst);
|
|
if (mhdst.indxthrd == 0){
|
|
cnt[mhdst.indxthrd]->webstream_daemon = MHD_start_daemon (mhdst.mhd_flags
|
|
,cnt[mhdst.indxthrd]->conf.stream_port
|
|
,NULL, NULL
|
|
,&webu_answer_strm, cnt
|
|
,MHD_OPTION_ARRAY, mhdst.mhd_ops
|
|
,MHD_OPTION_END);
|
|
} else {
|
|
cnt[mhdst.indxthrd]->webstream_daemon = MHD_start_daemon (mhdst.mhd_flags
|
|
,cnt[mhdst.indxthrd]->conf.stream_port
|
|
,NULL, NULL
|
|
,&webu_answer_strm, cnt[mhdst.indxthrd]
|
|
,MHD_OPTION_ARRAY, mhdst.mhd_ops
|
|
,MHD_OPTION_END);
|
|
}
|
|
free(mhdst.mhd_ops);
|
|
if (cnt[mhdst.indxthrd]->webstream_daemon == NULL){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("Unable to start stream for camera %d")
|
|
,cnt[mhdst.indxthrd]->camera_id);
|
|
} else {
|
|
webu_strm_ntc(cnt,mhdst.indxthrd);
|
|
}
|
|
}
|
|
mhdst.indxthrd++;
|
|
}
|
|
if (mhdst.tls_cert != NULL) free(mhdst.tls_cert);
|
|
if (mhdst.tls_key != NULL) free(mhdst.tls_key);
|
|
|
|
return;
|
|
}
|
|
|
|
static void webu_start_ports(struct context **cnt){
|
|
/* Perform check for duplicate ports being specified. The config loading will
|
|
* duplicate ports from the motion.conf file to all the cameras so we do not
|
|
* log these duplicates to the user and instead just silently set them to zero
|
|
*/
|
|
int indx, indx2;
|
|
|
|
if (cnt[0]->conf.webcontrol_port != 0){
|
|
indx = 0;
|
|
while (cnt[indx] != NULL){
|
|
if ((cnt[0]->conf.webcontrol_port == cnt[indx]->conf.webcontrol_port) && (indx > 0)){
|
|
cnt[indx]->conf.webcontrol_port = 0;
|
|
}
|
|
|
|
if (cnt[0]->conf.webcontrol_port == cnt[indx]->conf.stream_port){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("Duplicate port requested %d")
|
|
,cnt[indx]->conf.stream_port);
|
|
cnt[indx]->conf.stream_port = 0;
|
|
}
|
|
|
|
indx++;
|
|
}
|
|
}
|
|
|
|
/* Now check on the stream ports */
|
|
indx = 0;
|
|
while (cnt[indx] != NULL){
|
|
if (cnt[indx]->conf.stream_port != 0){
|
|
indx2 = indx + 1;
|
|
while (cnt[indx2] != NULL){
|
|
if (cnt[indx]->conf.stream_port == cnt[indx2]->conf.stream_port){
|
|
if (indx != 0){
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("Duplicate port requested %d")
|
|
,cnt[indx2]->conf.stream_port);
|
|
}
|
|
cnt[indx2]->conf.stream_port = 0;
|
|
}
|
|
indx2++;
|
|
}
|
|
}
|
|
indx++;
|
|
}
|
|
}
|
|
|
|
void webu_stop(struct context **cnt) {
|
|
/* This function is called from the main Motion loop to shutdown the
|
|
* various MHD connections
|
|
*/
|
|
int indxthrd;
|
|
|
|
if (cnt[0]->webcontrol_daemon != NULL){
|
|
cnt[0]->webcontrol_finish = TRUE;
|
|
MHD_stop_daemon (cnt[0]->webcontrol_daemon);
|
|
}
|
|
|
|
|
|
indxthrd = 0;
|
|
while (cnt[indxthrd] != NULL){
|
|
if (cnt[indxthrd]->webstream_daemon != NULL){
|
|
cnt[indxthrd]->webcontrol_finish = TRUE;
|
|
MHD_stop_daemon (cnt[indxthrd]->webstream_daemon);
|
|
}
|
|
cnt[indxthrd]->webstream_daemon = NULL;
|
|
cnt[indxthrd]->webcontrol_daemon = NULL;
|
|
indxthrd++;
|
|
}
|
|
}
|
|
|
|
void webu_start(struct context **cnt) {
|
|
/* This function is called from the main motion thread to start up the
|
|
* webcontrol and streams. We need to block some signals otherwise MHD
|
|
* will not function correctly.
|
|
*/
|
|
struct sigaction act;
|
|
int indxthrd;
|
|
|
|
/* set signal handlers TO IGNORE */
|
|
memset(&act, 0, sizeof(act));
|
|
sigemptyset(&act.sa_mask);
|
|
act.sa_handler = SIG_IGN;
|
|
sigaction(SIGPIPE, &act, NULL);
|
|
sigaction(SIGCHLD, &act, NULL);
|
|
|
|
|
|
indxthrd = 0;
|
|
while (cnt[indxthrd] != NULL){
|
|
cnt[indxthrd]->webstream_daemon = NULL;
|
|
cnt[indxthrd]->webcontrol_daemon = NULL;
|
|
cnt[indxthrd]->webcontrol_finish = FALSE;
|
|
indxthrd++;
|
|
}
|
|
|
|
if (cnt[0]->conf.stream_preview_method != 99){
|
|
webu_start_ports(cnt);
|
|
|
|
webu_start_strm(cnt);
|
|
}
|
|
|
|
webu_start_ctrl(cnt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|