mirror of
https://github.com/Motion-Project/motion.git
synced 2026-02-07 21:41:47 -05:00
1392 lines
42 KiB
C++
1392 lines
42 KiB
C++
/*
|
|
* This file is part of MotionPlus.
|
|
*
|
|
* MotionPlus is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* MotionPlus is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with MotionPlus. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
* Copyright 2020-2021 MotionMrDave@gmail.com
|
|
*/
|
|
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include "motionplus.hpp"
|
|
#include "conf.hpp"
|
|
#include "logger.hpp"
|
|
#include "util.hpp"
|
|
#include "webu.hpp"
|
|
#include "webu_html.hpp"
|
|
#include "webu_stream.hpp"
|
|
#include "webu_json.hpp"
|
|
#include "webu_post.hpp"
|
|
#include "video_v4l2.hpp"
|
|
|
|
/* Context to pass the parms to functions to start mhd */
|
|
struct mhdstart_ctx {
|
|
struct ctx_motapp *motapp;
|
|
std::string tls_cert;
|
|
std::string tls_key;
|
|
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;
|
|
};
|
|
|
|
/* Set defaults for the webui context */
|
|
static void webu_context_init(struct ctx_motapp *motapp, struct ctx_webui *webui)
|
|
{
|
|
int indx;
|
|
char *tmplang;
|
|
|
|
webui->url = "";
|
|
webui->uri_camid = "";
|
|
webui->uri_cmd1 = "";
|
|
webui->uri_cmd2 = "";
|
|
webui->clientip = "";
|
|
webui->lang = ""; /* Two digit lang code */
|
|
|
|
webui->auth_opaque = (char*)mymalloc(WEBUI_LEN_PARM);
|
|
webui->auth_realm = (char*)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->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->resp_image = NULL; /* Buffer for sending the images */
|
|
webui->stream_pos = 0; /* Stream position of image being sent */
|
|
webui->stream_fps = 1; /* Stream rate */
|
|
webui->resp_page = ""; /* The response being constructed */
|
|
webui->post_info = NULL;
|
|
webui->post_sz = 0;
|
|
webui->motapp = motapp; /* The motion application context */
|
|
webui->cam = NULL; /* The context pointer for a single camera */
|
|
webui->cnct_type = WEBUI_CNCT_UNKNOWN;
|
|
webui->resp_type = WEBUI_RESP_HTML; /* Default to html response */
|
|
webui->cnct_method = WEBUI_METHOD_GET;
|
|
|
|
/* get the number of cameras and threads */
|
|
indx = 0;
|
|
if (webui->motapp->cam_list != NULL) {
|
|
while (webui->motapp->cam_list[++indx]) {
|
|
continue;
|
|
};
|
|
}
|
|
webui->cam_threads = indx;
|
|
|
|
webui->cam_count = indx;
|
|
if (indx > 1) {
|
|
webui->cam_count--;
|
|
}
|
|
|
|
tmplang = setlocale(LC_ALL, NULL);
|
|
if (tmplang == NULL) {
|
|
webui->lang = "en";
|
|
} else {
|
|
webui->lang.assign(tmplang, 2);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Free the variables in the webui context */
|
|
static void webu_context_free(struct ctx_webui *webui)
|
|
{
|
|
int indx;
|
|
|
|
if (webui->auth_user != NULL) {
|
|
free(webui->auth_user);
|
|
}
|
|
webui->auth_user = NULL;
|
|
|
|
if (webui->auth_pass != NULL) {
|
|
free(webui->auth_pass);
|
|
}
|
|
webui->auth_pass = NULL;
|
|
|
|
if (webui->auth_opaque != NULL) {
|
|
free(webui->auth_opaque);
|
|
}
|
|
webui->auth_opaque = NULL;
|
|
|
|
if (webui->auth_realm != NULL) {
|
|
free(webui->auth_realm);
|
|
}
|
|
webui->auth_realm = NULL;
|
|
|
|
if (webui->resp_image != NULL) {
|
|
free(webui->resp_image);
|
|
}
|
|
webui->resp_image = NULL;
|
|
|
|
for (indx = 0; indx<webui->post_sz; indx++) {
|
|
if (webui->post_info[indx].key_nm != NULL) {
|
|
free(webui->post_info[indx].key_nm);
|
|
}
|
|
webui->post_info[indx].key_nm = NULL;
|
|
if (webui->post_info[indx].key_val != NULL) {
|
|
free(webui->post_info[indx].key_val);
|
|
}
|
|
webui->post_info[indx].key_val = NULL;
|
|
}
|
|
if (webui->post_info != NULL) {
|
|
free(webui->post_info);
|
|
}
|
|
webui->post_info = NULL;
|
|
|
|
delete webui;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Edit the parameters specified in the url sent */
|
|
static void webu_parms_edit(struct ctx_webui *webui)
|
|
{
|
|
int indx, is_nbr;
|
|
|
|
if (webui->uri_camid.length() > 0) {
|
|
is_nbr = true;
|
|
for (indx=0; indx < (int)webui->uri_camid.length(); indx++) {
|
|
if ((webui->uri_camid[indx] > '9') || (webui->uri_camid[indx] < '0')) {
|
|
is_nbr = false;
|
|
}
|
|
}
|
|
if (is_nbr) {
|
|
webui->threadnbr = atoi(webui->uri_camid.c_str());
|
|
} else {
|
|
webui->threadnbr = -1;
|
|
}
|
|
} else {
|
|
webui->threadnbr = -1;
|
|
}
|
|
|
|
if (webui->threadnbr < 0) {
|
|
webui->cam = webui->motapp->cam_list[0];
|
|
webui->threadnbr = 0;
|
|
} else {
|
|
indx = 0;
|
|
while (webui->motapp->cam_list[indx] != NULL) {
|
|
if (webui->motapp->cam_list[indx]->camera_id == webui->threadnbr) {
|
|
webui->threadnbr = indx;
|
|
break;
|
|
}
|
|
indx++;
|
|
}
|
|
/* This may be null, in which case we will not answer the request */
|
|
webui->cam = webui->motapp->cam_list[indx];
|
|
}
|
|
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO
|
|
, "camid: >%s< thread: >%d< cmd1: >%s< cmd2: >%s<"
|
|
, webui->uri_camid.c_str(), webui->threadnbr
|
|
, webui->uri_cmd1.c_str(), webui->uri_cmd2.c_str());
|
|
|
|
|
|
}
|
|
|
|
/* Extract the camid and cmds from the url */
|
|
static int webu_parseurl(struct ctx_webui *webui)
|
|
{
|
|
int retcd;
|
|
char *tmpurl;
|
|
size_t pos_slash1, pos_slash2;
|
|
|
|
/* Example: /camid/cmd1/cmd2 */
|
|
retcd = 0;
|
|
webui->uri_camid = "";
|
|
webui->uri_cmd1 = "";
|
|
webui->uri_cmd2 = "";
|
|
|
|
if (webui->url.length() == 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (webui->url == "/favicon.ico") {
|
|
return -1;
|
|
}
|
|
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO, _("Sent url: %s"),webui->url.c_str());
|
|
|
|
tmpurl = (char*)mymalloc(webui->url.length()+1);
|
|
memcpy(tmpurl, webui->url.c_str(), webui->url.length());
|
|
|
|
MHD_http_unescape(tmpurl);
|
|
|
|
webui->url.assign(tmpurl);
|
|
free(tmpurl);
|
|
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO, _("Decoded url: %s"),webui->url.c_str());
|
|
|
|
if (webui->url.length() == 1) {
|
|
return 0;
|
|
}
|
|
|
|
pos_slash1 = webui->url.find("/", 1);
|
|
if (pos_slash1 != std::string::npos) {
|
|
webui->uri_camid = webui->url.substr(1, pos_slash1 - 1);
|
|
} else {
|
|
webui->uri_camid = webui->url.substr(1);
|
|
return 0;
|
|
}
|
|
|
|
pos_slash1++;
|
|
if (pos_slash1 >= webui->url.length()) {
|
|
return 0;
|
|
}
|
|
|
|
pos_slash2 = webui->url.find("/", pos_slash1);
|
|
if (pos_slash2 != std::string::npos) {
|
|
webui->uri_cmd1 = webui->url.substr(pos_slash1, pos_slash2 - pos_slash1);
|
|
} else {
|
|
webui->uri_cmd1 = webui->url.substr(pos_slash1);
|
|
return 0;
|
|
}
|
|
|
|
pos_slash1 = ++pos_slash2;
|
|
if (pos_slash1 >= webui->url.length()) {
|
|
return 0;
|
|
}
|
|
|
|
pos_slash2 = webui->url.find("/", pos_slash1);
|
|
if (pos_slash2 != std::string::npos) {
|
|
webui->uri_cmd2 = webui->url.substr(pos_slash1, pos_slash2 - pos_slash1);
|
|
} else {
|
|
webui->uri_cmd2 = webui->url.substr(pos_slash1);
|
|
return 0;
|
|
}
|
|
|
|
return retcd;
|
|
|
|
}
|
|
|
|
/* Log the ip of the client connecting*/
|
|
static void webu_clientip(struct ctx_webui *webui)
|
|
{
|
|
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->motapp->cam_list[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) {
|
|
webui->clientip = "Unknown";
|
|
} else {
|
|
webui->clientip.assign(client);
|
|
if (webui->clientip.substr(0, 7) == "::ffff:") {
|
|
webui->clientip = webui->clientip.substr(7);
|
|
}
|
|
}
|
|
} 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) {
|
|
webui->clientip = "Unknown";
|
|
} else {
|
|
webui->clientip.assign(client);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Get the hostname */
|
|
static void webu_hostname(struct ctx_webui *webui)
|
|
{
|
|
const char *hdr;
|
|
std::string hostname;
|
|
|
|
hostname = "localhost";
|
|
|
|
hdr = MHD_lookup_connection_value(webui->connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST);
|
|
if (hdr != NULL) {
|
|
hostname.assign(hdr);
|
|
if ((hdr[0] == '[') && (hostname.find("]") != std::string::npos)) {
|
|
hostname = hostname.substr(0, hostname.find("]") + 1);
|
|
} else if ((hdr[0] != '[') && (hostname.find(":") != std::string::npos)) {
|
|
hostname = hostname.substr(0, hostname.find(":"));
|
|
}
|
|
}
|
|
|
|
if (webui->motapp->cam_list[0]->conf->webcontrol_tls) {
|
|
webui->hostfull = "https";
|
|
} else {
|
|
webui->hostfull = "http";
|
|
}
|
|
webui->hostfull += "://" + hostname + ":" +
|
|
std::to_string(webui->motapp->cam_list[0]->conf->webcontrol_port);
|
|
|
|
MOTION_LOG(DBG,TYPE_ALL, NO_ERRNO, _("Full Host: %s"), webui->hostfull.c_str());
|
|
|
|
return;
|
|
}
|
|
|
|
/* Log the failed authentication check */
|
|
static void webu_failauth_log(struct ctx_webui *webui)
|
|
{
|
|
timespec tm_cnct;
|
|
struct ctx_failauth failauth;
|
|
std::list<ctx_failauth>::iterator it;
|
|
|
|
MOTION_LOG(ALR, TYPE_STREAM, NO_ERRNO
|
|
,_("Failed authentication from %s"), webui->clientip.c_str());
|
|
|
|
clock_gettime(CLOCK_REALTIME, &tm_cnct);
|
|
|
|
it = webui->motapp->webcontrol_failauth.begin();
|
|
while (it != webui->motapp->webcontrol_failauth.end()) {
|
|
if (it->clientip == webui->clientip) {
|
|
it->attempt_nbr++;
|
|
it->attempt_time.tv_sec =tm_cnct.tv_sec;
|
|
return;
|
|
}
|
|
it++;
|
|
}
|
|
|
|
failauth.clientip = webui->clientip;
|
|
failauth.attempt_nbr = 1;
|
|
failauth.attempt_time = tm_cnct;
|
|
|
|
webui->motapp->webcontrol_failauth.push_back(failauth);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Log the failed authentication check */
|
|
static void webu_failauth_reset(struct ctx_webui *webui)
|
|
{
|
|
timespec tm_cnct;
|
|
std::list<ctx_failauth>::iterator it;
|
|
|
|
clock_gettime(CLOCK_REALTIME, &tm_cnct);
|
|
it = webui->motapp->webcontrol_failauth.begin();
|
|
while (it != webui->motapp->webcontrol_failauth.end()) {
|
|
if ((it->clientip == webui->clientip) ||
|
|
((tm_cnct.tv_sec - it->attempt_time.tv_sec) >= 600)) {
|
|
it = webui->motapp->webcontrol_failauth.erase(it);
|
|
} else {
|
|
it++;
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Check for ips with excessive failed authentication attempts */
|
|
static mhdrslt webu_failauth_check(struct ctx_webui *webui)
|
|
{
|
|
timespec tm_cnct;
|
|
std::list<ctx_failauth>::iterator it;
|
|
|
|
if (webui->motapp->webcontrol_failauth.size() == 0) {
|
|
return MHD_YES;
|
|
}
|
|
|
|
clock_gettime(CLOCK_REALTIME, &tm_cnct);
|
|
it = webui->motapp->webcontrol_failauth.begin();
|
|
while (it != webui->motapp->webcontrol_failauth.end()) {
|
|
if ((it->clientip == webui->clientip) &&
|
|
((tm_cnct.tv_sec - it->attempt_time.tv_sec) < 600) &&
|
|
(it->attempt_nbr > 5)) {
|
|
MOTION_LOG(ALR, TYPE_STREAM, NO_ERRNO
|
|
,_("ignoring attempt from %s"), webui->clientip.c_str());
|
|
|
|
it->attempt_time = tm_cnct;
|
|
return MHD_NO;
|
|
} else if ((tm_cnct.tv_sec - it->attempt_time.tv_sec) >= 600) {
|
|
it = webui->motapp->webcontrol_failauth.erase(it);
|
|
} else {
|
|
it++;
|
|
}
|
|
}
|
|
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
/* Create a authorization denied response to user*/
|
|
static mhdrslt webu_mhd_digest_fail(struct ctx_webui *webui,int signal_stale)
|
|
{
|
|
struct MHD_Response *response;
|
|
mhdrslt retcd;
|
|
|
|
webui->authenticated = false;
|
|
|
|
webui->resp_page = "<html><head><title>Access denied</title>"
|
|
"</head><body>Access denied</body></html>";
|
|
|
|
response = MHD_create_response_from_buffer(webui->resp_page.length()
|
|
,(void *)webui->resp_page.c_str(), 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;
|
|
}
|
|
|
|
/* Perform digest authentication */
|
|
static mhdrslt webu_mhd_digest(struct ctx_webui *webui)
|
|
{
|
|
/* 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 (mystrne(user, webui->auth_user)) {
|
|
webu_failauth_log(webui);
|
|
if (user != NULL) {
|
|
free(user);
|
|
}
|
|
user = NULL;
|
|
return webu_mhd_digest_fail(webui, MHD_NO);
|
|
}
|
|
if (user != NULL) {
|
|
free(user);
|
|
}
|
|
user = NULL;
|
|
|
|
/* 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) {
|
|
webu_failauth_log(webui);
|
|
}
|
|
|
|
if ( (retcd == MHD_INVALID_NONCE) || (retcd == MHD_NO) ) {
|
|
return webu_mhd_digest_fail(webui, retcd);
|
|
}
|
|
|
|
webui->authenticated = true;
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
/* Create a authorization denied response to user*/
|
|
static mhdrslt webu_mhd_basic_fail(struct ctx_webui *webui)
|
|
{
|
|
struct MHD_Response *response;
|
|
int retcd;
|
|
|
|
webui->authenticated = false;
|
|
|
|
webui->resp_page = "<html><head><title>Access denied</title>"
|
|
"</head><body>Access denied</body></html>";
|
|
|
|
response = MHD_create_response_from_buffer(webui->resp_page.length()
|
|
,(void *)webui->resp_page.c_str(), 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);
|
|
|
|
if (retcd == MHD_YES) {
|
|
return MHD_YES;
|
|
} else {
|
|
return MHD_NO;
|
|
}
|
|
|
|
}
|
|
|
|
/* Perform Basic Authentication. */
|
|
static mhdrslt webu_mhd_basic(struct ctx_webui *webui)
|
|
{
|
|
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);
|
|
}
|
|
user = NULL;
|
|
if (pass != NULL) {
|
|
free(pass);
|
|
}
|
|
pass = NULL;
|
|
return webu_mhd_basic_fail(webui);
|
|
}
|
|
|
|
if ((mystrne(user, webui->auth_user)) || (mystrne(pass, webui->auth_pass))) {
|
|
webu_failauth_log(webui);
|
|
if (user != NULL) {
|
|
free(user);
|
|
}
|
|
user = NULL;
|
|
if (pass != NULL) {
|
|
free(pass);
|
|
}
|
|
pass = NULL;
|
|
return webu_mhd_basic_fail(webui);
|
|
}
|
|
|
|
if (user != NULL) {
|
|
free(user);
|
|
}
|
|
user = NULL;
|
|
if (pass != NULL) {
|
|
free(pass);
|
|
}
|
|
pass = NULL;
|
|
|
|
webui->authenticated = true;
|
|
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
/* Parse apart the user:pass provided*/
|
|
static void webu_mhd_auth_parse(struct ctx_webui *webui)
|
|
{
|
|
int auth_len;
|
|
char *col_pos;
|
|
|
|
if (webui->auth_user != NULL) {
|
|
free(webui->auth_user);
|
|
}
|
|
webui->auth_user = NULL;
|
|
if (webui->auth_pass != NULL) {
|
|
free(webui->auth_pass);
|
|
}
|
|
webui->auth_pass = NULL;
|
|
|
|
auth_len = webui->motapp->cam_list[0]->conf->webcontrol_authentication.length();
|
|
col_pos =(char*) strstr(webui->motapp->cam_list[0]->conf->webcontrol_authentication.c_str() ,":");
|
|
if (col_pos == NULL) {
|
|
webui->auth_user = (char*)mymalloc(auth_len+1);
|
|
webui->auth_pass = (char*)mymalloc(2);
|
|
snprintf(webui->auth_user, auth_len + 1, "%s"
|
|
,webui->motapp->cam_list[0]->conf->webcontrol_authentication.c_str());
|
|
snprintf(webui->auth_pass, 2, "%s","");
|
|
} else {
|
|
webui->auth_user = (char*)mymalloc(auth_len - strlen(col_pos) + 1);
|
|
webui->auth_pass =(char*)mymalloc(strlen(col_pos));
|
|
snprintf(webui->auth_user, auth_len - strlen(col_pos) + 1, "%s"
|
|
,webui->motapp->cam_list[0]->conf->webcontrol_authentication.c_str());
|
|
snprintf(webui->auth_pass, strlen(col_pos), "%s", col_pos + 1);
|
|
}
|
|
|
|
}
|
|
|
|
/* Initialize for authorization */
|
|
static mhdrslt webu_mhd_auth(struct ctx_webui *webui)
|
|
{
|
|
unsigned int rand1,rand2;
|
|
|
|
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 (webui->motapp->cam_list[0]->conf->webcontrol_authentication == "") {
|
|
webui->authenticated = true;
|
|
if (webui->motapp->cam_list[0]->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);
|
|
}
|
|
|
|
if (webui->motapp->cam_list[0]->conf->webcontrol_auth_method == 1) {
|
|
return webu_mhd_basic(webui);
|
|
} else if (webui->motapp->cam_list[0]->conf->webcontrol_auth_method == 2) {
|
|
return webu_mhd_digest(webui);
|
|
}
|
|
|
|
|
|
webui->authenticated = true;
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
/* Send the response that we created back to the user. */
|
|
static mhdrslt webu_mhd_send(struct ctx_webui *webui)
|
|
{
|
|
mhdrslt retcd;
|
|
struct MHD_Response *response;
|
|
int indx;
|
|
|
|
response = MHD_create_response_from_buffer(webui->resp_page.length()
|
|
,(void *)webui->resp_page.c_str(), MHD_RESPMEM_PERSISTENT);
|
|
if (!response) {
|
|
MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO, _("Invalid response"));
|
|
return MHD_NO;
|
|
}
|
|
|
|
if (webui->cam != NULL) {
|
|
if (webui->motapp->webcontrol_headers->params_count > 0) {
|
|
for (indx = 0; indx < webui->motapp->webcontrol_headers->params_count; indx++) {
|
|
MHD_add_response_header (response
|
|
, webui->motapp->webcontrol_headers->params_array[indx].param_name
|
|
, webui->motapp->webcontrol_headers->params_array[indx].param_value
|
|
);
|
|
}
|
|
}
|
|
if (webui->resp_type == WEBUI_RESP_TEXT) {
|
|
MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain;");
|
|
} else if (webui->resp_type == WEBUI_RESP_JSON) {
|
|
MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "application/json;");
|
|
} else {
|
|
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;
|
|
}
|
|
|
|
|
|
/* Process the post data command */
|
|
static mhdrslt webu_answer_post(struct ctx_webui *webui)
|
|
{
|
|
mhdrslt retcd;
|
|
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO ,"processing post");
|
|
|
|
pthread_mutex_lock(&webui->motapp->mutex_post);
|
|
webu_post_main(webui);
|
|
pthread_mutex_unlock(&webui->motapp->mutex_post);
|
|
|
|
if (webui->motapp->cam_list[0]->conf->webcontrol_interface == 3) {
|
|
webu_html_user(webui);
|
|
} else {
|
|
webu_html_page(webui);
|
|
}
|
|
|
|
retcd = webu_mhd_send(webui);
|
|
if (retcd == MHD_NO) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,"send post page failed");
|
|
}
|
|
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
/*Append more data on to an existing entry in the post info structure */
|
|
static void webu_iterate_post_append(struct ctx_webui *webui, int indx
|
|
, const char *data, size_t datasz)
|
|
{
|
|
|
|
webui->post_info[indx].key_val = (char*)realloc(
|
|
webui->post_info[indx].key_val
|
|
, webui->post_info[indx].key_sz + datasz + 1);
|
|
|
|
memset(webui->post_info[indx].key_val +
|
|
webui->post_info[indx].key_sz, 0, datasz + 1);
|
|
|
|
if (datasz > 0) {
|
|
memcpy(webui->post_info[indx].key_val +
|
|
webui->post_info[indx].key_sz, data, datasz);
|
|
}
|
|
|
|
webui->post_info[indx].key_sz += datasz;
|
|
|
|
}
|
|
|
|
/*Create new entry in the post info structure */
|
|
static void webu_iterate_post_new(struct ctx_webui *webui, const char *key
|
|
, const char *data, size_t datasz)
|
|
{
|
|
int retcd;
|
|
|
|
webui->post_sz++;
|
|
if (webui->post_sz == 1) {
|
|
webui->post_info = (ctx_key *)malloc(sizeof(struct ctx_key));
|
|
} else {
|
|
webui->post_info = (ctx_key *)realloc(webui->post_info
|
|
, webui->post_sz * sizeof(struct ctx_key));
|
|
}
|
|
|
|
webui->post_info[webui->post_sz-1].key_nm = (char*)malloc(strlen(key)+1);
|
|
retcd = snprintf(webui->post_info[webui->post_sz-1].key_nm, strlen(key)+1, "%s", key);
|
|
|
|
webui->post_info[webui->post_sz-1].key_val = (char*)malloc(datasz+1);
|
|
memset(webui->post_info[webui->post_sz-1].key_val,0,datasz+1);
|
|
if (datasz > 0) {
|
|
memcpy(webui->post_info[webui->post_sz-1].key_val, data, datasz);
|
|
}
|
|
|
|
webui->post_info[webui->post_sz-1].key_sz = datasz;
|
|
|
|
if (retcd < 0) {
|
|
printf("Error processing post data\n");
|
|
}
|
|
|
|
}
|
|
|
|
static mhdrslt webu_iterate_post (void *ptr, enum MHD_ValueKind kind
|
|
, const char *key, const char *filename, const char *content_type
|
|
, const char *transfer_encoding, const char *data, uint64_t off, size_t datasz)
|
|
{
|
|
(void) kind;
|
|
(void) filename;
|
|
(void) content_type;
|
|
(void) transfer_encoding;
|
|
(void) off;
|
|
|
|
struct ctx_webui *webui = (ctx_webui *)ptr;
|
|
int indx;
|
|
|
|
for (indx=0; indx < webui->post_sz; indx++) {
|
|
if (mystreq(webui->post_info[indx].key_nm, key)) {
|
|
break;
|
|
}
|
|
}
|
|
if (indx < webui->post_sz) {
|
|
webu_iterate_post_append(webui, indx, data, datasz);
|
|
} else {
|
|
webu_iterate_post_new(webui, key, data, datasz);
|
|
}
|
|
|
|
return MHD_YES;
|
|
}
|
|
|
|
/* Answer the get request from the user */
|
|
static mhdrslt webu_answer_get(struct ctx_webui *webui)
|
|
{
|
|
int retcd;
|
|
|
|
MOTION_LOG(DBG, TYPE_STREAM, NO_ERRNO ,"processing get");
|
|
|
|
retcd = MHD_NO;
|
|
if ((webui->uri_cmd1 == "mjpg") ||
|
|
(webui->uri_cmd1 == "static")) {
|
|
|
|
retcd = webu_stream_main(webui);
|
|
if (retcd == MHD_NO) {
|
|
webu_html_badreq(webui);
|
|
retcd = webu_mhd_send(webui);
|
|
}
|
|
|
|
} else if ((webui->uri_camid == "config.json") ||
|
|
(webui->uri_cmd1 == "config.json")) {
|
|
|
|
pthread_mutex_lock(&webui->motapp->mutex_post);
|
|
webu_json_config(webui);
|
|
pthread_mutex_unlock(&webui->motapp->mutex_post);
|
|
|
|
retcd = webu_mhd_send(webui);
|
|
if (retcd == MHD_NO) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("send page failed."));
|
|
}
|
|
|
|
} else {
|
|
pthread_mutex_lock(&webui->motapp->mutex_post);
|
|
if (webui->motapp->cam_list[0]->conf->webcontrol_interface == 3) {
|
|
webu_html_user(webui);
|
|
} else {
|
|
webu_html_page(webui);
|
|
}
|
|
pthread_mutex_unlock(&webui->motapp->mutex_post);
|
|
|
|
retcd = webu_mhd_send(webui);
|
|
if (retcd == MHD_NO) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("send page failed."));
|
|
}
|
|
}
|
|
|
|
return retcd;
|
|
|
|
}
|
|
|
|
|
|
/* Answer the connection request for the webcontrol*/
|
|
static mhdrslt webu_answer(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)
|
|
{
|
|
(void)cls;
|
|
(void)url;
|
|
(void)version;
|
|
(void)upload_data;
|
|
(void)upload_data_size;
|
|
|
|
mhdrslt retcd;
|
|
ctx_webui *webui =(struct ctx_webui *) *ptr;
|
|
|
|
webui->cnct_type = WEBUI_CNCT_CONTROL;
|
|
webui->connection = connection;
|
|
|
|
/* Throw bad URLS back to user*/
|
|
if ((webui->cam == NULL) || (webui->url.length() == 0)) {
|
|
webu_html_badreq(webui);
|
|
retcd = webu_mhd_send(webui);
|
|
return retcd;
|
|
}
|
|
|
|
if (webui->cam->finish_cam) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Shutting down camera"));
|
|
return MHD_NO;
|
|
}
|
|
|
|
if (webui->clientip.length() == 0) {
|
|
webu_clientip(webui);
|
|
}
|
|
|
|
if (webu_failauth_check(webui) == MHD_NO) {
|
|
return MHD_NO;
|
|
}
|
|
|
|
webu_hostname(webui);
|
|
|
|
if (!webui->authenticated) {
|
|
retcd = webu_mhd_auth(webui);
|
|
if (!webui->authenticated) {
|
|
return retcd;
|
|
}
|
|
}
|
|
|
|
webu_failauth_reset(webui);
|
|
|
|
MOTION_LOG(INF,TYPE_ALL, NO_ERRNO, _("Connection from: %s"),webui->clientip.c_str());
|
|
|
|
if (webui->mhd_first) {
|
|
webui->mhd_first = false;
|
|
if (mystreq(method,"POST")) {
|
|
webui->post_processor = MHD_create_post_processor (webui->connection
|
|
, WEBUI_POST_BFRSZ, webu_iterate_post, (void *)webui);
|
|
if (webui->post_processor == NULL) {
|
|
return MHD_NO;
|
|
}
|
|
webui->cnct_method = WEBUI_METHOD_POST;
|
|
} else {
|
|
webui->cnct_method = WEBUI_METHOD_GET;
|
|
}
|
|
|
|
return MHD_YES;
|
|
}
|
|
|
|
if (mystreq(method,"POST")) {
|
|
if (*upload_data_size != 0) {
|
|
retcd = MHD_post_process (webui->post_processor, upload_data, *upload_data_size);
|
|
*upload_data_size = 0;
|
|
} else {
|
|
retcd = webu_answer_post(webui);
|
|
}
|
|
} else {
|
|
retcd = webu_answer_get(webui);
|
|
}
|
|
|
|
return retcd;
|
|
|
|
}
|
|
|
|
/* Initialize the MHD answer */
|
|
static void *webu_mhd_init(void *cls, const char *uri, struct MHD_Connection *connection)
|
|
{
|
|
struct ctx_motapp *motapp = (struct ctx_motapp *)cls;
|
|
struct ctx_webui *webui;
|
|
int retcd;
|
|
|
|
(void)connection;
|
|
|
|
mythreadname_set("wc", 0, NULL);
|
|
|
|
webui = new ctx_webui;
|
|
|
|
webu_context_init(motapp, webui);
|
|
|
|
webui->mhd_first = true;
|
|
|
|
webui->url.assign(uri);
|
|
|
|
retcd = webu_parseurl(webui);
|
|
if (retcd != 0) {
|
|
webui->uri_camid = "";
|
|
webui->uri_cmd1 = "";
|
|
webui->url = "";
|
|
}
|
|
|
|
webu_parms_edit(webui);
|
|
|
|
return webui;
|
|
}
|
|
|
|
/* Clean up our variables when the MHD connection closes */
|
|
static void webu_mhd_deinit(void *cls, struct MHD_Connection *connection
|
|
, void **con_cls, enum MHD_RequestTerminationCode toe)
|
|
{
|
|
struct ctx_webui *webui =(struct ctx_webui *) *con_cls;
|
|
|
|
(void)connection;
|
|
(void)cls;
|
|
(void)toe;
|
|
/* Sometimes we can shutdown after we have initiated a connection but yet
|
|
* before the connection counter has been incremented. So we check the
|
|
* connection counter before we decrement
|
|
*/
|
|
if (webui->cnct_type == WEBUI_CNCT_FULL ) {
|
|
pthread_mutex_lock(&webui->cam->stream.mutex);
|
|
if (webui->cam->stream.norm.cnct_count > 0) {
|
|
webui->cam->stream.norm.cnct_count--;
|
|
}
|
|
pthread_mutex_unlock(&webui->cam->stream.mutex);
|
|
|
|
} else if (webui->cnct_type == WEBUI_CNCT_SUB ) {
|
|
pthread_mutex_lock(&webui->cam->stream.mutex);
|
|
if (webui->cam->stream.sub.cnct_count > 0) {
|
|
webui->cam->stream.sub.cnct_count--;
|
|
}
|
|
pthread_mutex_unlock(&webui->cam->stream.mutex);
|
|
|
|
} else if (webui->cnct_type == WEBUI_CNCT_MOTION ) {
|
|
pthread_mutex_lock(&webui->cam->stream.mutex);
|
|
if (webui->cam->stream.motion.cnct_count > 0) {
|
|
webui->cam->stream.motion.cnct_count--;
|
|
}
|
|
pthread_mutex_unlock(&webui->cam->stream.mutex);
|
|
|
|
} else if (webui->cnct_type == WEBUI_CNCT_SOURCE ) {
|
|
pthread_mutex_lock(&webui->cam->stream.mutex);
|
|
if (webui->cam->stream.source.cnct_count > 0) {
|
|
webui->cam->stream.source.cnct_count--;
|
|
}
|
|
pthread_mutex_unlock(&webui->cam->stream.mutex);
|
|
|
|
} else if (webui->cnct_type == WEBUI_CNCT_SECONDARY ) {
|
|
pthread_mutex_lock(&webui->cam->stream.mutex);
|
|
if (webui->cam->stream.secondary.cnct_count > 0) {
|
|
webui->cam->stream.secondary.cnct_count--;
|
|
}
|
|
pthread_mutex_unlock(&webui->cam->stream.mutex);
|
|
|
|
}
|
|
|
|
if (webui != NULL) {
|
|
if (webui->cnct_method == WEBUI_METHOD_POST) {
|
|
MHD_destroy_post_processor (webui->post_processor);
|
|
}
|
|
webu_context_free(webui);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Validate that the MHD version installed can process basic authentication */
|
|
static void webu_mhd_features_basic(struct mhdstart_ctx *mhdst)
|
|
{
|
|
#if MHD_VERSION < 0x00094400
|
|
(void)mhdst;
|
|
#else
|
|
mhdrslt 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->motapp->cam_list[0]->conf->webcontrol_auth_method == 1) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Basic authentication: disabled"));
|
|
mhdst->motapp->cam_list[0]->conf->webcontrol_auth_method = 0;
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("Basic authentication: disabled"));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Validate that the MHD version installed can process digest authentication */
|
|
static void webu_mhd_features_digest(struct mhdstart_ctx *mhdst)
|
|
{
|
|
#if MHD_VERSION < 0x00094400
|
|
(void)mhdst;
|
|
#else
|
|
mhdrslt 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->motapp->cam_list[0]->conf->webcontrol_auth_method == 2) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("Digest authentication: disabled"));
|
|
mhdst->motapp->cam_list[0]->conf->webcontrol_auth_method = 0;
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("Digest authentication: disabled"));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Validate that the MHD version installed can process IPV6 */
|
|
static void webu_mhd_features_ipv6(struct mhdstart_ctx *mhdst)
|
|
{
|
|
#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
|
|
mhdrslt 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
|
|
}
|
|
|
|
/* Validate that the MHD version installed can process tls */
|
|
static void webu_mhd_features_tls(struct mhdstart_ctx *mhdst)
|
|
{
|
|
#if MHD_VERSION < 0x00094400
|
|
if (mhdst->motapp->cam_list[0]->conf->webcontrol_tls) {
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("libmicrohttpd libary too old SSL/TLS disabled"));
|
|
mhdst->motapp->cam_list[0]->conf->webcontrol_tls = 0;
|
|
}
|
|
#else
|
|
mhdrslt 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->motapp->cam_list[0]->conf->webcontrol_tls) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO ,_("SSL/TLS: disabled"));
|
|
mhdst->motapp->cam_list[0]->conf->webcontrol_tls = 0;
|
|
} else {
|
|
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO ,_("SSL/TLS: disabled"));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Validate the features that MHD can support */
|
|
static void webu_mhd_features(struct mhdstart_ctx *mhdst)
|
|
{
|
|
webu_mhd_features_basic(mhdst);
|
|
|
|
webu_mhd_features_digest(mhdst);
|
|
|
|
webu_mhd_features_ipv6(mhdst);
|
|
|
|
webu_mhd_features_tls(mhdst);
|
|
|
|
}
|
|
/* Load a either the key or cert file for MHD*/
|
|
static std::string webu_mhd_loadfile(std::string fname)
|
|
{
|
|
/* This needs conversion to c++ stream */
|
|
FILE *infile;
|
|
size_t file_size, read_size;
|
|
char *file_char;
|
|
std::string filestr;
|
|
|
|
filestr = "";
|
|
if (fname != "") {
|
|
infile = fopen(fname.c_str() , "rb");
|
|
if (infile != NULL) {
|
|
fseek(infile, 0, SEEK_END);
|
|
file_size = ftell(infile);
|
|
if (file_size > 0 ) {
|
|
file_char = (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;
|
|
filestr.assign(file_char, file_size);
|
|
} else {
|
|
MOTION_LOG(ERR, TYPE_STREAM, NO_ERRNO
|
|
,_("Error reading file for SSL/TLS support."));
|
|
}
|
|
free(file_char);
|
|
}
|
|
fclose(infile);
|
|
}
|
|
}
|
|
return filestr;
|
|
|
|
}
|
|
|
|
/* Validate that we have the files needed for tls*/
|
|
static void webu_mhd_checktls(struct mhdstart_ctx *mhdst)
|
|
{
|
|
|
|
if (mhdst->motapp->cam_list[0]->conf->webcontrol_tls) {
|
|
if ((mhdst->motapp->cam_list[0]->conf->webcontrol_cert == "") || (mhdst->tls_cert == "")) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("SSL/TLS requested but no cert file provided. SSL/TLS disabled"));
|
|
mhdst->motapp->cam_list[0]->conf->webcontrol_tls = 0;
|
|
}
|
|
if ((mhdst->motapp->cam_list[0]->conf->webcontrol_key == "") || (mhdst->tls_key == "")) {
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
,_("SSL/TLS requested but no key file provided. SSL/TLS disabled"));
|
|
mhdst->motapp->cam_list[0]->conf->webcontrol_tls = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Set the initialization function for MHD to call upon getting a connection */
|
|
static void webu_mhd_opts_init(struct mhdstart_ctx *mhdst)
|
|
{
|
|
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->motapp;
|
|
mhdst->mhd_opt_nbr++;
|
|
}
|
|
|
|
/* Set the MHD option on the function to call when the connection closes */
|
|
static void webu_mhd_opts_deinit(struct mhdstart_ctx *mhdst)
|
|
{
|
|
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++;
|
|
|
|
}
|
|
|
|
/* Set the MHD option on acceptable connections */
|
|
static void webu_mhd_opts_localhost(struct mhdstart_ctx *mhdst)
|
|
{
|
|
if (mhdst->motapp->cam_list[0]->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->motapp->cam_list[0]->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->motapp->cam_list[0]->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++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Set the mhd digest options */
|
|
static void webu_mhd_opts_digest(struct mhdstart_ctx *mhdst)
|
|
{
|
|
|
|
if (mhdst->motapp->cam_list[0]->conf->webcontrol_auth_method == 2) {
|
|
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].option = MHD_OPTION_DIGEST_AUTH_RANDOM;
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].value = sizeof(mhdst->motapp->webcontrol_digest_rand);
|
|
mhdst->mhd_ops[mhdst->mhd_opt_nbr].ptr_value = mhdst->motapp->webcontrol_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++;
|
|
}
|
|
|
|
}
|
|
|
|
/* Set the MHD options needed when we want TLS connections */
|
|
static void webu_mhd_opts_tls(struct mhdstart_ctx *mhdst)
|
|
{
|
|
if (mhdst->motapp->cam_list[0]->conf->webcontrol_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 = (void *)mhdst->tls_cert.c_str();
|
|
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 = (void *)mhdst->tls_key.c_str();
|
|
mhdst->mhd_opt_nbr++;
|
|
}
|
|
|
|
}
|
|
|
|
/* Set all the MHD options based upon the configuration parameters*/
|
|
static void webu_mhd_opts(struct mhdstart_ctx *mhdst)
|
|
{
|
|
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++;
|
|
|
|
}
|
|
|
|
/* Set the mhd start up flags */
|
|
static void webu_mhd_flags(struct mhdstart_ctx *mhdst)
|
|
{
|
|
mhdst->mhd_flags = MHD_USE_THREAD_PER_CONNECTION;
|
|
|
|
if (mhdst->ipv6) {
|
|
mhdst->mhd_flags = mhdst->mhd_flags | MHD_USE_DUAL_STACK;
|
|
}
|
|
|
|
if (mhdst->motapp->cam_list[0]->conf->webcontrol_tls) {
|
|
mhdst->mhd_flags = mhdst->mhd_flags | MHD_USE_SSL;
|
|
}
|
|
|
|
}
|
|
|
|
/* Start the webcontrol */
|
|
static void webu_init_webcontrol(struct ctx_motapp *motapp)
|
|
{
|
|
struct mhdstart_ctx mhdst;
|
|
unsigned int randnbr;
|
|
|
|
MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO
|
|
, _("Starting webcontrol on port %d")
|
|
, motapp->cam_list[0]->conf->webcontrol_port);
|
|
|
|
motapp->webcontrol_headers = (ctx_params*)mymalloc(sizeof(struct ctx_params));
|
|
motapp->webcontrol_headers->update_params = true;
|
|
util_parms_parse(motapp->webcontrol_headers, motapp->cam_list[0]->conf->webcontrol_headers);
|
|
|
|
mhdst.tls_cert = webu_mhd_loadfile(motapp->cam_list[0]->conf->webcontrol_cert);
|
|
mhdst.tls_key = webu_mhd_loadfile(motapp->cam_list[0]->conf->webcontrol_key);
|
|
mhdst.motapp = motapp;
|
|
mhdst.ipv6 = motapp->cam_list[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(motapp->webcontrol_digest_rand
|
|
,sizeof(motapp->webcontrol_digest_rand),"%d",randnbr);
|
|
|
|
mhdst.mhd_ops =(struct MHD_OptionItem*)mymalloc(sizeof(struct MHD_OptionItem)*WEBUI_MHD_OPTS);
|
|
webu_mhd_features(&mhdst);
|
|
webu_mhd_opts(&mhdst);
|
|
webu_mhd_flags(&mhdst);
|
|
|
|
motapp->webcontrol_daemon = MHD_start_daemon (
|
|
mhdst.mhd_flags
|
|
, motapp->cam_list[0]->conf->webcontrol_port
|
|
, NULL, NULL
|
|
, &webu_answer, motapp->cam_list
|
|
, MHD_OPTION_ARRAY, mhdst.mhd_ops
|
|
, MHD_OPTION_END);
|
|
|
|
free(mhdst.mhd_ops);
|
|
if (motapp->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")
|
|
,motapp->cam_list[0]->conf->webcontrol_port);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Shut down the webcontrol */
|
|
void webu_deinit(struct ctx_motapp *motapp)
|
|
{
|
|
|
|
if (motapp->webcontrol_daemon != NULL) {
|
|
motapp->webcontrol_finish = true;
|
|
MHD_stop_daemon (motapp->webcontrol_daemon);
|
|
}
|
|
|
|
util_parms_free(motapp->webcontrol_headers);
|
|
if (motapp->webcontrol_headers != NULL) {
|
|
free(motapp->webcontrol_headers);
|
|
}
|
|
motapp->webcontrol_headers = NULL;
|
|
|
|
}
|
|
|
|
/* Start the webcontrol and streams */
|
|
void webu_init(struct ctx_motapp *motapp)
|
|
{
|
|
struct sigaction act;
|
|
|
|
/* We need to block some signals otherwise MHD will not function correctly. */
|
|
/* 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);
|
|
|
|
motapp->webcontrol_daemon = NULL;
|
|
motapp->webcontrol_finish = false;
|
|
|
|
/* Start the webcontrol */
|
|
if (motapp->cam_list[0]->conf->webcontrol_port != 0 ) {
|
|
webu_init_webcontrol(motapp);
|
|
}
|
|
|
|
return;
|
|
|
|
}
|