Merge branch 'master' of github.com:ZoneMinder/zoneminder

pull/4135/head
Isaac Connor 2024-09-16 13:45:06 -04:00
commit 62e0d87a6b
4 changed files with 320 additions and 243 deletions

View File

@ -38,6 +38,7 @@ set(ZM_BIN_SRC_FILES
zm_monitor_rtsp2web.cpp
zm_monitor_janus.cpp
zm_monitor_amcrest.cpp
zm_monitor_onvif.cpp
zm_monitorlink_expression.cpp
#zm_monitorlink_token.cpp
zm_monitorstream.cpp

View File

@ -322,9 +322,7 @@ Monitor::Monitor() :
RTSP2Web_Manager(nullptr),
Janus_Manager(nullptr),
Amcrest_Manager(nullptr),
#ifdef WITH_GSOAP
soap(nullptr),
#endif
onvif(nullptr),
red_val(0),
green_val(0),
blue_val(0),
@ -1180,97 +1178,7 @@ bool Monitor::connect() {
if (use_Amcrest_API) {
Amcrest_Manager = new AmcrestAPI(this);
} else { //using GSOAP
#ifdef WITH_GSOAP
tev__PullMessages.Timeout = "PT20S";
tev__PullMessages.MessageLimit = 10;
std::string Termination_time = "PT60S";
wsnt__Renew.TerminationTime = &Termination_time;
soap = soap_new();
soap->connect_timeout = 0;
soap->recv_timeout = 0;
soap->send_timeout = 0;
//soap->bind_flags |= SO_REUSEADDR;
soap_register_plugin(soap, soap_wsse);
if (soap_wsa_compl) {soap_register_plugin(soap, soap_wsa);};
proxyEvent = PullPointSubscriptionBindingProxy(soap);
if (!onvif_url.empty()) {
std::string full_url = onvif_url + onvif_events_path;
proxyEvent.soap_endpoint = full_url.c_str();
set_credentials(soap);
const char *RequestMessageID = soap_wsa_compl ? soap_wsa_rand_uuid(soap) : "RequestMessageID";
if ((!soap_wsa_compl) || (soap_wsa_request(soap, RequestMessageID, proxyEvent.soap_endpoint, "CreatePullPointSubscriptionRequest") == SOAP_OK)) {
Debug(1, "ONVIF Endpoint: %s", proxyEvent.soap_endpoint);
int rc = proxyEvent.CreatePullPointSubscription(&request, response);
if (rc != SOAP_OK) {
const char *detail = soap_fault_detail(soap);
Error("ONVIF Couldn't create subscription! %d, fault:%s, detail:%s", rc, soap_fault_string(soap), detail ? detail : "null");
_wsnt__Unsubscribe wsnt__Unsubscribe;
_wsnt__UnsubscribeResponse wsnt__UnsubscribeResponse;
proxyEvent.Unsubscribe(response.SubscriptionReference.Address, NULL, &wsnt__Unsubscribe, wsnt__UnsubscribeResponse);
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
soap = nullptr;
} else {
//Empty the stored messages
set_credentials(soap);
RequestMessageID = soap_wsa_compl ? soap_wsa_rand_uuid(soap):NULL;
if ((!soap_wsa_compl) || (soap_wsa_request(soap, RequestMessageID, response.SubscriptionReference.Address, "PullMessageRequest") == SOAP_OK)) {
Debug(1, "ONVIF :soap_wsa_request OK ");
if ((proxyEvent.PullMessages(response.SubscriptionReference.Address, NULL, &tev__PullMessages, tev__PullMessagesResponse) != SOAP_OK) &&
(soap->error != SOAP_EOF)
) { //SOAP_EOF could indicate no messages to pull.
Error("Couldn't do initial event pull! Error %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
Event_Poller_Healthy = false;
} else {
Debug(1, "Good Initial ONVIF Pull%i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
Event_Poller_Healthy = true;
}
} else {
Error("ONVIF Couldn't set wsa headers RequestMessageID= %s ; TO= %s ; Request= PullMessageRequest .... ! Error %i %s, %s",RequestMessageID, response.SubscriptionReference.Address, soap->error, soap_fault_string(soap), soap_fault_detail(soap));
Event_Poller_Healthy = false;
}
// we renew the current subscription .........
if (soap_wsa_compl) {
set_credentials(soap);
RequestMessageID = soap_wsa_rand_uuid(soap);
if (soap_wsa_request(soap, RequestMessageID, response.SubscriptionReference.Address, "RenewRequest") == SOAP_OK) {
Debug(1, "ONVIF :soap_wsa_request OK");
if (proxyEvent.Renew(response.SubscriptionReference.Address, NULL, &wsnt__Renew, wsnt__RenewResponse) != SOAP_OK) {
Error("ONVIF Couldn't do initial Renew ! Error %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
if (soap->error==12) {//ActionNotSupported
Event_Poller_Healthy = true;
} else {
Event_Poller_Healthy = false;
}
} else {
Debug(1, "Good Initial ONVIF Renew %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
Event_Poller_Healthy = true;
}
} else {
Error("ONVIF Couldn't set wsa headers RequestMessageID=%s; TO=%s; Request=RenewRequest Error %i %s, %s",
RequestMessageID,
response.SubscriptionReference.Address,
soap->error,
soap_fault_string(soap),
soap_fault_detail(soap));
Event_Poller_Healthy = false;
}// end renew
}
}
} else {
Error("ONVIF Couldn't set wsa headers RequestMessageID=%s; TO=%s; Request=CreatePullPointSubscriptionRequest Error %i %s, %s",
RequestMessageID, proxyEvent.soap_endpoint, soap->error, soap_fault_string(soap), soap_fault_detail(soap));
}
} else {
Warning("You must specify the url to the ONVIF endpoint");
}
#else
Error("zmc not compiled with GSOAP. ONVIF support not built in!");
#endif
onvif = new ONVIF(this);
} // end if Armcrest or GSOAP
} else {
Debug(1, "Not Starting ONVIF");
@ -1385,8 +1293,8 @@ Monitor::~Monitor() {
}
if (Amcrest_Manager != nullptr) {
delete Amcrest_Manager;
} else {
}
if (onvif) delete onvif;
} // end Monitor::~Monitor()
void Monitor::AddPrivacyBitmask() {
@ -1962,92 +1870,22 @@ bool Monitor::Poll() {
// We want to trigger every 5 seconds or so. so grab the time at the beginning of the loop, and sleep at the end.
std::chrono::system_clock::time_point loop_start_time = std::chrono::system_clock::now();
if (Event_Poller_Healthy) {
if (use_Amcrest_API) {
if (Event_Poller_Healthy) {
Amcrest_Manager->WaitForMessage();
} else {
#ifdef WITH_GSOAP
set_credentials(soap);
const char *RequestMessageID = soap_wsa_compl ? soap_wsa_rand_uuid(soap) : "RequestMessageID";
if ((!soap_wsa_compl) || (soap_wsa_request(soap, RequestMessageID, response.SubscriptionReference.Address, "PullMessageRequest") == SOAP_OK)) {
Debug(1, ":soap_wsa_request OK; starting ONVIF PullMessageRequest ...");
int result = proxyEvent.PullMessages(response.SubscriptionReference.Address, NULL, &tev__PullMessages, tev__PullMessagesResponse);
if (result != SOAP_OK) {
const char *detail = soap_fault_detail(soap);
Debug(1, "Result of getting ONVIF messages= %d soap_fault_string=%s detail=%s",
result, soap_fault_string(soap), detail ? detail : "null");
if (result != SOAP_EOF) { //Ignore the timeout error
Error("Failed to get ONVIF messages! %d %s", result, soap_fault_string(soap));
// Event_Poller_Healthy = false;
delete Amcrest_Manager;
Amcrest_Manager = new AmcrestAPI(this);
}
} else if (onvif) {
if (onvif->isHealthy()) {
onvif->WaitForMessage();
} else {
Debug(1, "ONVIF polling : Got Good Response! %i", result);
for (auto msg : tev__PullMessagesResponse.wsnt__NotificationMessage) {
if ((msg->Topic != nullptr) &&
(msg->Topic->__any.text != nullptr) &&
std::strstr(msg->Topic->__any.text, onvif_alarm_txt.c_str()) &&
(msg->Message.__any.elts != nullptr) &&
(msg->Message.__any.elts->next != nullptr) &&
(msg->Message.__any.elts->next->elts != nullptr) &&
(msg->Message.__any.elts->next->elts->atts != nullptr) &&
(msg->Message.__any.elts->next->elts->atts->next != nullptr) &&
(msg->Message.__any.elts->next->elts->atts->next->text != nullptr)
) {
Info("Got Motion Alarm!");
if (strcmp(msg->Message.__any.elts->next->elts->atts->next->text, "true") == 0) {
//Event Start
Info("Triggered on ONVIF");
if (!Poll_Trigger_State) {
Info("Triggered Event");
Poll_Trigger_State = true;
std::this_thread::sleep_for(std::chrono::seconds(1)); //thread sleep
delete onvif;
onvif = new ONVIF(this);
onvif->start();
}
} else {
Info("Triggered off ONVIF");
Poll_Trigger_State = false;
if (!Event_Poller_Closes_Event) { //If we get a close event, then we know to expect them.
Event_Poller_Closes_Event = true;
Info("Setting ClosesEvent");
}
}
} else {
Debug(1, "Got a message that we couldn't parse");
}
} // end foreach msg
// we renew the current subscription .........
if (soap_wsa_compl) {
set_credentials(soap);
std::string Termination_time = "PT60S";
wsnt__Renew.TerminationTime = &Termination_time;
RequestMessageID = soap_wsa_compl ? soap_wsa_rand_uuid(soap) : "RequestMessageID";
if ((!soap_wsa_compl) || (soap_wsa_request(soap, RequestMessageID, response.SubscriptionReference.Address, "RenewRequest") == SOAP_OK)) {
Debug(1, ":soap_wsa_request OK");
if (proxyEvent.Renew(response.SubscriptionReference.Address, NULL, &wsnt__Renew, wsnt__RenewResponse) != SOAP_OK) {
Error("Couldn't do Renew! Error %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
if (soap->error==12) {//ActionNotSupported
Event_Poller_Healthy = true;
} else {
Event_Poller_Healthy = false;
}
} else {
Debug(1, "Good Renew ONVIF Renew %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
Event_Poller_Healthy = true;
}
} else {
Error("Couldn't set wsa headers RequestMessageID=%s; TO=%s; Request= RenewRequest .... ! Error %i %s, %s",
RequestMessageID, response.SubscriptionReference.Address, soap->error, soap_fault_string(soap), soap_fault_detail(soap));
Event_Poller_Healthy = false;
} // end renew
}
} // end if SOAP OK/NOT OK
} else {
Error("Couldn't set wsa headers RequestMessageID= %s ; TO= %s ; Request= PullMessageRequest .... ! Error %i %s, %s",
RequestMessageID, response.SubscriptionReference.Address, soap->error, soap_fault_string(soap), soap_fault_detail(soap));
} // end if soap == OK
#endif
} // end if Amcrest or not
} // end if Healthy
if (RTSP2Web_enabled and RTSP2Web_Manager) {
Debug(1, "Trying to check RTSP2Web in Poller");
@ -2113,11 +1951,11 @@ bool Monitor::Analyse() {
#ifdef WITH_GSOAP
if (onvif_event_listener && Event_Poller_Healthy) {
if (Poll_Trigger_State) {
if ((onvif and onvif->isAlarmed()) or Poll_Trigger_State) {
score += 9;
Debug(4, "Triggered on ONVIF");
Event::StringSet noteSet;
noteSet.insert("ONVIF2");
noteSet.insert("ONVIF");
noteSetMap[MOTION_CAUSE] = noteSet;
cause += "ONVIF";
// If the camera isn't going to send an event close, we need to close it here, but only after it has actually triggered an alarm.
@ -3597,6 +3435,7 @@ int Monitor::Play() {
return 1;
} // end int Monitor::Play()
// Close can be called multiple times, for example before object delete, and then in the destructor.
int Monitor::Close() {
Pause();
@ -3605,25 +3444,11 @@ int Monitor::Close() {
Poller->Stop();
Debug(1, "Poller stopped");
}
#ifdef WITH_GSOAP
if (onvif_event_listener && (soap != nullptr)) {
Debug(1, "Tearing Down Onvif");
_wsnt__Unsubscribe wsnt__Unsubscribe;
_wsnt__UnsubscribeResponse wsnt__UnsubscribeResponse;
const char *RequestMessageID = soap_wsa_compl ? soap_wsa_rand_uuid(soap) : "RequestMessageID";
if ((!soap_wsa_compl) || (soap_wsa_request(soap, RequestMessageID, response.SubscriptionReference.Address, "UnsubscribeRequest") == SOAP_OK)) {
proxyEvent.Unsubscribe(response.SubscriptionReference.Address, NULL, &wsnt__Unsubscribe, wsnt__UnsubscribeResponse);
} else {
Error("Couldn't set wsa headers RequestMessageID=%s; TO= %s; Request=UnsubscribeRequest .... ! Error %i %s, %s",
RequestMessageID, response.SubscriptionReference.Address, soap->error, soap_fault_string(soap), soap_fault_detail(soap));
if (onvif) {
delete onvif;
onvif = nullptr;
}
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
soap = nullptr;
} //End ONVIF
#endif
// RTSP2Web teardown
if (RTSP2Web_enabled and (purpose == CAPTURE) and RTSP2Web_Manager) {
delete RTSP2Web_Manager;
@ -3732,33 +3557,3 @@ StringVector Monitor::GroupNames() {
return groupnames;
} // end Monitor::GroupNames()
#ifdef WITH_GSOAP
//ONVIF Set Credentials
void Monitor::set_credentials(struct soap *soap) {
soap_wsse_delete_Security(soap);
soap_wsse_add_Timestamp(soap, NULL, 10);
soap_wsse_add_UsernameTokenDigest(soap, "Auth", onvif_username.c_str(), onvif_password.c_str());
}
//GSOAP boilerplate
int SOAP_ENV__Fault(struct soap *soap, char *faultcode, char *faultstring, char *faultactor, struct SOAP_ENV__Detail *detail, struct SOAP_ENV__Code *SOAP_ENV__Code, struct SOAP_ENV__Reason *SOAP_ENV__Reason, char *SOAP_ENV__Node, char *SOAP_ENV__Role, struct SOAP_ENV__Detail *SOAP_ENV__Detail) {
// populate the fault struct from the operation arguments to print it
soap_fault(soap);
// SOAP 1.1
soap->fault->faultcode = faultcode;
soap->fault->faultstring = faultstring;
soap->fault->faultactor = faultactor;
soap->fault->detail = detail;
// SOAP 1.2
soap->fault->SOAP_ENV__Code = SOAP_ENV__Code;
soap->fault->SOAP_ENV__Reason = SOAP_ENV__Reason;
soap->fault->SOAP_ENV__Node = SOAP_ENV__Node;
soap->fault->SOAP_ENV__Role = SOAP_ENV__Role;
soap->fault->SOAP_ENV__Detail = SOAP_ENV__Detail;
// set error
soap->error = SOAP_FAULT;
// handle or display the fault here with soap_stream_fault(soap, std::cerr);
// return HTTP 202 Accepted
return soap_send_empty_response(soap, SOAP_OK);
}
#endif

View File

@ -321,6 +321,31 @@ class Monitor : public std::enable_shared_from_this<Monitor> {
};
protected:
class ONVIF {
protected:
Monitor *parent;
bool alarmed;
bool healthy;
#ifdef WITH_GSOAP
struct soap *soap = nullptr;
_tev__CreatePullPointSubscription request;
_tev__CreatePullPointSubscriptionResponse response;
_tev__PullMessages tev__PullMessages;
_tev__PullMessagesResponse tev__PullMessagesResponse;
_wsnt__Renew wsnt__Renew;
_wsnt__RenewResponse wsnt__RenewResponse;
PullPointSubscriptionBindingProxy proxyEvent;
void set_credentials(struct soap *soap);
#endif
public:
explicit ONVIF(Monitor *parent_);
~ONVIF();
void start();
void WaitForMessage();
bool isAlarmed() const { return alarmed; };
bool isHealthy() const { return healthy; };
};
class AmcrestAPI {
protected:
Monitor *parent;
@ -613,19 +638,7 @@ class Monitor : public std::enable_shared_from_this<Monitor> {
RTSP2WebManager *RTSP2Web_Manager;
JanusManager *Janus_Manager;
AmcrestAPI *Amcrest_Manager;
#ifdef WITH_GSOAP
struct soap *soap = nullptr;
_tev__CreatePullPointSubscription request;
_tev__CreatePullPointSubscriptionResponse response;
_tev__PullMessages tev__PullMessages;
_tev__PullMessagesResponse tev__PullMessagesResponse;
_wsnt__Renew wsnt__Renew;
_wsnt__RenewResponse wsnt__RenewResponse;
PullPointSubscriptionBindingProxy proxyEvent;
void set_credentials(struct soap *soap);
#endif
ONVIF *onvif;
// Used in check signal
uint8_t red_val;

268
src/zm_monitor_onvif.cpp Normal file
View File

@ -0,0 +1,268 @@
//
// ZoneMinder Monitor::ONVIF Class Implementation
// Copyright (C) 2024 ZoneMinder Inc
//
// 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_monitor.h"
#include <cstring>
Monitor::ONVIF::ONVIF(Monitor *parent_) :
parent(parent_),
alarmed(false),
healthy(false),
#ifdef WITH_GSOAP
soap(nullptr)
#endif
{
}
Monitor::ONVIF::~ONVIF() {
#ifdef WITH_GSOAP
if (soap != nullptr) {
Debug(1, "Tearing Down Onvif");
_wsnt__Unsubscribe wsnt__Unsubscribe;
_wsnt__UnsubscribeResponse wsnt__UnsubscribeResponse;
const char *RequestMessageID = parent->soap_wsa_compl ? soap_wsa_rand_uuid(soap) : "RequestMessageID";
if ((!parent->soap_wsa_compl) || (soap_wsa_request(soap, RequestMessageID, response.SubscriptionReference.Address, "UnsubscribeRequest") == SOAP_OK)) {
proxyEvent.Unsubscribe(response.SubscriptionReference.Address, nullptr, &wsnt__Unsubscribe, wsnt__UnsubscribeResponse);
} else {
Error("Couldn't set wsa headers RequestMessageID=%s; TO= %s; Request=UnsubscribeRequest .... ! Error %i %s, %s",
RequestMessageID, response.SubscriptionReference.Address, soap->error, soap_fault_string(soap), soap_fault_detail(soap));
}
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
soap = nullptr;
} // end if soap
#endif
}
void Monitor::ONVIF::start() {
#ifdef WITH_GSOAP
tev__PullMessages.Timeout = "PT20S";
tev__PullMessages.MessageLimit = 10;
std::string Termination_time = "PT60S";
wsnt__Renew.TerminationTime = &Termination_time;
soap = soap_new();
soap->connect_timeout = 0;
soap->recv_timeout = 0;
soap->send_timeout = 0;
//soap->bind_flags |= SO_REUSEADDR;
soap_register_plugin(soap, soap_wsse);
if (parent->soap_wsa_compl) {soap_register_plugin(soap, soap_wsa);};
proxyEvent = PullPointSubscriptionBindingProxy(soap);
if (!parent->onvif_url.empty()) {
std::string full_url = parent->onvif_url + parent->onvif_events_path;
proxyEvent.soap_endpoint = full_url.c_str();
set_credentials(soap);
const char *RequestMessageID = parent->soap_wsa_compl ? soap_wsa_rand_uuid(soap) : "RequestMessageID";
if ((!parent->soap_wsa_compl) || (soap_wsa_request(soap, RequestMessageID, proxyEvent.soap_endpoint, "CreatePullPointSubscriptionRequest") == SOAP_OK)) {
Debug(1, "ONVIF Endpoint: %s", proxyEvent.soap_endpoint);
int rc = proxyEvent.CreatePullPointSubscription(&request, response);
if (rc != SOAP_OK) {
const char *detail = soap_fault_detail(soap);
Error("ONVIF Couldn't create subscription! %d, fault:%s, detail:%s", rc, soap_fault_string(soap), detail ? detail : "null");
_wsnt__Unsubscribe wsnt__Unsubscribe;
_wsnt__UnsubscribeResponse wsnt__UnsubscribeResponse;
proxyEvent.Unsubscribe(response.SubscriptionReference.Address, nullptr, &wsnt__Unsubscribe, wsnt__UnsubscribeResponse);
soap_destroy(soap);
soap_end(soap);
soap_free(soap);
soap = nullptr;
} else {
//Empty the stored messages
set_credentials(soap);
RequestMessageID = parent->soap_wsa_compl ? soap_wsa_rand_uuid(soap):nullptr;
if ((!parent->soap_wsa_compl) || (soap_wsa_request(soap, RequestMessageID, response.SubscriptionReference.Address, "PullMessageRequest") == SOAP_OK)) {
Debug(1, "ONVIF :soap_wsa_request OK ");
if ((proxyEvent.PullMessages(response.SubscriptionReference.Address, nullptr, &tev__PullMessages, tev__PullMessagesResponse) != SOAP_OK) &&
(soap->error != SOAP_EOF)
) { //SOAP_EOF could indicate no messages to pull.
Error("Couldn't do initial event pull! Error %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
healthy = false;
} else {
Debug(1, "Good Initial ONVIF Pull%i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
healthy = true;
}
} else {
Error("ONVIF Couldn't set wsa headers RequestMessageID= %s ; TO= %s ; Request= PullMessageRequest .... ! Error %i %s, %s",RequestMessageID, response.SubscriptionReference.Address, soap->error, soap_fault_string(soap), soap_fault_detail(soap));
healthy = false;
}
// we renew the current subscription .........
if (parent->soap_wsa_compl) {
set_credentials(soap);
RequestMessageID = soap_wsa_rand_uuid(soap);
if (soap_wsa_request(soap, RequestMessageID, response.SubscriptionReference.Address, "RenewRequest") == SOAP_OK) {
Debug(1, "ONVIF :soap_wsa_request OK");
if (proxyEvent.Renew(response.SubscriptionReference.Address, nullptr, &wsnt__Renew, wsnt__RenewResponse) != SOAP_OK) {
Error("ONVIF Couldn't do initial Renew ! Error %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
if (soap->error==12) {//ActionNotSupported
healthy = true;
} else {
healthy = false;
}
} else {
Debug(1, "Good Initial ONVIF Renew %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
healthy = true;
}
} else {
Error("ONVIF Couldn't set wsa headers RequestMessageID=%s; TO=%s; Request=RenewRequest Error %i %s, %s",
RequestMessageID,
response.SubscriptionReference.Address,
soap->error,
soap_fault_string(soap),
soap_fault_detail(soap));
healthy = false;
} // end renew
}
}
} else {
Error("ONVIF Couldn't set wsa headers RequestMessageID=%s; TO=%s; Request=CreatePullPointSubscriptionRequest Error %i %s, %s",
RequestMessageID, proxyEvent.soap_endpoint, soap->error, soap_fault_string(soap), soap_fault_detail(soap));
}
} else {
Warning("You must specify the url to the ONVIF endpoint");
}
#else
Error("zmc not compiled with GSOAP. ONVIF support not built in!");
#endif
}
void Monitor::ONVIF::WaitForMessage() {
#ifdef WITH_GSOAP
set_credentials(soap);
const char *RequestMessageID = parent->soap_wsa_compl ? soap_wsa_rand_uuid(soap) : "RequestMessageID";
if ((!parent->soap_wsa_compl) || (soap_wsa_request(soap, RequestMessageID, response.SubscriptionReference.Address, "PullMessageRequest") == SOAP_OK)) {
Debug(1, ":soap_wsa_request OK; starting ONVIF PullMessageRequest ...");
int result = proxyEvent.PullMessages(response.SubscriptionReference.Address, nullptr, &tev__PullMessages, tev__PullMessagesResponse);
if (result != SOAP_OK) {
const char *detail = soap_fault_detail(soap);
Debug(1, "Result of getting ONVIF messages= %d soap_fault_string=%s detail=%s",
result, soap_fault_string(soap), detail ? detail : "null");
if (result != SOAP_EOF) { //Ignore the timeout error
Error("Failed to get ONVIF messages! %d %s", result, soap_fault_string(soap));
// healthy = false;
}
} else {
Debug(1, "ONVIF polling : Got Good Response! %i", result);
for (auto msg : tev__PullMessagesResponse.wsnt__NotificationMessage) {
if ((msg->Topic != nullptr) &&
(msg->Topic->__any.text != nullptr) &&
std::strstr(msg->Topic->__any.text, parent->onvif_alarm_txt.c_str()) &&
(msg->Message.__any.elts != nullptr) &&
(msg->Message.__any.elts->next != nullptr) &&
(msg->Message.__any.elts->next->elts != nullptr) &&
(msg->Message.__any.elts->next->elts->atts != nullptr) &&
(msg->Message.__any.elts->next->elts->atts->next != nullptr) &&
(msg->Message.__any.elts->next->elts->atts->next->text != nullptr)
) {
Info("Got Motion Alarm!");
if (strcmp(msg->Message.__any.elts->next->elts->atts->next->text, "true") == 0) {
// Event Start
Info("Triggered on ONVIF");
if (!alarmed) {
Info("Triggered Event");
alarmed = true;
// Why sleep?
std::this_thread::sleep_for(std::chrono::seconds(1)); //thread sleep
}
} else {
Info("Triggered off ONVIF");
alarmed = false;
if (!parent->Event_Poller_Closes_Event) { //If we get a close event, then we know to expect them.
parent->Event_Poller_Closes_Event = true;
Info("Setting ClosesEvent");
}
}
} else {
Debug(1, "Got a message that we couldn't parse");
if ((msg->Topic != nullptr) && (msg->Topic->__any.text != nullptr)) {
Debug(1, "text was %s", msg->Topic->__any.text);
}
}
} // end foreach msg
// we renew the current subscription .........
if (parent->soap_wsa_compl) {
set_credentials(soap);
std::string Termination_time = "PT60S";
wsnt__Renew.TerminationTime = &Termination_time;
RequestMessageID = parent->soap_wsa_compl ? soap_wsa_rand_uuid(soap) : "RequestMessageID";
if ((!parent->soap_wsa_compl) || (soap_wsa_request(soap, RequestMessageID, response.SubscriptionReference.Address, "RenewRequest") == SOAP_OK)) {
Debug(1, ":soap_wsa_request OK");
if (proxyEvent.Renew(response.SubscriptionReference.Address, nullptr, &wsnt__Renew, wsnt__RenewResponse) != SOAP_OK) {
Error("Couldn't do Renew! Error %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
if (soap->error==12) {//ActionNotSupported
healthy = true;
} else {
healthy = false;
}
} else {
Debug(1, "Good Renew ONVIF Renew %i %s, %s", soap->error, soap_fault_string(soap), soap_fault_detail(soap));
healthy = true;
}
} else {
Error("Couldn't set wsa headers RequestMessageID=%s; TO=%s; Request= RenewRequest .... ! Error %i %s, %s",
RequestMessageID, response.SubscriptionReference.Address, soap->error, soap_fault_string(soap), soap_fault_detail(soap));
healthy = false;
} // end renew
}
} // end if SOAP OK/NOT OK
} else {
Error("Couldn't set wsa headers RequestMessageID= %s ; TO= %s ; Request= PullMessageRequest .... ! Error %i %s, %s",
RequestMessageID, response.SubscriptionReference.Address, soap->error, soap_fault_string(soap), soap_fault_detail(soap));
} // end if soap == OK
#endif
return;
}
#ifdef WITH_GSOAP
//ONVIF Set Credentials
void Monitor::ONVIF::set_credentials(struct soap *soap) {
soap_wsse_delete_Security(soap);
soap_wsse_add_Timestamp(soap, nullptr, 10);
soap_wsse_add_UsernameTokenDigest(soap, "Auth", parent->onvif_username.c_str(), parent->onvif_password.c_str());
}
//GSOAP boilerplate
int SOAP_ENV__Fault(struct soap *soap, char *faultcode, char *faultstring, char *faultactor, struct SOAP_ENV__Detail *detail, struct SOAP_ENV__Code *SOAP_ENV__Code, struct SOAP_ENV__Reason *SOAP_ENV__Reason, char *SOAP_ENV__Node, char *SOAP_ENV__Role, struct SOAP_ENV__Detail *SOAP_ENV__Detail) {
// populate the fault struct from the operation arguments to print it
soap_fault(soap);
// SOAP 1.1
soap->fault->faultcode = faultcode;
soap->fault->faultstring = faultstring;
soap->fault->faultactor = faultactor;
soap->fault->detail = detail;
// SOAP 1.2
soap->fault->SOAP_ENV__Code = SOAP_ENV__Code;
soap->fault->SOAP_ENV__Reason = SOAP_ENV__Reason;
soap->fault->SOAP_ENV__Node = SOAP_ENV__Node;
soap->fault->SOAP_ENV__Role = SOAP_ENV__Role;
soap->fault->SOAP_ENV__Detail = SOAP_ENV__Detail;
// set error
soap->error = SOAP_FAULT;
// handle or display the fault here with soap_stream_fault(soap, std::cerr);
// return HTTP 202 Accepted
return soap_send_empty_response(soap, SOAP_OK);
}
#endif