Files
zoneminder/src/zm_monitor_rtsp2web.cpp
copilot-swe-agent[bot] d9c9b4087e fix: add HAVE_LIBCURL preprocessor guards to fix ZM_NO_CURL=ON build
Wraps all libcurl usage in #if HAVE_LIBCURL / #endif guards, matching
the existing pattern used for HAVE_LIBVLC and HAVE_LIBVNC.

Files changed:
- src/zm_monitor.h: guard curl include, 4 inner class declarations,
  4 manager member pointers, check_janus() decl, EventPollerHealthy()
- src/zmc.cpp: guard curl_global_init/cleanup calls
- src/zm_monitor.cpp: guard initializer list, instantiation, destructor,
  Poll(), Analyse(), Poller thread condition, and Close() teardown
- src/zm_monitor_amcrest.cpp: wrap implementation in HAVE_LIBCURL guard
- src/zm_monitor_janus.cpp: wrap implementation in HAVE_LIBCURL guard
- src/zm_monitor_rtsp2web.cpp: wrap implementation in HAVE_LIBCURL guard
- src/zm_monitor_go2rtc.cpp: wrap implementation in HAVE_LIBCURL guard

Verified: builds succeed with both ZM_NO_CURL=ON and ZM_NO_CURL=OFF

refs #4967

Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
2026-03-05 02:13:59 +00:00

264 lines
9.7 KiB
C++

