/*
* 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 .
*
* Copyright 2020 MotionMrDave@gmail.com
*/
/*
* Functional naming scheme
* webu_html* - Functions that create the display webcontrol page.
* webu_html_main - Entry point from webu_ans_ctrl(in webu.c)
* webu_html_page - Create the web page
* webu_html_head - Header section of the page
* webu_html_style* - The style section of the web page
* webu_html_body - Calls all the functions to create the body
* webu_html_navbar* - The navbar section of the web page
* webu_html_config* - config parms of page
* webu_html_track* - Tracking functions
* webu_html_script* - The javascripts of the web page
*
* To debug, run code, open page, view source and make copy of html
* into a local file to revise changes then determine applicable section(s)
* in this code to modify to match modified version.
* Known HTML Issues:
* Single and double quotes are not consistently used.
* HTML ids do not follow any naming convention.
* After clicking restart, do something...Try to connect again?
*
* Additional functionality considerations:
* Notification to user of items that require restart when changed.
* Notification to user that item successfully implemented (config change/tracking)
* List MotionPlus parms somewhere so they can be found by xgettext
*
*/
#include "motionplus.hpp"
#include "conf.hpp"
#include "logger.hpp"
#include "util.hpp"
#include "webu.hpp"
#include "webu_html.hpp"
/* struct to save information regarding the links to include in html page */
struct strminfo_ctx {
struct ctx_cam **camlst;
char lnk_ref[WEBUI_LEN_LNK];
char lnk_src[WEBUI_LEN_LNK];
char lnk_camid[WEBUI_LEN_LNK];
char proto[WEBUI_LEN_LNK];
int port;
int motion_images;
};
static void webu_html_style_navbar(struct webui_ctx *webui) {
/* Write out the style section of the web page */
char response[WEBUI_LEN_RESP];
snprintf(response, sizeof (response),"%s",
" .navbar {\n"
" overflow: hidden;\n"
" background-color: #333;\n"
" font-family: Arial;\n"
" }\n"
" .navbar a {\n"
" float: left;\n"
" font-size: 16px;\n"
" color: white;\n"
" text-align: center;\n"
" padding: 14px 16px;\n"
" text-decoration: none;\n"
" }\n"
" .navbar a:hover, {\n"
" background-color: darkgray;\n"
" }\n");
webu_write(webui, response);
}
static void webu_html_style_dropdown(struct webui_ctx *webui) {
/* Write out the style section of the web page */
char response[WEBUI_LEN_RESP];
snprintf(response, sizeof (response),"%s",
" .dropdown {\n"
" float: left;\n"
" overflow: hidden;\n"
" }\n"
" .dropdown .dropbtn {\n"
" font-size: 16px;\n"
" border: none;\n"
" outline: none;\n"
" color: white;\n"
" padding: 14px 16px;\n"
" background-color: inherit;\n"
" font-family: inherit;\n"
" margin: 0;\n"
" }\n"
" .dropdown-content {\n"
" display: none;\n"
" position: absolute;\n"
" background-color: #f9f9f9;\n"
" min-width: 160px;\n"
" box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);\n"
" z-index: 1;\n"
" }\n"
" .dropdown-content a {\n"
" float: none;\n"
" color: black;\n"
" padding: 12px 16px;\n"
" text-decoration: none;\n"
" display: block;\n"
" text-align: left;\n"
" }\n"
" .dropdown-content a:hover {\n"
" background-color: lightgray;\n"
" }\n"
" .dropdown:hover .dropbtn {\n"
" background-color: darkgray;\n"
" }\n"
" .border {\n"
" border-width: 2px;\n"
" border-color: white;\n"
" border-style: solid;\n"
" }\n");
webu_write(webui, response);
}
static void webu_html_style_input(struct webui_ctx *webui) {
/* Write out the style section of the web page */
char response[WEBUI_LEN_RESP];
snprintf(response, sizeof (response),"%s",
" input , select {\n"
" width: 25%;\n"
" padding: 5px;\n"
" margin: 0;\n"
" display: inline-block;\n"
" border: 1px solid #ccc;\n"
" border-radius: 4px;\n"
" box-sizing: border-box;\n"
" height: 50%;\n"
" font-size: 75%;\n"
" margin-bottom: 5px;\n"
" }\n"
" .frm-input{\n"
" text-align:center;\n"
" }\n");
webu_write(webui, response);
}
static void webu_html_style_base(struct webui_ctx *webui) {
/* Write out the style section of the web page */
char response[WEBUI_LEN_RESP];
snprintf(response, sizeof (response),"%s",
" * {margin: 0; padding: 0; }\n"
" body {\n"
" padding: 0;\n"
" margin: 0;\n"
" font-family: Arial, Helvetica, sans-serif;\n"
" font-size: 16px;\n"
" line-height: 1;\n"
" color: #606c71;\n"
" background-color: #159957;\n"
" background-image: linear-gradient(120deg, #155799, #159957);\n"
" margin-left:0.5% ;\n"
" margin-right:0.5% ;\n"
" width: device-width ;\n"
" }\n"
" img {\n"
" max-width: 100%;\n"
" max-height: 100%;\n"
" height: auto;\n"
" }\n"
" .page-header {\n"
" color: #fff;\n"
" text-align: center;\n"
" margin-top: 0rem;\n"
" margin-bottom: 0rem;\n"
" font-weight: normal;\n"
" }\n");
webu_write(webui, response);
snprintf(response, sizeof (response),"%s",
" .page-header h4 {\n"
" height: 2px;\n"
" padding: 0;\n"
" margin: 1rem 0;\n"
" border: 0;\n"
" }\n"
" .main-content {\n"
" background-color: #000000;\n"
" text-align: center;\n"
" margin-top: 0rem;\n"
" margin-bottom: 0rem;\n"
" font-weight: normal;\n"
" font-size: 0.90em;\n"
" }\n"
" .header-right{\n"
" float: right;\n"
" color: white;\n"
" }\n"
" .header-center {\n"
" text-align: center;\n"
" color: white;\n"
" margin-top: 10px;\n"
" margin-bottom: 10px;\n"
" }\n");
webu_write(webui, response);
}
static void webu_html_style(struct webui_ctx *webui) {
/* Write out the style section of the web page */
char response[WEBUI_LEN_RESP];
snprintf(response, sizeof (response),"%s", " \n");
webu_write(webui, response);
}
static void webu_html_head(struct webui_ctx *webui) {
/* Write out the header section of the web page */
char response[WEBUI_LEN_RESP];
snprintf(response, sizeof (response),"%s","
\n"
" \n"
" MotionPlus\n"
" \n");
webu_write(webui, response);
webu_html_style(webui);
snprintf(response, sizeof (response),"%s", "\n");
webu_write(webui, response);
}
static void webu_html_navbar_camera(struct webui_ctx *webui) {
/*Write out the options included in the camera dropdown */
char response[WEBUI_LEN_RESP];
int indx;
if (webui->cam_threads == 1){
/* Only MotionPlus.conf file */
if (webui->camlst[0]->conf->camera_name == ""){
snprintf(response, sizeof (response),
"
\n"
,_("Help"));
webu_write(webui, response);
}
static void webu_html_config_notice(struct webui_ctx *webui) {
/* Print out the header description of which parameters are included based upon the
* webcontrol_parms that was specified
*/
char response[WEBUI_LEN_RESP];
if (webui->camlst[0]->conf->webcontrol_parms == 0){
snprintf(response, sizeof (response),
"
\n"
,_("Restricted Configuration Options"));
}
webu_write(webui, response);
}
static void webu_html_h3desc(struct webui_ctx *webui) {
/* Write out the status description for the camera */
char response[WEBUI_LEN_RESP];
if (webui->cam_threads == 1){
snprintf(response, sizeof (response),
"
\n"
,_("All Cameras"));
webu_write(webui,response);
}
}
static void webu_html_config(struct webui_ctx *webui) {
/* Write out the options to put into the config dropdown
* We use html data attributes to store the values for the options
* We always set a cam_all00 attribute and if the value if different for
* any of our cameras, then we also add a cam_xxxxx which has the config
* value for camera xxxxx The javascript then decodes these to display
*/
char response[WEBUI_LEN_RESP];
int indx_parm, indx, diff_vals, retcd;
char val_main[PATH_MAX], val_thread[PATH_MAX];
char *val_temp;
snprintf(response, sizeof (response),"%s",
"
\n"
,_("Save"));
webu_write(webui, response);
}
static void webu_html_track(struct webui_ctx *webui) {
/* Write the section for handling the tracking function */
char response[WEBUI_LEN_RESP];
snprintf(response, sizeof (response),
"
\n"
" \n"
"
\n"
,_("Pan/Tilt")
,_("Absolute Change")
,_("Center")
,_("Pan")
,_("Tilt")
,_("Save"));
webu_write(webui, response);
}
static void webu_html_strminfo(struct strminfo_ctx *strm_info, int indx) {
/* This determines all the items we need to know to specify links and
* stream sources for the page. The options are 0-3 as of this writing
* where 0 = full streams, 1 = substreams, 2 = static images and 3 is
* the legacy code for creating streams. So we need to assign not only
* what images are to be sent but also whether we have tls/ssl.
* There are WAY too many options for this.
*/
/* If using the main port,we need to insert a thread number into url*/
if (strm_info->camlst[0]->conf->stream_port != 0) {
snprintf(strm_info->lnk_camid,WEBUI_LEN_LNK,"/%d"
,strm_info->camlst[indx]->camera_id);
strm_info->port = strm_info->camlst[0]->conf->stream_port;
if (strm_info->camlst[0]->conf->stream_tls) {
snprintf(strm_info->proto,WEBUI_LEN_LNK,"%s","https");
} else {
snprintf(strm_info->proto,WEBUI_LEN_LNK,"%s","http");
}
} else {
snprintf(strm_info->lnk_camid,WEBUI_LEN_LNK,"%s","");
strm_info->port = strm_info->camlst[indx]->conf->stream_port;
if (strm_info->camlst[indx]->conf->stream_tls) {
snprintf(strm_info->proto,WEBUI_LEN_LNK,"%s","https");
} else {
snprintf(strm_info->proto,WEBUI_LEN_LNK,"%s","http");
}
}
if (strm_info->motion_images){
snprintf(strm_info->lnk_ref,WEBUI_LEN_LNK,"%s","/motion");
snprintf(strm_info->lnk_src,WEBUI_LEN_LNK,"%s","/motion");
} else {
/* Assign what images and links we want */
if (strm_info->camlst[indx]->conf->stream_preview_method == 1){
/* Substream for preview */
snprintf(strm_info->lnk_ref,WEBUI_LEN_LNK,"%s","/stream");
snprintf(strm_info->lnk_src,WEBUI_LEN_LNK,"%s","/substream");
} else if (strm_info->camlst[indx]->conf->stream_preview_method == 2){
/* Static image for preview */
snprintf(strm_info->lnk_ref,WEBUI_LEN_LNK,"%s","/stream");
snprintf(strm_info->lnk_src,WEBUI_LEN_LNK,"%s","/current");
} else if (strm_info->camlst[indx]->conf->stream_preview_method == 4){
/* Source image for preview */
snprintf(strm_info->lnk_ref,WEBUI_LEN_LNK,"%s","/source");
snprintf(strm_info->lnk_src,WEBUI_LEN_LNK,"%s","/source");
} else {
/* Full stream for preview (method 0 or 3)*/
snprintf(strm_info->lnk_ref,WEBUI_LEN_LNK,"%s","/stream");
snprintf(strm_info->lnk_src,WEBUI_LEN_LNK,"%s","/stream");
}
}
}
static void webu_html_preview(struct webui_ctx *webui) {
/* Write the initial version of the preview section. The javascript
* will change this section when user selects a different camera */
char response[WEBUI_LEN_RESP];
int indx, indx_st, preview_scale;
struct strminfo_ctx strm_info;
strm_info.camlst = webui->camlst;
snprintf(response, sizeof (response),"%s",
"
\n"
"\n"
"\n");
webu_write(webui, response);
return;
}
void webu_html_main(struct webui_ctx *webui) {
/* Note some detection and config requested actions call the
* action function. This is because the legacy interface
* put these into those pages. We put them together here
* based upon the structure of the new interface
*/
int retcd;
retcd = 0;
if (strlen(webui->uri_camid) == 0){
webu_html_page(webui);
} else if ((mystreq(webui->uri_cmd1,"config")) &&
(mystreq(webui->uri_cmd2,"write"))) {
webu_process_action(webui);
} else if (mystreq(webui->uri_cmd1,"config")) {
retcd = webu_process_config(webui);
} else if (mystreq(webui->uri_cmd1,"action")){
webu_process_action(webui);
} else if (mystreq(webui->uri_cmd1,"detection")){
webu_process_action(webui);
} else if (mystreq(webui->uri_cmd1,"track")){
retcd = webu_process_track(webui);
} else{
MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO,
_("Invalid action requested: >%s< >%s< >%s<")
,webui->uri_camid, webui->uri_cmd1, webui->uri_cmd2);
retcd = -1;
}
if (retcd < 0) webu_html_badreq(webui);
return;
}