diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js
index 76fc47c79..96f56dbd4 100644
--- a/web/skins/classic/views/js/event.js
+++ b/web/skins/classic/views/js/event.js
@@ -200,6 +200,19 @@ function changeReplayMode() {
refreshWindow();
}
+function changeRate() {
+ var rate = $j('select[name="rate"]').val();
+ if ( ! rate ) {
+ pauseClicked();
+ } else {
+ if ( vid ) {
+ vid.playbackRate(rate/100);
+ Cookie.write('zmEventRate', rate, {duration: 10*365});
+ }
+ }
+}
+
+
var streamParms = "view=request&request=stream&connkey="+connKey;
if ( auth_hash ) {
streamParms += '&auth='+auth_hash;
@@ -245,7 +258,7 @@ function getCmdResponse( respObj, respText ) {
if ( streamStatus.paused == true ) {
streamPause( );
} else {
- $j('#rateValue').html(streamStatus.rate);
+ $j('select[name="rate"]').val(streamStatus.rate*100);
Cookie.write('zmEventRate', streamStatus.rate*100, {duration: 10*365});
streamPlay( );
}
@@ -296,7 +309,7 @@ function vjsPause() {
function streamPause( ) {
$j('#modeValue').html('Paused');
- $j('#rateValue').html('0');
+ $j('select[name="rate"]').val('0');
setButtonState( $('pauseBtn'), 'active' );
setButtonState( $('playBtn'), 'inactive' );
setButtonState( $('fastFwdBtn'), 'unavail' );
@@ -322,7 +335,7 @@ function vjsPlay() { //catches if we change mode programatically
if ( intervalRewind ) {
stopFastRev();
}
- $j('#rateValue').html(vid.playbackRate());
+ $j('select[name="rate"]').val(vid.playbackRate());
Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365});
streamPlay();
}
@@ -350,7 +363,7 @@ function streamFastFwd( action ) {
if ( rates.indexOf(vid.playbackRate()*100)-1 == -1 ) {
setButtonState($('fastFwdBtn'), 'unavail');
}
- $j('#rateValue').html(vid.playbackRate());
+ $j('#rate').val(vid.playbackRate());
Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365});
} else {
streamReq.send(streamParms+"&command="+CMD_FASTFWD);
@@ -397,7 +410,7 @@ function streamFastRev( action ) {
setButtonState( $('fastRevBtn'), 'unavail' );
}
clearInterval(intervalRewind);
- $j('#rateValue').html(-revSpeed);
+ $j('#rate').val(-revSpeed);
Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365});
intervalRewind = setInterval(function() {
if (vid.currentTime() <= 0) {
@@ -588,7 +601,7 @@ function getEventResponse( respObj, respText ) {
CurEventDefVideoPath = null;
$j('#modeValue').html('Replay');
$j('#zoomValue').html('1');
- $j('#rateValue').html('1');
+ $j('#rate').val('100');
vjsPanZoom('zoomOut');
} else {
drawProgressBar();
@@ -1066,7 +1079,8 @@ function initPage() {
$j('#progressValue').html(secsToTime(Math.floor(vid.currentTime())));
});
- if ( rate > 1 ) {
+ // rate is in % so 100 would be 1x
+ if ( rate > 0 ) {
// rate should be 100 = 1x, etc.
vid.playbackRate(rate/100);
}
@@ -1091,6 +1105,9 @@ function initPage() {
nearEventsQuery(eventData.Id);
initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues
if (scale == "auto") changeScale();
+ document.querySelectorAll('select[name="rate"]').forEach(function(el) {
+ el.onchange = window['changeRate'];
+ });
}
// Kick everything off
From 1bdabefb2d06ef343782cb9c723fb9b506a8bb3e Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 7 Jan 2020 17:07:35 -0500
Subject: [PATCH 004/319] fix single stepping in reverse but actually modifying
curr_frame_id
---
src/zm_eventstream.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp
index e2f1862ab..2ed2dc167 100644
--- a/src/zm_eventstream.cpp
+++ b/src/zm_eventstream.cpp
@@ -383,6 +383,8 @@ void EventStream::processCommand(const CmdMsg *msg) {
paused = true;
replay_rate = ZM_RATE_BASE;
step = -1;
+ curr_frame_id -= 1;
+ if ( curr_frame_id < 1 ) curr_frame_id = 1;
break;
case CMD_FASTREV :
Debug(1, "Got FAST REV command");
@@ -904,7 +906,7 @@ void EventStream::runStream() {
send_frame = true;
}
} else if ( step != 0 ) {
- Debug(2, "Paused with step");
+ Debug(2, "Paused with step %d", step);
// We are paused and are just stepping forward or backward one frame
step = 0;
send_frame = true;
From 1959b2e070e1968101677d01b0a3bb19db53455c Mon Sep 17 00:00:00 2001
From: hax0kartik
Date: Thu, 20 Feb 2020 07:45:33 -0800
Subject: [PATCH 005/319] Add optional library jwt
---
src/jwt-cpp/vcpkg/fix-wolfssl.patch | 214 ++++++++++++++++++++++++++++
1 file changed, 214 insertions(+)
create mode 100644 src/jwt-cpp/vcpkg/fix-wolfssl.patch
diff --git a/src/jwt-cpp/vcpkg/fix-wolfssl.patch b/src/jwt-cpp/vcpkg/fix-wolfssl.patch
new file mode 100644
index 000000000..cf535a1e2
--- /dev/null
+++ b/src/jwt-cpp/vcpkg/fix-wolfssl.patch
@@ -0,0 +1,214 @@
+diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h
+index ed93fd5..977e6aa 100644
+--- a/include/jwt-cpp/jwt.h
++++ b/include/jwt-cpp/jwt.h
+@@ -6,11 +6,13 @@
+ #include
+ #include
+ #include
+-#include
+-#include
+-#include
+-#include
+-#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
+
+ //If openssl version less than 1.1
+ #if OPENSSL_VERSION_NUMBER < 269484032
+@@ -280,7 +282,7 @@ namespace jwt {
+ throw signature_verification_exception("failed to verify signature: VerifyInit failed");
+ if (!EVP_VerifyUpdate(ctx.get(), data.data(), data.size()))
+ throw signature_verification_exception("failed to verify signature: VerifyUpdate failed");
+- auto res = EVP_VerifyFinal(ctx.get(), (const unsigned char*)signature.data(), signature.size(), pkey.get());
++ auto res = EVP_VerifyFinal(ctx.get(), (unsigned char*)signature.data(), signature.size(), pkey.get());
+ if (res != 1)
+ throw signature_verification_exception("evp verify final failed: " + std::to_string(res) + " " + ERR_error_string(ERR_get_error(), NULL));
+ }
+@@ -342,8 +344,8 @@ namespace jwt {
+ if(!pkey)
+ throw rsa_exception("at least one of public or private key need to be present");
+
+- if(EC_KEY_check_key(pkey.get()) == 0)
+- throw ecdsa_exception("failed to load key: key is invalid");
++ //if(EC_KEY_check_key(pkey.get()) == 0)
++ // throw ecdsa_exception("failed to load key: key is invalid");
+ }
+ /**
+ * Sign jwt data
+@@ -355,7 +357,7 @@ namespace jwt {
+ const std::string hash = generate_hash(data);
+
+ std::unique_ptr
+- sig(ECDSA_do_sign((const unsigned char*)hash.data(), hash.size(), pkey.get()), ECDSA_SIG_free);
++ sig(wolfSSL_ECDSA_do_sign((const unsigned char*)hash.data(), hash.size(), pkey.get()), ECDSA_SIG_free);
+ if(!sig)
+ throw signature_generation_exception();
+ #ifdef OPENSSL10
+@@ -470,109 +472,6 @@ namespace jwt {
+ const size_t signature_length;
+ };
+
+- /**
+- * Base class for PSS-RSA family of algorithms
+- */
+- struct pss {
+- /**
+- * Construct new pss algorithm
+- * \param public_key RSA public key in PEM format
+- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
+- * \param public_key_password Password to decrypt public key pem.
+- * \param privat_key_password Password to decrypt private key pem.
+- * \param md Pointer to hash function
+- * \param name Name of the algorithm
+- */
+- pss(const std::string& public_key, const std::string& private_key, const std::string& public_key_password, const std::string& private_key_password, const EVP_MD*(*md)(), const std::string& name)
+- : md(md), alg_name(name)
+- {
+- if (!private_key.empty()) {
+- pkey = helper::load_private_key_from_string(private_key, private_key_password);
+- } else if(!public_key.empty()) {
+- pkey = helper::load_public_key_from_string(public_key, public_key_password);
+- } else
+- throw rsa_exception("at least one of public or private key need to be present");
+- }
+- /**
+- * Sign jwt data
+- * \param data The data to sign
+- * \return ECDSA signature for the given data
+- * \throws signature_generation_exception
+- */
+- std::string sign(const std::string& data) const {
+- auto hash = this->generate_hash(data);
+-
+- std::unique_ptr key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free);
+- const int size = RSA_size(key.get());
+-
+- std::string padded(size, 0x00);
+- if (!RSA_padding_add_PKCS1_PSS_mgf1(key.get(), (unsigned char*)padded.data(), (const unsigned char*)hash.data(), md(), md(), -1))
+- throw signature_generation_exception("failed to create signature: RSA_padding_add_PKCS1_PSS_mgf1 failed");
+-
+- std::string res(size, 0x00);
+- if (RSA_private_encrypt(size, (const unsigned char*)padded.data(), (unsigned char*)res.data(), key.get(), RSA_NO_PADDING) < 0)
+- throw signature_generation_exception("failed to create signature: RSA_private_encrypt failed");
+- return res;
+- }
+- /**
+- * Check if signature is valid
+- * \param data The data to check signature against
+- * \param signature Signature provided by the jwt
+- * \throws signature_verification_exception If the provided signature does not match
+- */
+- void verify(const std::string& data, const std::string& signature) const {
+- auto hash = this->generate_hash(data);
+-
+- std::unique_ptr key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free);
+- const int size = RSA_size(key.get());
+-
+- std::string sig(size, 0x00);
+- if(!RSA_public_decrypt(signature.size(), (const unsigned char*)signature.data(), (unsigned char*)sig.data(), key.get(), RSA_NO_PADDING))
+- throw signature_verification_exception("Invalid signature");
+-
+- if(!RSA_verify_PKCS1_PSS_mgf1(key.get(), (const unsigned char*)hash.data(), md(), md(), (const unsigned char*)sig.data(), -1))
+- throw signature_verification_exception("Invalid signature");
+- }
+- /**
+- * Returns the algorithm name provided to the constructor
+- * \return Algorithmname
+- */
+- std::string name() const {
+- return alg_name;
+- }
+- private:
+- /**
+- * Hash the provided data using the hash function specified in constructor
+- * \param data Data to hash
+- * \return Hash of data
+- */
+- std::string generate_hash(const std::string& data) const {
+-#ifdef OPENSSL10
+- std::unique_ptr ctx(EVP_MD_CTX_create(), &EVP_MD_CTX_destroy);
+-#else
+- std::unique_ptr ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
+-#endif
+- if(EVP_DigestInit(ctx.get(), md()) == 0)
+- throw signature_generation_exception("EVP_DigestInit failed");
+- if(EVP_DigestUpdate(ctx.get(), data.data(), data.size()) == 0)
+- throw signature_generation_exception("EVP_DigestUpdate failed");
+- unsigned int len = 0;
+- std::string res;
+- res.resize(EVP_MD_CTX_size(ctx.get()));
+- if(EVP_DigestFinal(ctx.get(), (unsigned char*)res.data(), &len) == 0)
+- throw signature_generation_exception("EVP_DigestFinal failed");
+- res.resize(len);
+- return res;
+- }
+-
+- /// OpenSSL structure containing keys
+- std::shared_ptr pkey;
+- /// Hash generator function
+- const EVP_MD*(*md)();
+- /// Algorithmname
+- const std::string alg_name;
+- };
+-
+ /**
+ * HS256 algorithm
+ */
+@@ -700,51 +599,6 @@ namespace jwt {
+ {}
+ };
+
+- /**
+- * PS256 algorithm
+- */
+- struct ps256 : public pss {
+- /**
+- * Construct new instance of algorithm
+- * \param public_key RSA public key in PEM format
+- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
+- * \param public_key_password Password to decrypt public key pem.
+- * \param privat_key_password Password to decrypt private key pem.
+- */
+- explicit ps256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "")
+- : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "PS256")
+- {}
+- };
+- /**
+- * PS384 algorithm
+- */
+- struct ps384 : public pss {
+- /**
+- * Construct new instance of algorithm
+- * \param public_key RSA public key in PEM format
+- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
+- * \param public_key_password Password to decrypt public key pem.
+- * \param privat_key_password Password to decrypt private key pem.
+- */
+- explicit ps384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "")
+- : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "PS384")
+- {}
+- };
+- /**
+- * PS512 algorithm
+- */
+- struct ps512 : public pss {
+- /**
+- * Construct new instance of algorithm
+- * \param public_key RSA public key in PEM format
+- * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
+- * \param public_key_password Password to decrypt public key pem.
+- * \param privat_key_password Password to decrypt private key pem.
+- */
+- explicit ps512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "")
+- : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "PS512")
+- {}
+- };
+ }
+
+ /**
From 04b78ac7e61e0d7e278547b1b76d16646d9cc5bb Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Thu, 27 Feb 2020 17:56:56 -0500
Subject: [PATCH 006/319] typo and punctuation fixes.
---
docs/userguide/options/options_system.rst | 24 +++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/docs/userguide/options/options_system.rst b/docs/userguide/options/options_system.rst
index bd871a1e8..33c59cf1c 100644
--- a/docs/userguide/options/options_system.rst
+++ b/docs/userguide/options/options_system.rst
@@ -14,13 +14,13 @@ LANG_DEFAULT - ZoneMinder allows the web interface to use languages other than E
OPT_USE_AUTH - ZoneMinder can run in two modes. The simplest is an entirely unauthenticated mode where anyone can access ZoneMinder and perform all tasks. This is most suitable for installations where the web server access is limited in other ways. The other mode enables user accounts with varying sets of permissions. Users must login or authenticate to access ZoneMinder and are limited by their defined permissions. Authenticated mode alone should not be relied up for securing Internet connected ZoneMinder.
-AUTH_TYPE - ZoneMinder can use two methods to authenticate users when running in authenticated mode. The first is a builtin method where ZoneMinder provides facilities for users to log in and maintains track of their identity. The second method allows interworking with other methods such as http basic authentication which passes an independently authentication 'remote' user via http. In this case ZoneMinder would use the supplied user without additional authentication provided such a user is configured ion ZoneMinder.
+AUTH_TYPE - ZoneMinder can use two methods to authenticate users when running in authenticated mode. The first is a builtin method where ZoneMinder provides facilities for users to log in and maintains track of their identity. The second method allows interworking with other methods such as http basic authentication which passes an independently authenticated 'remote' user via http. In this case ZoneMinder would use the supplied user without additional authentication provided such a user is configured in ZoneMinder.
AUTH_RELAY - When ZoneMinder is running in authenticated mode it can pass user details between the web pages and the back end processes. There are two methods for doing this. This first is to use a time limited hashed string which contains no direct username or password details, the second method is to pass the username and passwords around in plaintext. This method is not recommend except where you do not have the md5 libraries available on your system or you have a completely isolated system with no external access. You can also switch off authentication relaying if your system is isolated in other ways.
-AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is necessary to generate hashed strings containing encrypted sensitive information such as usernames and password. Although these string are reasonably secure the addition of a random secret increases security substantially. Note that if you are using the new token based APIs, then this field is mandatory with ZM 1.34 and above
+AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is necessary to generate hashed strings containing encrypted sensitive information such as usernames and passwords. Although these strings are reasonably secure the addition of a random secret increases security substantially. Note that if you are using the new token based APIs, then this field is mandatory with ZM 1.34 and above.
-AUTH_HASH_IPS - When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help. It is recommended you keep this off if you use mobile apps like zmNinja over mobile carrier networks - several APNs change the IP very frequently which may result in authentication failure.
+AUTH_HASH_IPS - When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help. It is recommended you keep this off if you use mobile apps like zmNinja over mobile carrier networks - several APNs change the IP very frequently which may result in authentication failures.
AUTH_HASH_TTL - Time before ZM auth will expire (does not apply to API tokens). The default has traditionally been 2 hours. A new hash will automatically be regenerated at half this value.
@@ -34,11 +34,11 @@ OPT_USE_API - A global setting to enable/disable ZoneMinder APIs. If you are usi
OPT_USE_LEGACY_AUTH - Starting version 1.34.0, ZoneMinder uses a more secure Authentication mechanism using JWT tokens. Older versions used a less secure MD5 based auth hash. It is recommended you turn this off after you are sure you don't need it. If you are using a 3rd party app that relies on the older API auth mechanisms, you will have to update that app if you turn this off. Note that zmNinja 1.3.057 onwards supports the new token system.
-OPT_USE_EVENT_NOTIFICATION - zmeventnotification is a 3rd party event notification server that is used to get notifications for alarms detected by ZoneMinder in real time. zmNinja requires this server for push notifications to mobile phones. This option only enables the server if its already installed. Please visit the `Event Notification Server project site `__ for installation instructions.
+OPT_USE_EVENT_NOTIFICATION - zmeventnotification is a 3rd party event notification server that is used to get notifications for alarms detected by ZoneMinder in real time. zmNinja requires this server for push notifications to mobile phones. This option only enables the server if it is already installed. Please visit the `Event Notification Server project site `__ for installation instructions.
-OPT_USE_GOOG_RECAPTCHA - This option allows you to include a google reCaptcha validation at login. This means in addition to providing a valid usernane and password, you will also have to pass the reCaptcha test. Please note that enabling this option results in the zoneminder login page reach out to google servers for captcha validation. Also please note that enabling this option may break 3rd party clients if they rely on web based logins (Note that zmNinja now uses the API based token method and will not be affected if reCAPTCHA is enabled). If you enable this, you also need to specify your site and secret key (please refer to context help in the ZoneMinder system screen)
+OPT_USE_GOOG_RECAPTCHA - This option allows you to include a google reCaptcha validation at login. This means in addition to providing a valid username and password, you will also have to pass the reCaptcha test. Please note that enabling this option results in the zoneminder login page reaching out to google servers for captcha validation. Also please note that enabling this option may break 3rd party clients if they rely on web based logins (Note that zmNinja now uses the API based token method and will not be affected if reCAPTCHA is enabled). If you enable this, you also need to specify your site and secret key (please refer to context help in the ZoneMinder system screen).
-SYSTEM_SHUTDOWN - this option decides if it is allowed to shutdown the full system via the ZM UI. The system will need to have sudo installed and the following added to /etc/sudoers:
+SYSTEM_SHUTDOWN - this option puts a poweroff icon in the header of the ZM UI for users with System privilege accessi. This icon will allow the user to shutdown the full system via the ZM UI. The system will need to have sudo installed and the following added to /etc/sudoers:
::
@@ -46,9 +46,9 @@ SYSTEM_SHUTDOWN - this option decides if it is allowed to shutdown the full syst
to perform the shutdown or reboot
-OPT_FAST_DELETE - Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if your are trying to do a lot of events at once. **NOTE**: It is recommended that you keep this option OFF, unless you are running on an old or low-powered system.
+OPT_FAST_DELETE - Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if youxr are trying to do a lot of events at once. **NOTE**: It is recommended that you keep this option OFF, unless you are running on an old or low-powered system.
-FILTER_RELOAD_DELAY - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often in seconds the filters are reloaded from the database to get the latest versions or new filters. If you don't change filters very often this value can be set to a large value.
+FILTER_RELOAD_DELAY - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often in seconds the filters are reloaded from the database to get the latest versions or new filters. If you don't change filters very often this value can be set to a large value. As of 1.34.0 filters should be automatically reloaded when saving a filter so this setting should have little effect.
FILTER_EXECUTE_INTERVAL - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often the filters are executed on the saved event in the database. If you want a rapid response to new events this should be a smaller value, however this may increase the overall load on the system and affect performance of other elements.
@@ -58,9 +58,9 @@ STATUS_UPDATE_INTERVAL - The zmstats daemon performs various db queries related
WATCH_CHECK_INTERVAL - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines how often the daemons are checked.
-WATCH_MAX_DELAY - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines the maximum delay to allow since the last captured frame. The daemon will be restarted if it has not captured any images after this period though the actual restart may take slightly longer in conjunction with the check interval value above.
+WATCH_MAX_DELAY - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines the maximum delay to allow since the last captured frame. The daemon will be restarted if it has not captured any images after this period though the actual restart may take slightly longer in conjunction with the check interval value above. Please note that some cameras can take up to 30 seconds to get a valid image, so this setting should be larger than that.
-RUN_AUDIT - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. It is recommended you keep this **OFF** in most systems.
+RUN_AUDIT - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. It is recommended you keep this **OFF** in most systems and run it manually if needed after a system crash.
AUDIT_CHECK_INTERVAL - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. The default check interval of 900 seconds (15 minutes) is fine for most systems however if you have a very large number of events the process of scanning the database and filesystem may take a long time and impact performance. In this case you may prefer to make this interval much larger to reduce the impact on your system. This option determines how often these checks are performed.
@@ -70,11 +70,11 @@ OPT_CONTROL - ZoneMinder includes limited support for controllable cameras. A nu
OPT_TRIGGERS - ZoneMinder can interact with external systems which prompt or cancel alarms. This is done via the zmtrigger.pl script. This option indicates whether you want to use these external triggers. Most people will say no here.
-CHECK_FOR_UPDATES - From ZoneMinder version 1.17.0 onwards new versions are expected to be more frequent. To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable
+CHECK_FOR_UPDATES - To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable.
TELEMETRY_DATA - Enable collection of usage information of the local system and send it to the ZoneMinder development team. This data will be used to determine things like who and where our customers are, how big their systems are, the underlying hardware and operating system, etc. This is being done for the sole purpose of creating a better product for our target audience. This script is intended to be completely transparent to the end user, and can be disabled from the web console under Options. For more details on what information we collect, please refer to Zoneminder's privacy statement (available in the contextual help of TELEMETRY_DATA on your installation).
-UPDATE_CHECK_PROXY - If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of ``http://:/``
+UPDATE_CHECK_PROXY - If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of ``http://:/``.
SHM_KEY - ZoneMinder uses shared memory to speed up communication between modules. To identify the right area to use shared memory keys are used. This option controls what the base key is, each monitor will have it's Id or'ed with this to get the actual key used. You will not normally need to change this value unless it clashes with another instance of ZoneMinder on the same machine. Only the first four hex digits are used, the lower four will be masked out and ignored.
From 76131d1887ec2584a3d365a6d299d34e0a321c7f Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Sun, 1 Mar 2020 11:07:31 -0500
Subject: [PATCH 007/319] Fix timeline. Update parseFilterToTree to add
missing operators. Fix logging calls to include ZM namespace. Update code
style.
---
.../classic/includes/timeline_functions.php | 379 +++++++++---------
web/skins/classic/views/timeline.php | 17 +-
2 files changed, 197 insertions(+), 199 deletions(-)
diff --git a/web/skins/classic/includes/timeline_functions.php b/web/skins/classic/includes/timeline_functions.php
index b5cc3909c..1c0995df4 100644
--- a/web/skins/classic/includes/timeline_functions.php
+++ b/web/skins/classic/includes/timeline_functions.php
@@ -1,6 +1,6 @@
$maxLines ) {
@@ -29,20 +29,21 @@ function getYScale( $range, $minLines, $maxLines ) {
}
$scale['lines'] = (int)(($scale['range']-1)/$scale['divisor'])+1;
- return( $scale );
+ return $scale;
}
-function getSlotFrame( $slot ) {
+function getSlotFrame($slot) {
$slotFrame = isset($slot['frame'])?$slot['frame']['FrameId']:1;
+ # FIXME what's with this false?
if ( false && $slotFrame ) {
$slotFrame -= $monitor['PreEventCount'];
if ( $slotFrame < 1 )
$slotFrame = 1;
}
- return( $slotFrame );
+ return $slotFrame;
}
-function parseFilterToTree( $filter ) {
+function parseFilterToTree($filter) {
if ( count($filter['terms']) <= 0 ) {
return false;
}
@@ -68,73 +69,82 @@ function parseFilterToTree( $filter ) {
'or' => 4,
);
- for ( $i = 0; $i <= count($terms); $i++ ) {
- if ( !empty($terms[$i]['cnj']) ) {
+ for ( $i = 0; $i < count($terms); $i++ ) {
+ $term = $terms[$i];
+ if ( !empty($term['cnj']) ) {
while( true ) {
if ( !count($postfixStack) ) {
- $postfixStack[] = array('type'=>'cnj', 'value'=>$terms[$i]['cnj'], 'sqlValue'=>$terms[$i]['cnj']);
+ $postfixStack[] = array('type'=>'cnj', 'value'=>$term['cnj'], 'sqlValue'=>$term['cnj']);
break;
} elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) {
- $postfixStack[] = array('type'=>'cnj', 'value'=>$terms[$i]['cnj'], 'sqlValue'=>$terms[$i]['cnj']);
+ $postfixStack[] = array('type'=>'cnj', 'value'=>$term['cnj'], 'sqlValue'=>$term['cnj']);
break;
- } elseif ( $priorities[$terms[$i]['cnj']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) {
- $postfixStack[] = array('type'=>'cnj', 'value'=>$terms[$i]['cnj'], 'sqlValue'=>$terms[$i]['cnj']);
+ } elseif ( $priorities[$term['cnj']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) {
+ $postfixStack[] = array('type'=>'cnj', 'value'=>$term['cnj'], 'sqlValue'=>$term['cnj']);
break;
} else {
$postfixExpr[] = array_pop($postfixStack);
}
}
- }
- if ( !empty($terms[$i]['obr']) ) {
- for ( $j = 0; $j < $terms[$i]['obr']; $j++ ) {
- $postfixStack[] = array('type'=>'obr', 'value'=>$terms[$i]['obr']);
+ } # end if ! empty cnj
+
+ if ( !empty($term['obr']) ) {
+ for ( $j = 0; $j < $term['obr']; $j++ ) {
+ $postfixStack[] = array('type'=>'obr', 'value'=>$term['obr']);
}
}
- if ( !empty($terms[$i]['attr']) ) {
+ if ( !empty($term['attr']) ) {
$dtAttr = false;
- switch ( $terms[$i]['attr']) {
+ switch ( $term['attr']) {
case 'MonitorName':
- $sqlValue = 'M.'.preg_replace( '/^Monitor/', '', $terms[$i]['attr']);
+ $sqlValue = 'M.'.preg_replace( '/^Monitor/', '', $term['attr']);
break;
case 'ServerId':
$sqlValue .= 'M.ServerId';
- break;
+ break;
+ case 'StorageServerId':
+ $sqlValue .= 'S.ServerId';
+ break;
+ case 'FilterServerId':
+ $sqlValue .= ZM_SERVER_ID;
+ break;
case 'DateTime':
case 'StartDateTime':
- $sqlValue = "E.StartTime";
+ $sqlValue = 'E.StartTime';
$dtAttr = true;
break;
case 'Date':
case 'StartDate':
- $sqlValue = "to_days( E.StartTime )";
+ $sqlValue = 'to_days(E.StartTime)';
$dtAttr = true;
break;
case 'Time':
case 'StartTime':
- $sqlValue = "extract( hour_second from E.StartTime )";
+ $sqlValue = 'extract(hour_second from E.StartTime)';
break;
case 'Weekday':
case 'StartWeekday':
- $sqlValue = "weekday( E.StartTime )";
+ $sqlValue = 'weekday(E.StartTime)';
break;
case 'EndDateTime':
- $sqlValue = "E.EndTime";
+ $sqlValue = 'E.EndTime';
$dtAttr = true;
break;
case 'EndDate':
- $sqlValue = "to_days( E.EndTime )";
+ $sqlValue = 'to_days(E.EndTime)';
$dtAttr = true;
break;
case 'EndTime':
- $sqlValue = "extract( hour_second from E.EndTime )";
+ $sqlValue = 'extract(hour_second from E.EndTime)';
break;
case 'EndWeekday':
- $sqlValue = "weekday( E.EndTime )";
+ $sqlValue = 'weekday(E.EndTime)';
break;
case 'Id':
case 'Name':
case 'MonitorId':
case 'StorageId':
+ case 'SecondaryStorageId':
case 'Length':
case 'Frames':
case 'AlarmFrames':
@@ -145,7 +155,7 @@ function parseFilterToTree( $filter ) {
case 'Notes':
case 'StateId':
case 'Archived':
- $sqlValue = "E.".$terms[$i]['attr'];
+ $sqlValue = 'E.'.$term['attr'];
break;
case 'DiskPercent':
// Need to specify a storage area, so need to look through other terms looking for a storage area, else we default to ZM_EVENTS_PATH
@@ -174,53 +184,66 @@ function parseFilterToTree( $filter ) {
$sqlValue = getDiskBlocks($StorageArea);
break;
default :
- $sqlValue = $terms[$i]['attr'];
+ $sqlValue = $term['attr'];
break;
}
if ( $dtAttr ) {
- $postfixExpr[] = array('type'=>'attr', 'value'=>$terms[$i]['attr'], 'sqlValue'=>$sqlValue, 'dtAttr'=>true);
+ $postfixExpr[] = array('type'=>'attr', 'value'=>$term['attr'], 'sqlValue'=>$sqlValue, 'dtAttr'=>true);
} else {
- $postfixExpr[] = array('type'=>'attr', 'value'=>$terms[$i]['attr'], 'sqlValue'=>$sqlValue);
+ $postfixExpr[] = array('type'=>'attr', 'value'=>$term['attr'], 'sqlValue'=>$sqlValue);
}
} # end if attr
- if ( isset($terms[$i]['op']) ) {
- if ( empty($terms[$i]['op']) ) {
- $terms[$i]['op'] = '=';
+ if ( isset($term['op']) ) {
+ if ( empty($term['op']) ) {
+ $term['op'] = '=';
}
- switch ( $terms[$i]['op']) {
+ switch ( $term['op']) {
case '=' :
case '!=' :
case '>=' :
case '>' :
case '<' :
case '<=' :
- $sqlValue = $terms[$i]['op'];
+ case 'LIKE' :
+ case 'NOT LIKE':
+ $sqlValue = $term['op'];
break;
- case '=~' :
+ case '=~' :
$sqlValue = 'regexp';
break;
case '!~' :
$sqlValue = 'not regexp';
break;
case '=[]' :
+ case 'IN' :
$sqlValue = 'in (';
break;
case '![]' :
$sqlValue = 'not in (';
break;
+ case 'IS' :
+ case 'IS NOT' :
+ if ( $term['val'] == 'Odd' ) {
+ $sqlValue .= ' % 2 = 1';
+ } else if ( $term['val'] == 'Even' ) {
+ $sqlValue .= ' % 2 = 0';
+ } else {
+ $sqlValue .= ' '.$term['op'];
+ }
+ break;
default :
- Error('Unknown operator in filter '. $terms[$i]['op']);
+ ZM\Error('Unknown operator in filter '.$term['op']);
}
while( true ) {
if ( !count($postfixStack) ) {
- $postfixStack[] = array('type'=>'op', 'value'=>$terms[$i]['op'], 'sqlValue'=>$sqlValue);
+ $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue);
break;
} elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) {
- $postfixStack[] = array('type'=>'op', 'value'=>$terms[$i]['op'], 'sqlValue'=>$sqlValue);
+ $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue);
break;
- } elseif ( $priorities[$terms[$i]['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) {
- $postfixStack[] = array('type'=>'op', 'value'=>$terms[$i]['op'], 'sqlValue'=>$sqlValue );
+ } elseif ( $priorities[$term['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) {
+ $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue );
break;
} else {
$postfixExpr[] = array_pop($postfixStack);
@@ -228,17 +251,23 @@ function parseFilterToTree( $filter ) {
} // end while
} // end if operator
- if ( isset($terms[$i]['val']) ) {
+ if ( isset($term['val']) ) {
$valueList = array();
- foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $terms[$i]['val'])) as $value ) {
- switch ( $terms[$i]['attr'] ) {
+ foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $term['val'])) as $value ) {
+ switch ( $term['attr'] ) {
case 'MonitorName':
case 'Name':
case 'Cause':
case 'Notes':
- $value = "'$value'";
- break;
- case 'ServerId':
+ if ( $term['op'] == 'LIKE' || $term['op'] == 'NOT LIKE' ) {
+ $value = '%'.$value.'%';
+ }
+ $value = dbEscape($value);
+ break;
+ case 'MonitorServerId':
+ case 'FilterServerId':
+ case 'StorageServerId':
+ case 'ServerId':
if ( $value == 'ZM_SERVER_ID' ) {
$value = ZM_SERVER_ID;
} else if ( $value == 'NULL' ) {
@@ -260,17 +289,17 @@ function parseFilterToTree( $filter ) {
case 'Date':
case 'EndDate':
case 'StartDate':
- $value = "to_days('".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."' )";
+ $value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')';
break;
case 'Time':
case 'EndTime':
case 'StartTime':
- $value = "extract( hour_second from '".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."' )";
+ $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')';
break;
case 'Weekday':
case 'EndWeekday':
case 'StartWeekday':
- $value = "weekday( '".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."' )";
+ $value = 'weekday(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')';
break;
default :
if ( $value != 'NULL' )
@@ -278,11 +307,11 @@ function parseFilterToTree( $filter ) {
} // end switch attribute
$valueList[] = $value;
} // end foreach value
- $postfixExpr[] = array('type'=>'val', 'value'=>$terms[$i]['val'], 'sqlValue'=>join(',', $valueList));
+ $postfixExpr[] = array('type'=>'val', 'value'=>$term['val'], 'sqlValue'=>join(',', $valueList));
} // end if has val
- if ( !empty($terms[$i]['cbr']) ) {
- for ( $j = 0; $j < $terms[$i]['cbr']; $j++ ) {
+ if ( !empty($term['cbr']) ) {
+ for ( $j = 0; $j < $term['cbr']; $j++ ) {
while ( count($postfixStack) ) {
$element = array_pop($postfixStack);
if ( $element['type'] == 'obr' ) {
@@ -292,8 +321,9 @@ function parseFilterToTree( $filter ) {
$postfixExpr[] = $element;
}
}
- }
- }
+ } #end if cbr
+ } # end foreach term
+
while ( count($postfixStack) ) {
$postfixExpr[] = array_pop($postfixStack);
}
@@ -314,11 +344,11 @@ function parseFilterToTree( $filter ) {
$node = array('data'=>$element, 'count'=>2+$left['count']+$right['count'], 'right'=>$right, 'left'=>$left);
$exprStack[] = $node;
} else {
- Fatal("Unexpected element type '".$element['type']."', value '".$element['value']."'");
+ ZM\Fatal('Unexpected element type \''.$element['type'].'\', value \''.$element['value'].'\'');
}
}
if ( count($exprStack) != 1 ) {
- Fatal('Expression stack has '.count($exprStack).' elements');
+ ZM\Fatal('Expression stack has '.count($exprStack).' elements');
}
return array_pop($exprStack);
}
@@ -345,188 +375,153 @@ function parseTreeToInfix($tree) {
return _parseTreeToInfix($tree);
}
-function _parseTreeToSQL( $node, $cbr=false )
-{
+function _parseTreeToSQL($node, $cbr=false) {
$expression = '';
- if ( $node )
- {
- if ( isset($node['left']) )
- {
- if ( !empty($node['data']['bracket']) )
- $expression .= '( ';
- $expression .= _parseTreeToSQL( $node['left'] );
- }
- $inExpr = $node['data']['type'] == 'op' && ($node['data']['value'] == '=[]' || $node['data']['value'] == '![]');
- $expression .= $node['data']['sqlValue'];
- if ( !$inExpr )
- $expression .= ' ';
- if ( $cbr )
- $expression .= ') ';
- if ( isset($node['right']) )
- {
- $expression .= _parseTreeToSQL( $node['right'], $inExpr );
- if ( !empty($node['data']['bracket']) )
- $expression .= ') ';
- }
- }
- return( $expression );
+ if ( !$node )
+ return $expression;
+
+ if ( isset($node['left']) ) {
+ if ( !empty($node['data']['bracket']) )
+ $expression .= '( ';
+ $expression .= _parseTreeToSQL($node['left']);
+ }
+ $inExpr = $node['data']['type'] == 'op' && (
+ $node['data']['value'] == '=[]'
+ or
+ $node['data']['value'] == '![]'
+ or
+ $node['data']['value'] == 'IN'
+ or
+ $node['data']['value'] == 'NOT IN'
+ );
+ $expression .= $node['data']['sqlValue'];
+ if ( !$inExpr )
+ $expression .= ' ';
+ if ( $cbr )
+ $expression .= ') ';
+ if ( isset($node['right']) ) {
+ $expression .= _parseTreeToSQL($node['right'], $inExpr);
+ if ( !empty($node['data']['bracket']) )
+ $expression .= ') ';
+ } # end if right
+ return $expression;
}
-function parseTreeToSQL( $tree )
-{
- return( _parseTreeToSQL( $tree ) );
+function parseTreeToSQL($tree) {
+ return _parseTreeToSQL($tree);
}
-function _parseTreeToFilter( $node, &$terms, &$level )
-{
+function _parseTreeToFilter($node, &$terms, &$level) {
$elements = array();
- if ( $node )
- {
- if ( isset($node['left']) )
- {
+ if ( $node ) {
+ if ( isset($node['left']) ) {
if ( !empty($node['data']['bracket']) )
$terms[$level]['obr'] = 1;
_parseTreeToFilter( $node['left'], $terms, $level );
}
- if ( $node['data']['type'] == 'cnj' )
- {
+ if ( $node['data']['type'] == 'cnj' ) {
$level++;
}
$terms[$level][$node['data']['type']] = $node['data']['value'];
- if ( isset($node['right']) )
- {
- _parseTreeToFilter( $node['right'], $terms, $level );
+ if ( isset($node['right']) ) {
+ _parseTreeToFilter($node['right'], $terms, $level);
if ( !empty($node['data']['bracket']) )
$terms[$level]['cbr'] = 1;
}
}
}
-function parseTreeToFilter( $tree )
-{
+function parseTreeToFilter($tree) {
$terms = array();
- if ( isset($tree) )
- {
+ if ( isset($tree) ) {
$level = 0;
- _parseTreeToFilter( $tree, $terms, $level );
+ _parseTreeToFilter($tree, $terms, $level);
}
- return( array( 'Query' => array( 'terms' => $terms ) ) );
+ return array('Query' => array('terms' => $terms));
}
-function parseTreeToQuery( $tree )
-{
- $filter = parseTreeToFilter( $tree );
- parseFilter( $filter, false, '&' );
- return( $filter['query'] );
+function parseTreeToQuery($tree) {
+ $filter = parseTreeToFilter($tree);
+ parseFilter($filter, false, '&');
+ return $filter['query'];
}
-function _drawTree( $node, $level )
-{
- if ( isset($node['left']) )
- {
- _drawTree( $node['left'], $level+1 );
+function _drawTree($node, $level) {
+ if ( isset($node['left']) ) {
+ _drawTree($node['left'], $level+1);
}
- echo str_repeat( ".", $level*2 ).$node['data']['value']." ";
- if ( isset($node['right']) )
- {
- _drawTree( $node['right'], $level+1 );
+ echo str_repeat('.', $level*2).$node['data']['value'].' ';
+ if ( isset($node['right']) ) {
+ _drawTree($node['right'], $level+1);
}
}
-function drawTree( $tree )
-{
- _drawTree( $tree, 0 );
+function drawTree($tree) {
+ _drawTree($tree, 0);
}
-function _extractDatetimeRange( &$node, &$minTime, &$maxTime, &$expandable, $subOr )
-{
+function _extractDatetimeRange(&$node, &$minTime, &$maxTime, &$expandable, $subOr) {
$pruned = $leftPruned = $rightPruned = false;
- if ( $node )
- {
- if ( isset($node['left']) && isset($node['right']) )
- {
- if ( $node['data']['type'] == 'cnj' && $node['data']['value'] == 'or' )
- {
- $subOr = true;
- }
- elseif ( !empty($node['left']['data']['dtAttr']) )
- {
- if ( $subOr )
- {
- $expandable = false;
- }
- elseif ( $node['data']['type'] == 'op' )
- {
- if ( $node['data']['value'] == '>' || $node['data']['value'] == '>=' )
- {
- if ( !$minTime || $minTime > $node['right']['data']['sqlValue'] )
- {
- $minTime = $node['right']['data']['value'];
- return( true );
- }
- }
- if ( $node['data']['value'] == '<' || $node['data']['value'] == '<=' )
- {
- if ( !$maxTime || $maxTime < $node['right']['data']['sqlValue'] )
- {
- $maxTime = $node['right']['data']['value'];
- return( true );
- }
- }
- }
- else
- {
- Fatal( "Unexpected node type '".$node['data']['type']."'" );
- }
- return( false );
- }
+ if ( !($node and isset($node['left']) and isset($node['right']) ) ) {
+ return $pruned;
+ }
- $leftPruned = _extractDatetimeRange( $node['left'], $minTime, $maxTime, $expandable, $subOr );
- $rightPruned = _extractDatetimeRange( $node['right'], $minTime, $maxTime, $expandable, $subOr );
+ if ( $node['data']['type'] == 'cnj' && $node['data']['value'] == 'or' ) {
+ $subOr = true;
+ } else if ( !empty($node['left']['data']['dtAttr']) ) {
+ if ( $subOr ) {
+ $expandable = false;
+ } elseif ( $node['data']['type'] == 'op' ) {
+ if ( $node['data']['value'] == '>' || $node['data']['value'] == '>=' ) {
+ if ( !$minTime || $minTime > $node['right']['data']['sqlValue'] ) {
+ $minTime = $node['right']['data']['value'];
+ return true;
+ }
+ } else if ( $node['data']['value'] == '<' || $node['data']['value'] == '<=' ) {
+ if ( !$maxTime || $maxTime < $node['right']['data']['sqlValue'] ) {
+ $maxTime = $node['right']['data']['value'];
+ return true;
+ }
+ }
+ } else {
+ ZM\Fatal("Unexpected node type '".$node['data']['type']."'");
+ }
+ return false;
+ }
- if ( $leftPruned && $rightPruned )
- {
- $pruned = true;
- }
- elseif ( $leftPruned )
- {
- $node = $node['right'];
- }
- elseif ( $rightPruned )
- {
- $node = $node['left'];
- }
- }
- }
- return( $pruned );
+ $leftPruned = _extractDatetimeRange( $node['left'], $minTime, $maxTime, $expandable, $subOr );
+ $rightPruned = _extractDatetimeRange( $node['right'], $minTime, $maxTime, $expandable, $subOr );
+
+ if ( $leftPruned && $rightPruned ) {
+ $pruned = true;
+ } else if ( $leftPruned ) {
+ $node = $node['right'];
+ } else if ( $rightPruned ) {
+ $node = $node['left'];
+ }
+ return $pruned;
}
-function extractDatetimeRange( &$tree, &$minTime, &$maxTime, &$expandable )
-{
- $minTime = "";
- $maxTime = "";
+function extractDatetimeRange( &$tree, &$minTime, &$maxTime, &$expandable ) {
+ $minTime = '';
+ $maxTime = '';
$expandable = true;
_extractDateTimeRange( $tree, $minTime, $maxTime, $expandable, false );
}
-function appendDatetimeRange( &$tree, $minTime, $maxTime=false )
-{
+function appendDatetimeRange( &$tree, $minTime, $maxTime=false ) {
$attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'StartDateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 );
$valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$minTime, 'sqlValue'=>$minTime ), 'count'=>0 );
$opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'>=', 'sqlValue'=>'>=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode );
- if ( isset($tree) )
- {
+ if ( isset($tree) ) {
$cnjNode = array( 'data'=>array( 'type'=>'cnj', 'value'=>'and', 'sqlValue'=>'and' ), 'count'=>2+$tree['count']+$opNode['count'], 'left'=>$tree, 'right'=>$opNode );
$tree = $cnjNode;
- }
- else
- {
+ } else {
$tree = $opNode;
}
- if ( $maxTime )
- {
+ if ( $maxTime ) {
$attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'StartDateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 );
$valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$maxTime, 'sqlValue'=>$maxTime ), 'count'=>0 );
$opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'<=', 'sqlValue'=>'<=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode );
diff --git a/web/skins/classic/views/timeline.php b/web/skins/classic/views/timeline.php
index 2fe419efd..794b99c20 100644
--- a/web/skins/classic/views/timeline.php
+++ b/web/skins/classic/views/timeline.php
@@ -212,11 +212,14 @@ if ( isset($minTime) && isset($maxTime) ) {
if ( !isset($minTime) || !isset($maxTime) ) {
// Dynamically determine range
$row = dbFetchOne($rangeSql);
-
- if ( !isset($minTime) )
- $minTime = $row['MinTime'];
- if ( !isset($maxTime) )
- $maxTime = $row['MaxTime'];
+ if ( $row ) {
+ if ( !isset($minTime) )
+ $minTime = $row['MinTime'];
+ if ( !isset($maxTime) )
+ $maxTime = $row['MaxTime'];
+ } else {
+ # Errors will be reported by db functions
+ }
}
if ( empty($minTime) )
@@ -307,7 +310,7 @@ $monEventSlots = array();
$monFrameSlots = array();
$events_result = dbQuery($eventsSql);
if ( !$events_result ) {
- Fatal('SQL-ERR');
+ ZM\Fatal('SQL-ERR');
return;
}
@@ -552,7 +555,7 @@ if ( $mode == 'overlay' ) {
$top += $chart['graph']['activityBarHeight']+1+$chart['graph']['eventBarHeight']+1;
}
} else {
- Warning("No mode $mode");
+ ZM\Warning("No mode $mode");
}
preg_match('/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $minTime, $startMatches);
From 7c21f065bd8c57622afcd6b648fa90f19641f342 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Mon, 2 Mar 2020 11:50:40 -0500
Subject: [PATCH 008/319] Checking AuthHashGeneratedAt is no good because it
gets updated before we check it. Instead check the auth hash in session
against the one in and sensd the update if they are different
---
web/ajax/status.php | 11 +++++++----
web/ajax/stream.php | 22 ++++++++++------------
2 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/web/ajax/status.php b/web/ajax/status.php
index 2725dcce8..1b1fb19a3 100644
--- a/web/ajax/status.php
+++ b/web/ajax/status.php
@@ -1,8 +1,11 @@
0 ) {
if ( count($rSockets) != 1 ) {
- ZM\Error('Bogus return from select, '.count($rSockets).' sockets available');
ajaxError('Bogus return from select, '.count($rSockets).' sockets available');
}
}
@@ -124,10 +121,12 @@ if ( sem_acquire($semaphore,1) !== false ) {
$data['delay'] = round( $data['delay'], 2 );
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
- $time = time();
- // Regenerate auth hash after half the lifetime of the hash
- if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) {
- $data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
+ $auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
+ if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
+ $data['auth'] = $auth_hash;
+ ZM\Logger::Debug("including nw auth hash " . $data['auth']);
+ } else {
+ ZM\Logger::Debug('Not including nw auth hash becase it hashn\'t changed '.$auth_hash);
}
}
ajaxResponse(array('status'=>$data));
@@ -141,12 +140,11 @@ if ( sem_acquire($semaphore,1) !== false ) {
$data = unpack('ltype/Qevent/iprogress/irate/izoom/Cpaused', $msg);
}
$data['rate'] /= RATE_BASE;
- $data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
+ $data['zoom'] = round($data['zoom']/SCALE_BASE, 1);
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
- $time = time();
- // Regenerate auth hash after half the lifetime of the hash
- if ( (!isset($_SESSION['AuthHashGeneratedAt'])) or ( $_SESSION['AuthHashGeneratedAt'] < $time - (ZM_AUTH_HASH_TTL * 1800) ) ) {
- $data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
+ $auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
+ if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
+ $data['auth'] = $auth_hash;
}
}
ajaxResponse(array('status'=>$data));
From bd59ae8856e9eb01aebe913c02314cf7ba19b21e Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Mon, 2 Mar 2020 14:59:36 -0500
Subject: [PATCH 009/319] Move the Basic Auth login code from skin.php to
includes/auth.php
---
web/includes/auth.php | 8 ++++++--
web/skins/classic/skin.php | 5 -----
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/web/includes/auth.php b/web/includes/auth.php
index f958463c3..51e4c0789 100644
--- a/web/includes/auth.php
+++ b/web/includes/auth.php
@@ -263,8 +263,6 @@ if ( ZM_OPT_USE_AUTH ) {
} else {
// Non token based auth
- $user = userFromSession();
-
if ( ZM_AUTH_HASH_LOGINS && empty($user) && !empty($_REQUEST['auth']) ) {
$user = getAuthUser($_REQUEST['auth']);
} else if (
@@ -280,6 +278,12 @@ if ( ZM_OPT_USE_AUTH ) {
return;
}
$user = $ret[0];
+ } else if ( (ZM_AUTH_TYPE == 'remote') and !empty($_SERVER['REMOTE_USER']) ) {
+ $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?';
+ // local user, shouldn't affect the global user
+ $user = dbFetchOne($sql, NULL, array($_SERVER['REMOTE_USER']));
+ } else {
+ $user = userFromSession();
}
if ( !empty($user) ) {
diff --git a/web/skins/classic/skin.php b/web/skins/classic/skin.php
index b24811066..9a6940887 100644
--- a/web/skins/classic/skin.php
+++ b/web/skins/classic/skin.php
@@ -39,11 +39,6 @@ if ( empty($view) ) {
$view = isset($user)?'console':'login';
}
-if ( !isset($user) && ZM_OPT_USE_AUTH && ZM_AUTH_TYPE == 'remote' && !empty( $_SERVER['REMOTE_USER']) ) {
- $view = 'postlogin';
- $action = 'login';
- $_REQUEST['username'] = $_SERVER['REMOTE_USER'];
-}
if ( isset($user) ) {
// Bandwidth Limiter
From 2f48c442cca979d814e3abb732817995ec0c21c1 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 4 Mar 2020 10:15:35 -0500
Subject: [PATCH 010/319] add Enabled to Storage
---
db/zm_create.sql.in | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in
index 920884cbf..ce504a422 100644
--- a/db/zm_create.sql.in
+++ b/db/zm_create.sql.in
@@ -733,13 +733,14 @@ CREATE TABLE `Storage` (
`Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium',
`ServerId` int(10) unsigned,
`DoDelete` BOOLEAN NOT NULL DEFAULT true,
+ `Enabled` BOOLEAN NOT NULL DEFAULT true,
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
--
-- Create a default storage location
--
-insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, NULL, 'Medium', 0, true );
+insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, NULL, 'Medium', 0, true, true );
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
From d59bc4ca6dad9896dd007e88ac6876ddbb4146f2 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 4 Mar 2020 10:15:44 -0500
Subject: [PATCH 011/319] add Enabled to Storage
---
db/zm_update-1.35.1.sql | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 db/zm_update-1.35.1.sql
diff --git a/db/zm_update-1.35.1.sql b/db/zm_update-1.35.1.sql
new file mode 100644
index 000000000..d250cf751
--- /dev/null
+++ b/db/zm_update-1.35.1.sql
@@ -0,0 +1,12 @@
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Storage'
+ AND column_name = 'Enabled'
+ ) > 0,
+"SELECT 'Column Enabled already exists in Storage'",
+"ALTER TABLE `Storage` ADD `Enabled` BOOLEAN NOT NULL default true AFTER `DoDelete`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
From 0b267fbdeeeb1150eb36bd20b4750f13ef67687a Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 4 Mar 2020 10:46:16 -0500
Subject: [PATCH 012/319] Add Enabled to Storage
---
web/includes/Storage.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/web/includes/Storage.php b/web/includes/Storage.php
index 695da80c7..8a397db97 100644
--- a/web/includes/Storage.php
+++ b/web/includes/Storage.php
@@ -16,6 +16,7 @@ class Storage extends ZM_Object {
'Scheme' => 'Medium',
'ServerId' => 0,
'DoDelete' => 1,
+ 'Enabled' => 1,
);
public static function find($parameters = array(), $options = array()) {
return ZM_Object::_find(get_class(), $parameters, $options);
From 077b8888f9bc0176993dbf2e8a2cae38e9194463 Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Wed, 4 Mar 2020 10:46:32 -0500
Subject: [PATCH 013/319] Use Storage object and add Enabled
---
web/skins/classic/views/storage.php | 45 ++++++++++++++---------------
1 file changed, 22 insertions(+), 23 deletions(-)
diff --git a/web/skins/classic/views/storage.php b/web/skins/classic/views/storage.php
index 08e41892f..44cf2fc0d 100644
--- a/web/skins/classic/views/storage.php
+++ b/web/skins/classic/views/storage.php
@@ -1,7 +1,7 @@
$_REQUEST['id'])) ) ) {
$view = 'error';
return;
- $newStorage['ServerId'] = '';
}
} else {
- $newStorage = array();
- $newStorage['Name'] = translate('NewStorage');
- $newStorage['Path'] = '';
- $newStorage['Type'] = 'local';
- $newStorage['Url'] = '';
- $newStorage['Scheme'] = 'Medium';
- $newStorage['StorageId'] = '';
- $newStorage['ServerId'] = '';
- $newStorage['DoDelete'] = 1;
+ $newStorage = new Storage();
+ $newStorage->Name(translate('NewStorage'));
}
$type_options = array( 'local' => translate('Local'), 's3fs' => translate('s3fs') );
@@ -55,12 +47,12 @@ foreach ( $servers as $S ) {
}
$focusWindow = true;
-xhtmlHeaders(__FILE__, translate('Storage').' - '.$newStorage['Name']);
+xhtmlHeaders(__FILE__, translate('Storage').' - '.$newStorage->Name());
?>