//
// ZoneMinder Monitor::RTSP2WebManager Class Implementation, $Date$, $Revision$
// Copyright (C) 2022 Jonathan Bennett
//
// This program 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 2
// of the License, or (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#include "zm_crypt.h"
#include "zm_monitor.h"
#include "zm_server.h"
#include "zm_time.h"
#include "zm_utils.h"
#include <algorithm>
#include <regex>
#if HAVE_LIBCURL
Monitor::RTSP2WebManager::RTSP2WebManager(Monitor *parent_) :
parent(parent_),
RTSP2Web_Healthy(false),
Use_RTSP_Restream(false)
{
if ((config.rtsp2web_path != nullptr) && (config.rtsp2web_path[0] != '\0')) {
RTSP2Web_endpoint = config.rtsp2web_path;
//remove the trailing slash if present
if (RTSP2Web_endpoint.back() == '/') RTSP2Web_endpoint.pop_back();
} else {
RTSP2Web_endpoint = "demo:demo@127.0.0.1:8083";
}
rtsp_path = parent->path;
rtsp_second_path = parent->GetSecondPath();
if (!parent->user.empty()) {
rtsp_username = escape_json_string(parent->user);
rtsp_password = escape_json_string(parent->pass);
if (rtsp_path.find("rtsp://") == 0) {
rtsp_path = "rtsp://" + rtsp_username + ":" + rtsp_password + "@" + rtsp_path.substr(7, std::string::npos);
} else {
rtsp_path = "rtsp://" + rtsp_username + ":" + rtsp_password + "@" + rtsp_path;
}
if (!rtsp_second_path.empty()) {
if (rtsp_second_path.find("rtsp://") == 0) {
rtsp_second_path = "rtsp://" + rtsp_username + ":" + rtsp_password + "@" + rtsp_second_path.substr(7, std::string::npos);
} else {
rtsp_second_path = "rtsp://" + rtsp_username + ":" + rtsp_password + "@" + rtsp_second_path;
}
}
} // end if !user.empty
Debug(1, "Monitor %u rtsp url is %s", parent->id, rtsp_path.c_str());
}
Monitor::RTSP2WebManager::~RTSP2WebManager() {
remove_from_RTSP2Web();
}
int Monitor::RTSP2WebManager::check_RTSP2Web() {
curl = curl_easy_init();
if (!curl) {
Error("Failed to init curl");
return -1;
}
//Assemble our actual request
std::string response;
std::string endpoint = RTSP2Web_endpoint+"/stream/"+std::to_string(parent->id)+"/info";
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
if (ssl_verification_failed) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
} else {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
}
CURLcode res = curl_easy_perform(curl);
// If SSL verification failed, retry without verification and remember for future calls
if (!ssl_verification_failed &&
(res == CURLE_SSL_CACERT || res == CURLE_SSL_PEER_CERTIFICATE || res == CURLE_SSL_CACERT_BADFILE ||
res == CURLE_SSL_CERTPROBLEM || res == CURLE_PEER_FAILED_VERIFICATION)) {
Warning("RTSP2Web: SSL certificate verification failed for %s (%s), retrying without verification",
endpoint.c_str(), curl_easy_strerror(res));
ssl_verification_failed = true;
response.clear();
curl_easy_reset(curl);
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
res = curl_easy_perform(curl);
}
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
Warning("Attempted to send to %s and got %s", endpoint.c_str(), curl_easy_strerror(res));
return -1;
}
response = remove_newlines(response);
Debug(1, "Queried for stream status: %s", response.c_str());
if (response.find("\"status\": 0") != std::string::npos) {
if (response.find("stream not found") != std::string::npos) {
Debug(1, "Mountpoint Missing");
return 0;
} else {
Warning("unknown RTSP2Web error");
return 0;
}
}
return 1;
}
int Monitor::RTSP2WebManager::add_to_RTSP2Web() {
Debug(1, "Adding stream to RTSP2Web");
curl = curl_easy_init();
if (!curl) {
Error("Failed to init curl");
return -1;
}
std::string endpoint = RTSP2Web_endpoint+"/stream/"+std::to_string(parent->id)+"/add";
//Assemble our actual request
std::string postData = "{\"name\" : \"" + std::string(parent->Name()) + "\", \"channels\" : {"
" \"0\" : {"
" \"name\" : \"ch1\", \"audio\" : true, \"url\" : \"" + rtsp_path + "\", \"on_demand\": true, \"debug\": false, \"status\": 0}";
if (!rtsp_second_path.empty()) {
postData += ", \"1\" : {"
" \"name\" : \"ch2\", \"audio\" : true, \"url\" : \"" + rtsp_second_path + "\", \"on_demand\": true, \"debug\": false, \"status\": 0}";
}
postData += "}" "}";
Debug(1, "Sending %s to %s", postData.c_str(), endpoint.c_str());
CURLcode res;
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
if (ssl_verification_failed) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
} else {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
}
res = curl_easy_perform(curl);
// If SSL verification failed, retry without verification and remember for future calls
if (!ssl_verification_failed &&
(res == CURLE_SSL_CACERT || res == CURLE_SSL_PEER_CERTIFICATE || res == CURLE_SSL_CACERT_BADFILE ||
res == CURLE_SSL_CERTPROBLEM || res == CURLE_PEER_FAILED_VERIFICATION)) {
Warning("RTSP2Web: SSL certificate verification failed for %s (%s), retrying without verification",
endpoint.c_str(), curl_easy_strerror(res));
ssl_verification_failed = true;
response.clear();
curl_easy_reset(curl);
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
res = curl_easy_perform(curl);
}
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
Error("Failed to curl_easy_perform adding rtsp stream %s", curl_easy_strerror(res));
return -1;
}
response = remove_newlines(response);
Debug(1, "Adding stream response: %s", response.c_str());
//scan for missing session or handle id "No such session" "no such handle"
if (response.find("\"status\": 1") == std::string::npos) {
if (response == "{ \"status\": 0, \"payload\": \"stream already exists\"}") {
Debug(1, "RTSP2Web failed adding stream, response: %s", response.c_str());
} else {
Warning("RTSP2Web failed adding stream, response: %s", response.c_str());
return -2;
}
} else {
Debug(1, "Added stream to RTSP2Web: %s", response.c_str());
}
return 0;
}
int Monitor::RTSP2WebManager::remove_from_RTSP2Web() {
curl = curl_easy_init();
if (!curl) return -1;
std::string endpoint = RTSP2Web_endpoint+"/stream/"+std::to_string(parent->id)+"/delete";
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
if (ssl_verification_failed) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
} else {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
}
CURLcode res = curl_easy_perform(curl);
// If SSL verification failed, retry without verification and remember for future calls
if (!ssl_verification_failed &&
(res == CURLE_SSL_CACERT || res == CURLE_SSL_PEER_CERTIFICATE || res == CURLE_SSL_CACERT_BADFILE ||
res == CURLE_SSL_CERTPROBLEM || res == CURLE_PEER_FAILED_VERIFICATION)) {
Warning("RTSP2Web: SSL certificate verification failed for %s (%s), retrying without verification",
endpoint.c_str(), curl_easy_strerror(res));
ssl_verification_failed = true;
response.clear();
curl_easy_reset(curl);
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
res = curl_easy_perform(curl);
}
if (res != CURLE_OK) {
Warning("Libcurl attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
} else {
Debug(1, "Removed stream from RTSP2Web: %s", remove_newlines(response).c_str());
}
curl_easy_cleanup(curl);
return 0;
}
size_t Monitor::RTSP2WebManager::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
#endif // HAVE_LIBCURL