From 6345ecc479a17ef3b5469f2aae1d8e86761ceb9a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 23 Jan 2020 17:04:33 -0500 Subject: [PATCH 01/33] remove debug --- web/index.php | 1 - 1 file changed, 1 deletion(-) diff --git a/web/index.php b/web/index.php index a662b4223..7f6e17432 100644 --- a/web/index.php +++ b/web/index.php @@ -245,7 +245,6 @@ if ( ZM_OPT_USE_AUTH and (!isset($user)) and ($view != 'login') and ($view != 'n if ( ! $request ) { zm_session_start(); $_SESSION['postLoginQuery'] = $_SERVER['QUERY_STRING']; - ZM\Error("postLoginQuery " . $_SESSION['postLoginQuery']); session_write_close(); } $request = null; From fb5111ffe4de9b5f417150af14f9bca236d7b620 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 24 Jan 2020 17:01:47 -0500 Subject: [PATCH 02/33] bump version to 1.34.1 for release --- db/zm_update-1.34.1.sql | 5 +++++ distros/redhat/zoneminder.spec | 2 +- version | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 db/zm_update-1.34.1.sql diff --git a/db/zm_update-1.34.1.sql b/db/zm_update-1.34.1.sql new file mode 100644 index 000000000..65ba2e5ff --- /dev/null +++ b/db/zm_update-1.34.1.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.0 database to 1.34.1 +-- +-- No changes required +-- diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 345139284..6b834ed54 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.34.0 +Version: 1.34.1 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index 2b17ffd50..a95a46d9f 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.34.0 +1.34.1 From c5b419976f23008d632bf4cdf7f0fe440d663b00 Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 28 Jan 2020 20:22:18 -0700 Subject: [PATCH 03/33] fix spelling --- docs/userguide/gettingstarted.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/userguide/gettingstarted.rst b/docs/userguide/gettingstarted.rst index 509f413a2..226da8a41 100644 --- a/docs/userguide/gettingstarted.rst +++ b/docs/userguide/gettingstarted.rst @@ -109,7 +109,7 @@ This brings up the new monitor window: * In this example, the Function is 'Modect', which means it will start recording if motion is detected on that camera feed. The parameters for what constitutes motion detected is specific in :doc:`definezone` -* In Analytis FPS, we've put in 5FPS here. Note that you should not put an FPS that is greater than the camera FPS. In my case, 5FPS is sufficient for my needs +* In Analysis FPS, we've put in 5FPS here. Note that you should not put an FPS that is greater than the camera FPS. In my case, 5FPS is sufficient for my needs .. note:: Leave Maximum FPS and Alarm Maximum FPS **empty** if you are configuring an IP camera. In older versions of ZoneMinder, you were encouraged to put a value here, but that is no longer recommended. Infact, if you see your feed going much slower than the feed is supposed to go, or you get a lot of buffering/display issues, make sure this is empty. If you need to control camera FPS, please do it directly on the camera (via its own web interface, for example) From 9a71ca9a2f42f2618ec17293368ce23a62baf86d Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Fri, 31 Jan 2020 19:25:35 -0600 Subject: [PATCH 04/33] update specfile changelog --- distros/redhat/zoneminder.spec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 6b834ed54..0638c6e49 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -416,6 +416,9 @@ EOF %dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %changelog +* Fri Jan 31 2020 Andrew Bauer - 1.34.1-1 +- 1.34.1 Release + * Sat Jan 18 2020 Andrew Bauer - 1.34.0-1 - 1.34.0 Release From 42b3bb6a53e7ec778e5aac8545219f9a7211d441 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 4 Feb 2020 16:40:52 -0500 Subject: [PATCH 05/33] increase width of google sitekey and secret key inputs to 100% --- web/skins/classic/css/base/views/options.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/skins/classic/css/base/views/options.css b/web/skins/classic/css/base/views/options.css index f7123086c..a60d98001 100644 --- a/web/skins/classic/css/base/views/options.css +++ b/web/skins/classic/css/base/views/options.css @@ -21,3 +21,8 @@ input.large { #contentTable.userTable .colMonitor, #contentTable.userTable .colUsername { text-align: left; } + +input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SITEKEY]"], +input[name="newConfig[ZM_OPT_GOOG_RECAPTCHA_SECRETKEY]"] { + width: 100%; +} From 1c2f2657d2c551add2f18b1c2c9ce26766466348 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 4 Feb 2020 16:41:19 -0500 Subject: [PATCH 06/33] We don't do automatic login when recaptcha is enabled, so add the login calls on successful recaptcha --- web/includes/actions/login.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/web/includes/actions/login.php b/web/includes/actions/login.php index 6c8312b2f..68ca4e604 100644 --- a/web/includes/actions/login.php +++ b/web/includes/actions/login.php @@ -49,14 +49,23 @@ if ( ('login' == $action) && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == ' // as it produces the same error as when you don't answer a recaptcha if ( isset($responseData['error-codes']) && is_array($responseData['error-codes']) ) { if ( !in_array('invalid-input-secret', $responseData['error-codes']) ) { - Error('reCaptcha authentication failed'); + ZM\Error('reCaptcha authentication failed. response was: ' . print_r($responseData['error-codes'],true)); unset($user); // unset should be ok here because we aren't in a function return; } else { - Error('Invalid recaptcha secret detected'); + ZM\Error('Invalid recaptcha secret detected'); } } } // end if success==false + if ( ! (empty($_REQUEST['username']) or empty($_REQUEST['password'])) ) { + $ret = validateUser($_REQUEST['username'], $_REQUEST['password']); + if ( !$ret[0] ) { + ZM\Error($ret[1]); + unset($user); // unset should be ok here because we aren't in a function + } else { + $user = $ret[0]; + } + } # end if have username and password } // end if using reCaptcha // if captcha existed, it was passed From af6b51f0d38b1167a55e0868ac60ffc5d169bffa Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 4 Feb 2020 16:47:33 -0500 Subject: [PATCH 07/33] release 1.34.2 --- db/zm_update-1.34.2.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 db/zm_update-1.34.2.sql diff --git a/db/zm_update-1.34.2.sql b/db/zm_update-1.34.2.sql new file mode 100644 index 000000000..1fcc882d2 --- /dev/null +++ b/db/zm_update-1.34.2.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.1 database to 1.34.2 +-- +-- No changes required +-- From b7ad7ab1fc25dfaf626f12c97d3403ff0aab1b2e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 4 Feb 2020 16:47:39 -0500 Subject: [PATCH 08/33] release 1.34.2 --- distros/redhat/zoneminder.spec | 5 ++++- version | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 0638c6e49..3724c3aad 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.34.1 +Version: 1.34.2 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons @@ -416,6 +416,9 @@ EOF %dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %changelog +* Tue Feb 04 2020 Isaac Connor - 1.34.2-1 +- 1.34.2 Release + * Fri Jan 31 2020 Andrew Bauer - 1.34.1-1 - 1.34.1 Release diff --git a/version b/version index a95a46d9f..00e952d04 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.34.1 +1.34.2 From e8a0ed920855e21cd8778741537a5ff0a3108069 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 6 Feb 2020 13:20:35 -0500 Subject: [PATCH 09/33] quotes and improve the output of the ajaxError to say what the action was and who the user was --- web/ajax/add_monitors.php | 68 ++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/web/ajax/add_monitors.php b/web/ajax/add_monitors.php index de24cdfac..e7048bb4a 100644 --- a/web/ajax/add_monitors.php +++ b/web/ajax/add_monitors.php @@ -46,18 +46,18 @@ if ( 0 ) { SOL_SOCKET, // socket level SO_SNDTIMEO, // timeout option array( - "sec"=>0, // Timeout in seconds - "usec"=>500 // I assume timeout in microseconds + 'sec'=>0, // Timeout in seconds + 'usec'=>500 // I assume timeout in microseconds ) ); $new_stream = null; -Info("Testing connection to " . $url_bits['host'].':'.$port); + Info('Testing connection to '.$url_bits['host'].':'.$port); if ( socket_connect( $socket, $url_bits['host'], $port ) ) { $new_stream = $url_bits; // make a copy $new_stream['port'] = $port; } else { socket_close($socket); - ZM\Info("No connection to ".$url_bits['host'] . " on port $port"); + ZM\Info('No connection to '.$url_bits['host'].' on port '.$port); continue; } if ( $new_stream ) { @@ -92,10 +92,10 @@ Info("Testing connection to " . $url_bits['host'].':'.$port); } foreach ( $available_streams as &$stream ) { # check for existence in db. - $stream['url'] = unparse_url( $stream, array('path'=>'/','query'=>'action=stream') ); - $monitors = ZM\Monitor::find( array('Path'=>$stream['url']) ); + $stream['url'] = unparse_url($stream, array('path'=>'/','query'=>'action=stream')); + $monitors = ZM\Monitor::find(array('Path'=>$stream['url'])); if ( count($monitors) ) { - ZM\Info("Found monitors matching " . $stream['url'] ); + ZM\Info('Found monitors matching ' . $stream['url'] ); $stream['Monitor'] = $monitors[0]; if ( isset( $stream['Width'] ) and ( $stream['Monitor']->Width() != $stream['Width'] ) ) { $stream['Warning'] .= 'Monitor width ('.$stream['Monitor']->Width().') and stream width ('.$stream['Width'].") do not match!\n"; @@ -106,11 +106,11 @@ Info("Testing connection to " . $url_bits['host'].':'.$port); } else { $stream['Monitor'] = clone $defaultMonitor; if ( isset($stream['Width']) ) { - $stream['Monitor']->Width( $stream['Width'] ); - $stream['Monitor']->Height( $stream['Height'] ); + $stream['Monitor']->Width($stream['Width']); + $stream['Monitor']->Height($stream['Height']); } if ( isset($stream['Name']) ) { - $stream['Monitor']->Name( $stream['Name'] ); + $stream['Monitor']->Name($stream['Name']); } } // Monitor found or not } // end foreach Stream @@ -121,16 +121,16 @@ Info("Testing connection to " . $url_bits['host'].':'.$port); return $available_streams; } // end function probe -if ( canEdit( 'Monitors' ) ) { +if ( canEdit('Monitors') ) { switch ( $_REQUEST['action'] ) { case 'probe' : { $available_streams = array(); $url_bits = null; - if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $_REQUEST['url'] ) ) { - $url_bits = array( 'host'=>$_REQUEST['url'] ); + if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $_REQUEST['url']) ) { + $url_bits = array('host'=>$_REQUEST['url']); } else { - $url_bits = parse_url( $_REQUEST['url'] ); + $url_bits = parse_url($_REQUEST['url']); } if ( 0 ) { @@ -147,13 +147,13 @@ if ( 0 ) { } if ( ! $url_bits ) { - ajaxError("The given URL was too malformed to parse."); + ajaxError('The given URL was too malformed to parse.'); return; } - $available_streams = probe( $url_bits ); + $available_streams = probe($url_bits); - ajaxResponse( array('Streams'=>$available_streams) ); + ajaxResponse(array('Streams'=>$available_streams)); return; } // end case url_probe case 'import': @@ -161,16 +161,16 @@ if ( 0 ) { $file = $_FILES['import_file']; - if ($file["error"] > 0) { - ajaxError($file["error"]); + if ( $file['error'] > 0 ) { + ajaxError($file['error']); return; } else { - $filename = $file["name"]; + $filename = $file['name']; - $available_streams = array(); + $available_streams = array(); $row = 1; - if (($handle = fopen($file['tmp_name'], 'r')) !== FALSE) { - while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { + if ( ($handle = fopen($file['tmp_name'], 'r')) !== FALSE ) { + while ( ($data = fgetcsv($handle, 1000, ',')) !== FALSE ) { $name = $data[0]; $url = $data[1]; $group = $data[2]; @@ -178,16 +178,16 @@ if ( 0 ) { $url_bits = null; if ( preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $url) ) { - $url_bits = array( 'host'=>$url, 'scheme'=>'http' ); + $url_bits = array('host'=>$url, 'scheme'=>'http'); } else { - $url_bits = parse_url( $url ); + $url_bits = parse_url($url); } if ( ! $url_bits ) { ZM\Info("Bad url, skipping line $name $url $group"); continue; } - $available_streams += probe( $url_bits ); + $available_streams += probe($url_bits); //$url_bits['url'] = unparse_url( $url_bits ); //$url_bits['Monitor'] = $defaultMonitor; @@ -197,23 +197,19 @@ if ( 0 ) { } // end while rows fclose($handle); - ajaxResponse( array('Streams'=>$available_streams) ); + ajaxResponse(array('Streams'=>$available_streams)); } else { - ajaxError("Uploaded file does not exist"); + ajaxError('Uploaded file does not exist'); return; } - } } // end case import default: - { - ZM\Warning("unknown action " . $_REQUEST['action'] ); - } // end ddcase default - } + ZM\Warning('unknown action '.$_REQUEST['action']); + } // end switch action } else { - ZM\Warning("Cannot edit monitors" ); + ZM\Warning('Cannot edit monitors'); } -ajaxError( 'Unrecognised action or insufficient permissions' ); - +ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']); ?> From dd0ce50c17ba7a9c2524a189be231edd864412dd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 6 Feb 2020 13:21:30 -0500 Subject: [PATCH 10/33] quotes and improve the output of the ajaxError to say what the action was and who the user was --- web/ajax/console.php | 54 +++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/web/ajax/console.php b/web/ajax/console.php index 1a5919b58..ae8a60b15 100644 --- a/web/ajax/console.php +++ b/web/ajax/console.php @@ -1,35 +1,33 @@ beginTransaction(); - $dbConn->exec('LOCK TABLES Monitors WRITE'); - for ( $i = 0; $i < count($monitor_ids); $i += 1 ) { - $monitor_id = $monitor_ids[$i]; - $monitor_id = preg_replace( '/^monitor_id-/', '', $monitor_id ); - if ( ( ! $monitor_id ) or ! ( is_integer( $monitor_id ) or ctype_digit( $monitor_id ) ) ) { - Warning("Got $monitor_id from " . $monitor_ids[$i]); - continue; - } - dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($i, $monitor_id)); - } // end for each monitor_id - $dbConn->commit(); - $dbConn->exec('UNLOCK TABLES'); - - return; - } // end case sort - default: - { - ZM\Warning('unknown action ' . $_REQUEST['action']); - } // end ddcase default - } + switch ( $_REQUEST['action'] ) { + case 'sort' : + { + $monitor_ids = $_POST['monitor_ids']; + # Two concurrent sorts could generate odd sortings... so lock the table. + global $dbConn; + $dbConn->beginTransaction(); + $dbConn->exec('LOCK TABLES Monitors WRITE'); + for ( $i = 0; $i < count($monitor_ids); $i += 1 ) { + $monitor_id = $monitor_ids[$i]; + $monitor_id = preg_replace('/^monitor_id-/', '', $monitor_id); + if ( ( !$monitor_id ) or ! ( is_integer($monitor_id) or ctype_digit($monitor_id) ) ) { + Warning('Got '.$monitor_id.' from '.$monitor_ids[$i]); + continue; + } + dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($i, $monitor_id)); + } // end for each monitor_id + $dbConn->commit(); + $dbConn->exec('UNLOCK TABLES'); + + return; + } // end case sort + default: + ZM\Warning('unknown action '.$_REQUEST['action']); + } } else { ZM\Warning('Cannot edit monitors'); } -ajaxError('Unrecognised action or insufficient permissions'); +ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']); ?> From 1cbc75813c8a9eea1929a18181b0f4a2a07c142c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 6 Feb 2020 13:21:57 -0500 Subject: [PATCH 11/33] improve the output of the ajaxError to say what the action was and who the user was --- web/ajax/event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/ajax/event.php b/web/ajax/event.php index cb4d3d7ad..9c2e57c2c 100644 --- a/web/ajax/event.php +++ b/web/ajax/event.php @@ -155,5 +155,5 @@ if ( canEdit('Events') ) { } // end switch action } // end if canEdit('Events') -ajaxError('Unrecognised action or insufficient permissions'); +ajaxError('Unrecognised action '.$_REQUEST['action'].' or insufficient permissions for user ' . $user['Username']); ?> From c465fa55d8fab1f9ca554f5c8786a719f4d740e9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 6 Feb 2020 13:22:22 -0500 Subject: [PATCH 12/33] Use ZM_PATH_FFMPEG instead of ffmpeg --- web/views/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/views/image.php b/web/views/image.php index 295b4b6c2..b05cc79d9 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -200,7 +200,7 @@ if ( empty($_REQUEST['path']) ) { header('HTTP/1.0 404 Not Found'); ZM\Fatal("Can't create frame images from video because there is no video file for this event at (".$Event->Path().'/'.$Event->DefaultVideo() ); } - $command ='ffmpeg -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -frames:v 1 '.$path; + $command = ZM_PATH_FFMPEG.' -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -frames:v 1 '.$path; #$command ='ffmpeg -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path; #$command ='ffmpeg -v 0 -i '.$Storage->Path().'/'.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path; ZM\Logger::Debug("Running $command"); From 83411cd6f257f59590554af38cdce94c5cfa2c36 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 6 Feb 2020 14:45:38 -0500 Subject: [PATCH 13/33] fix zmrepo dest dir when doing a proper release --- utils/do_debian_package.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index b60996a78..539205e3b 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -128,14 +128,14 @@ else fi; fi +IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE" if [ "$PPA" == "" ]; then if [ "$RELEASE" != "" ]; then # We need to use our official tarball for the original source, so grab it and overwrite our generated one. - IFS='.' read -r -a VERSION <<< "$RELEASE" - if [ "${VERSION[0]}.${VERSION[1]}" == "1.30" ]; then + if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then PPA="ppa:iconnor/zoneminder-stable" else - PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}" + PPA="ppa:iconnor/zoneminder-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" fi; else if [ "$BRANCH" == "" ]; then @@ -316,7 +316,7 @@ EOF read -p "Do you want to upload this binary to zmrepo? (y/N)" if [[ $REPLY == [yY] ]]; then if [ "$RELEASE" != "" ]; then - scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/stable/mini-dinstall/incoming/" + scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/release-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}/mini-dinstall/incoming/" else if [ "$BRANCH" == "" ]; then scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/" From 4479029e271e1940cef3ffbe5523a590ccb557c6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 6 Feb 2020 14:46:26 -0500 Subject: [PATCH 14/33] When doing a release, reploy to debian/release-VERSION instead of master. --- utils/packpack/rsync_xfer.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/utils/packpack/rsync_xfer.sh b/utils/packpack/rsync_xfer.sh index f6ce377e7..c9a737a03 100755 --- a/utils/packpack/rsync_xfer.sh +++ b/utils/packpack/rsync_xfer.sh @@ -18,7 +18,16 @@ for CMD in sshfs rsync find fusermount mkdir; do done if [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then - targetfolder="debian/master/mini-dinstall/incoming" + if [ "${RELEASE}" != "" ]; then + IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE" + if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then + targetfolder="debian/release/mini-dinstall/incoming" + else + targetfolder="debian/release-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}/mini-dinstall/incoming" + fi + else + targetfolder="debian/master/mini-dinstall/incoming" + fi else targetfolder="travis" fi From 4d82a5485ade9696a8ad08525f6feb414676e6b4 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 8 Feb 2020 10:46:54 -0600 Subject: [PATCH 15/33] rpm specfile I have to use my contact info for rpmfusion --- distros/redhat/zoneminder.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 3724c3aad..f36b239a8 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -416,7 +416,7 @@ EOF %dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %changelog -* Tue Feb 04 2020 Isaac Connor - 1.34.2-1 +* Tue Feb 04 2020 Andrew Bauer - 1.34.2-1 - 1.34.2 Release * Fri Jan 31 2020 Andrew Bauer - 1.34.1-1 From 466468b0fd81af3010972322bce110bd6ded38ee Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 9 Feb 2020 13:50:22 -0500 Subject: [PATCH 16/33] code style cleanups and uncomment get_service_urls call in create_services so that we actually parse the ONVIF response. --- onvif/modules/lib/ONVIF/Client.pm | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/onvif/modules/lib/ONVIF/Client.pm b/onvif/modules/lib/ONVIF/Client.pm index 90bfdd512..9676dff85 100644 --- a/onvif/modules/lib/ONVIF/Client.pm +++ b/onvif/modules/lib/ONVIF/Client.pm @@ -76,7 +76,7 @@ my %soap_version_of :ATTR(:default<('1.1')>); sub service { my ($self, $serviceName, $attr) = @_; -#print "service: " . $services_of{${$self}}{$serviceName}{$attr} . "\n"; + #print "service: " . $services_of{${$self}}{$serviceName}{$attr} . "\n"; # Please note that the Std::Class::Fast docs say not to use ident. $services_of{ident $self}{$serviceName}{$attr}; } @@ -114,18 +114,18 @@ sub get_service_urls { my $result = $self->service('device', 'ep')->GetServices( { IncludeCapability => 'true', # boolean - },, + } ); if ( $result ) { - foreach my $svc ( @{ $result->get_Service() } ) { + foreach my $svc ( @{ $result->get_Service() } ) { my $short_name = $namespace_map{$svc->get_Namespace()}; my $url_svc = $svc->get_XAddr()->get_value(); - if(defined $short_name && defined $url_svc) { -# print "Got $short_name service\n"; + if ( defined $short_name && defined $url_svc ) { + #print "Got $short_name service\n"; $self->set_service($short_name, 'url', $url_svc); } } - # } else { + #} else { #print "No results from GetServices: $result\n"; } @@ -142,14 +142,14 @@ sub get_service_urls { if ( my $function = $capabilities->can( "get_$capability" ) ) { my $Services = $function->( $capabilities ); if ( !$Services ) { - print "Nothing returned ffrom get_$capability\n"; + #print "Nothing returned from get_$capability\n"; } else { foreach my $svc ( @{ $Services } ) { # The capability versions don't have a namespace, so just lowercase them. my $short_name = lc $capability; my $url_svc = $svc->get_XAddr()->get_value(); - if( defined $url_svc) { -# print "Got $short_name service\n"; + if ( defined $url_svc ) { + #print "Got $short_name service\n"; $self->set_service($short_name, 'url', $url_svc); } } # end foreach svr @@ -202,10 +202,10 @@ sub BUILD { # deserializer_args => { strict => 0 } }); - $services_of{$ident}{'device'} = { url => $url_svc_device, ep => $svc_device }; + $services_of{$ident}{device} = { url => $url_svc_device, ep => $svc_device }; # Can't, don't have credentials yet - #$self->get_service_urls(); + # $self->get_service_urls(); } sub get_users { @@ -260,7 +260,7 @@ sub set_credentials { sub create_services { my ($self) = @_; - #$self->get_service_urls(); + $self->get_service_urls(); if ( defined $self->service('media', 'url') ) { $self->set_service('media', 'ep', ONVIF::Media::Interfaces::Media::MediaPort->new({ From 3b30d0a8dbe8175d443a975c802b3ce513b39900 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 9 Feb 2020 13:51:36 -0500 Subject: [PATCH 17/33] code restructure, return all valid profiles instead of just the first one. Also add multicase stream types. This incorporates ideas from patch from remi-crypto. Fixes #2825 --- scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in index f6863367d..33e708703 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in @@ -255,15 +255,15 @@ sub discover { sub profiles { my ( $client ) = @_; - my $endpoint = $client->get_endpoint('media'); - if ( ! $endpoint ) { - print "No media enpoint for client.\n"; + my $media = $client->get_endpoint('media'); + if ( ! $media ) { + print "No media endpoint for client.\n"; return; } - my $result = $endpoint->GetProfiles( { } ,, ); + my $result = $media->GetProfiles( { } ,, ); if ( ! $result ) { - print "No result from GetProfiles\n"; + print "No result from GetProfiles.\n"; return; } if ( $verbose ) { @@ -272,48 +272,52 @@ sub profiles { my $profiles = $result->get_Profiles(); - foreach my $profile ( @{ $profiles } ) { + foreach my $profile ( @{ $profiles } ) { my $token = $profile->attr()->get_token() ; - my $video_encoder_configuration = $profile->get_VideoEncoderConfiguration(); - if ( ! $video_encoder_configuration ) { - print "Unknown profile $token " . $profile->get_Name()."\n"; + my $Name = $profile->get_Name(); + + my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration(); + if ( ! $VideoEncoderConfiguration ) { + print "Unknown profile $token $Name.\n"; next; } - print $token . ", " . - $profile->get_Name() . ", " . - $profile->get_VideoEncoderConfiguration()->get_Encoding() . ", " . - $profile->get_VideoEncoderConfiguration()->get_Resolution()->get_Width() . ", " . - $profile->get_VideoEncoderConfiguration()->get_Resolution()->get_Height() . ", " . - $profile->get_VideoEncoderConfiguration()->get_RateControl()->get_FrameRateLimit() . - ", "; # Specification gives conflicting values for unicast stream types, try both. # http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri - foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast' ) { - $result = $client->get_endpoint('media')->GetStreamUri( { + foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast', 'RTP-multicast', 'RTP-Multicast' ) { + my $StreamUri = $media->GetStreamUri( { StreamSetup => { # ONVIF::Media::Types::StreamSetup - Stream => $streamtype, # StreamType - Transport => { # ONVIF::Media::Types::Transport - Protocol => 'RTSP', # TransportProtocol - }, + Stream => $streamtype, # StreamType + Transport => { # ONVIF::Media::Types::Transport + Protocol => 'RTSP', # TransportProtocol + }, }, ProfileToken => $token, # ReferenceToken - } ,, ); - last if $result; - } - die $result if not $result; -# print $result . "\n"; + } ); + next if ! ( $StreamUri and $StreamUri->can('get_MediaUri') ); + my $MediaUri = $StreamUri->get_MediaUri(); + next if ! $MediaUri; + my $Uri = $MediaUri->get_Uri(); + next if ! $Uri; + + print join(', ', $token, + $Name, + $VideoEncoderConfiguration->get_Encoding(), + $VideoEncoderConfiguration->get_Resolution()->get_Width(), + $VideoEncoderConfiguration->get_Resolution()->get_Height(), + $VideoEncoderConfiguration->get_RateControl()->get_FrameRateLimit(), + $Uri, + ) . "\n"; + } # end foreach streamtype - print $result->get_MediaUri()->get_Uri() . - "\n"; } # end foreach profile # # use message parser without schema validation ??? # -} +} # end sub profiles sub move { my ($client, $dir) = @_; From 137bdb1f241bc33414bbdc145dfce5f06b22bd25 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 10 Feb 2020 14:20:46 -0500 Subject: [PATCH 18/33] Make shm have error or warning class if full --- web/skins/classic/includes/functions.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 9f14d93d0..ceaee50d5 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -423,7 +423,14 @@ if ( (!ZM_OPT_USE_AUTH) or $user ) { $storage_areas = $storage_areas_with_no_server_id; if ( count($storage_areas) <= 4 ) echo implode(', ', array_map($func, $storage_areas)); - echo ' ' . ZM_PATH_MAP .': '. getDiskPercent(ZM_PATH_MAP).'%'; + $shm_percent = getDiskPercent(ZM_PATH_MAP); + $class = ''; + if ( $shm_percent > 98 ) { + $class = 'error'; + } else if ( $shm_percent > 90 ) { + $class = 'warning'; + } + echo ' '.ZM_PATH_MAP.': '.$shm_percent.'%'; ?> From 4f25426b1f39df1fa98fdc7f60180d760c1952ad Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 10 Feb 2020 16:22:01 -0500 Subject: [PATCH 19/33] load event orientation as well and if it's an mp4 do the rotation on the image before outputting. Fixes #2829 --- src/zm_eventstream.cpp | 27 ++++++++++++++++++++++++++- src/zm_eventstream.h | 1 + 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 2ca12972d..36ea2b391 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -117,7 +117,7 @@ bool EventStream::loadEventData(uint64_t event_id) { snprintf(sql, sizeof(sql), "SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, " "(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, " - "`DefaultVideo`, `Scheme`, `SaveJPEGs` FROM `Events` WHERE `Id` = %" PRIu64, event_id); + "`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id); if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); @@ -160,6 +160,7 @@ bool EventStream::loadEventData(uint64_t event_id) { event_data->scheme = Storage::SHALLOW; } event_data->SaveJPEGs = dbrow[7] == NULL ? 0 : atoi(dbrow[7]); + event_data->Orientation = (Monitor::Orientation)(dbrow[8] == NULL ? 0 : atoi(dbrow[8])); mysql_free_result(result); Storage * storage = new Storage(event_data->storage_id); @@ -703,6 +704,30 @@ Debug(1, "Loading image"); Error("Failed getting a frame."); return false; } + + // when stored as an mp4, we just have the rotation as a flag in the headers + // so we need to rotate it before outputting + if ( event_data->Orientation != Monitor::ROTATE_0 ) { + Debug(2, "Rotating image %d", event_data->Orientation); + switch ( event_data->Orientation ) { + case Monitor::ROTATE_0 : + // No action required + break; + case Monitor::ROTATE_90 : + case Monitor::ROTATE_180 : + case Monitor::ROTATE_270 : + image->Rotate((event_data->Orientation-1)*90); + break; + case Monitor::FLIP_HORI : + case Monitor::FLIP_VERT : + image->Flip(event_data->Orientation==Monitor::FLIP_HORI); + break; + default: + Error("Invalid Orientation: %d", event_data->Orientation); + } + } else { + Debug(2, "Not Rotating image %d", event_data->Orientation); + } // end if have rotation } else { Error("Unable to get a frame"); return false; diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 5e7d91bb2..d0b5827e7 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -66,6 +66,7 @@ class EventStream : public StreamBase { char video_file[PATH_MAX]; Storage::Schemes scheme; int SaveJPEGs; + Monitor::Orientation Orientation; }; protected: From ac8c7a9347ffdd945a0515eb26174cbdd94b5150 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 Feb 2020 13:21:02 -0500 Subject: [PATCH 20/33] default ZM_TIMEZONE value to empty in which case we don't set the value --- scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index cd93dfe15..de4eb6984 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -785,7 +785,7 @@ our @options = ( }, { name => 'ZM_TIMEZONE', - default => 'UTC', + default => '', description => 'The timezone that php should use.', help => q` This should be set equal to the system timezone of the mysql server`, From 821355c117668b32d56729a30700b8bf779aa294 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 Feb 2020 13:21:28 -0500 Subject: [PATCH 21/33] Only set date.timezone if we have a configured value in the Config --- web/includes/config.php.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/includes/config.php.in b/web/includes/config.php.in index 909a10e15..57064f22d 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -193,7 +193,8 @@ if ( ! defined('ZM_SERVER_ID') ) { } } -ini_set('date.timezone', ZM_TIMEZONE); +if ( ZM_TIMEZONE ) + ini_set('date.timezone', ZM_TIMEZONE); function process_configfile($configFile) { if ( is_readable( $configFile ) ) { From 600dbba8c52b3ed5c27260617321659a90edbce2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 Feb 2020 13:23:53 -0500 Subject: [PATCH 22/33] We don't need to set date_default_timezone_get --- web/index.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/index.php b/web/index.php index 7f6e17432..2f1a6cf62 100644 --- a/web/index.php +++ b/web/index.php @@ -202,7 +202,8 @@ isset($action) || $action = NULL; if ( (!$view and !$request) or ($view == 'console') ) { // Verify the system, php, and mysql timezones all match - date_default_timezone_set(ZM_TIMEZONE); + #if ( ZM_TIMEZONE ) + #date_default_timezone_set(ZM_TIMEZONE); check_timezone(); } From 844ff529e1919bf3ee78aa5d168398e138cf13e3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 Feb 2020 13:24:26 -0500 Subject: [PATCH 23/33] add an unset value to ZM_TIMEZONE in Options->System --- web/skins/classic/views/options.php | 58 ++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index 82692fbaf..731c7ac11 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -393,41 +393,41 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $ $configCats[$tab]['ZM_SKIN_DEFAULT']['Hint'] = join('|', array_map('basename', glob('skins/*',GLOB_ONLYDIR))); $configCats[$tab]['ZM_CSS_DEFAULT']['Hint'] = join('|', array_map ( 'basename', glob('skins/'.ZM_SKIN_DEFAULT.'/css/*',GLOB_ONLYDIR) )); $configCats[$tab]['ZM_BANDWIDTH_DEFAULT']['Hint'] = $bandwidth_options; + function timezone_list() { - static $timezones = null; + static $timezones = null; - if ($timezones === null) { - $timezones = []; - $offsets = []; - $now = new DateTime('now', new DateTimeZone('UTC')); + if ( $timezones === null ) { + $timezones = []; + $offsets = []; + $now = new DateTime('now', new DateTimeZone('UTC')); - foreach (DateTimeZone::listIdentifiers() as $timezone) { - $now->setTimezone(new DateTimeZone($timezone)); - $offsets[] = $offset = $now->getOffset(); - $timezones[$timezone] = '(' . format_GMT_offset($offset) . ') ' . format_timezone_name($timezone); + foreach ( DateTimeZone::listIdentifiers() as $timezone ) { + $now->setTimezone(new DateTimeZone($timezone)); + $offsets[] = $offset = $now->getOffset(); + $timezones[$timezone] = '(' . format_GMT_offset($offset) . ') ' . format_timezone_name($timezone); + } + + array_multisort($offsets, $timezones); + } + + return $timezones; } - array_multisort($offsets, $timezones); - } + function format_GMT_offset($offset) { + $hours = intval($offset / 3600); + $minutes = abs(intval($offset % 3600 / 60)); + return 'GMT' . ($offset ? sprintf('%+03d:%02d', $hours, $minutes) : ''); + } - return $timezones; -} - -function format_GMT_offset($offset) { - $hours = intval($offset / 3600); - $minutes = abs(intval($offset % 3600 / 60)); - return 'GMT' . ($offset ? sprintf('%+03d:%02d', $hours, $minutes) : ''); -} - -function format_timezone_name($name) { - $name = str_replace('/', ', ', $name); - $name = str_replace('_', ' ', $name); - $name = str_replace('St ', 'St. ', $name); - return $name; -} - $configCats[$tab]['ZM_TIMEZONE']['Hint'] = timezone_list(); - - } + function format_timezone_name($name) { + $name = str_replace('/', ', ', $name); + $name = str_replace('_', ' ', $name); + $name = str_replace('St ', 'St. ', $name); + return $name; + } + $configCats[$tab]['ZM_TIMEZONE']['Hint'] = array('', translate('Unset') + timezone_list(); + } # end if tab == system ?>
From 9f9a97f596c9c97ad13424988038e883759058ce Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 Feb 2020 14:29:21 -0500 Subject: [PATCH 24/33] fix dropdown with unset option --- web/skins/classic/views/options.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index 731c7ac11..2775656a3 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -426,7 +426,7 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $ $name = str_replace('St ', 'St. ', $name); return $name; } - $configCats[$tab]['ZM_TIMEZONE']['Hint'] = array('', translate('Unset') + timezone_list(); + $configCats[$tab]['ZM_TIMEZONE']['Hint'] = array(''=> translate('Unset')) + timezone_list(); } # end if tab == system ?> From 289bde869d95f859a6a13909248f0bfc170c89d3 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 Feb 2020 14:31:48 -0500 Subject: [PATCH 25/33] use a better language for unset value --- web/lang/en_gb.php | 1 + web/skins/classic/views/options.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index ad4911a4d..a8569639c 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -771,6 +771,7 @@ $SLANG = array( 'TurboPanSpeed' => 'Turbo Pan Speed', 'TurboTiltSpeed' => 'Turbo Tilt Speed', 'Type' => 'Type', + 'TZUnset' => 'Unset - use value in php.ini', 'Unarchive' => 'Unarchive', 'Undefined' => 'Undefined', 'Units' => 'Units', diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index 2775656a3..e1a77b874 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -426,7 +426,7 @@ foreach ( array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)) as $ $name = str_replace('St ', 'St. ', $name); return $name; } - $configCats[$tab]['ZM_TIMEZONE']['Hint'] = array(''=> translate('Unset')) + timezone_list(); + $configCats[$tab]['ZM_TIMEZONE']['Hint'] = array(''=> translate('TZUnset')) + timezone_list(); } # end if tab == system ?> From 4758b2304ae552119add2dc1cd7c1c939ff9b8be Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 Feb 2020 15:02:12 -0500 Subject: [PATCH 26/33] Do not alter the start time of an event --- scripts/zmaudit.pl.in | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 9204ef3a1..e57d98add 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -420,15 +420,15 @@ MAIN: while( $loop ) { Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir"); { my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*"); - Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . ' entries.' ); + Debug('glob("'.$monitor_dir.'/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned '.(scalar @event_dirs).' entries.'); foreach my $event_dir ( @event_dirs ) { if ( ! -d $event_dir ) { - Debug( "$event_dir is not a dir. Skipping" ); + Debug("$event_dir is not a dir. Skipping"); next; } my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/; - if ( ! $event_id ) { - Debug("Unable to parse date/event_id from $event_dir"); + if ( !$event_id ) { + Debug('Unable to parse date/event_id from '.$event_dir); next; } my $Event = $fs_events->{$event_id} = new ZoneMinder::Event(); @@ -438,8 +438,9 @@ MAIN: while( $loop ) { $Event->MonitorId( $monitor_dir ); $Event->StorageId( $Storage->Id() ); $Event->Path(); + $Event->age(); Debug("Have event $$Event{Id} at $$Event{Path}"); - $Event->StartTime( POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path())) ) ); + $Event->StartTime(POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path())))); } # end foreach event } @@ -466,13 +467,13 @@ MAIN: while( $loop ) { } # end foreach event chdir( $Storage->Path() ); } # if USE_DEEP_STORAGE - Debug( 'Got '.int(keys(%$fs_events))." filesystem events for monitor $monitor_dir" ); + Debug('Got '.int(keys(%$fs_events)).' filesystem events for monitor '.$monitor_dir); delete_empty_subdirs($$Storage{Path}.'/'.$monitor_dir); } # end foreach monitor if ( $cleaned ) { - Debug("First stage cleaning done. Restarting."); + Debug('First stage cleaning done. Restarting.'); redo MAIN; } @@ -484,7 +485,7 @@ MAIN: while( $loop ) { next; } my @event_ids = keys %$fs_events; - Debug('Have ' .scalar @event_ids . " events for monitor $monitor_id"); + Debug('Have ' .scalar @event_ids . ' events for monitor '.$monitor_id); foreach my $fs_event_id ( sort { $a <=> $b } keys %$fs_events ) { @@ -499,8 +500,8 @@ MAIN: while( $loop ) { } my $age = $Event->age(); - if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { - aud_print( "Filesystem event $fs_event_id at $$Event{Path} does not exist in database and is $age seconds old" ); + if ( $age and ($age > $Config{ZM_AUDIT_MIN_AGE}) ) { + aud_print("Filesystem event $fs_event_id at $$Event{Path} does not exist in database and is $age seconds old"); if ( confirm() ) { $Event->delete_files(); $cleaned = 1; @@ -586,7 +587,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) { } else { Debug("$$Event{Id} Not found at $path"); } - } + } # end foreach Storage if ( $Event->Archived() ) { Warning("Event $$Event{Id} is Archived. Taking no further action on it."); next; @@ -638,18 +639,13 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) { Info("Updating storage area on event $$Event{Id} from $$Event{StorageId} to $$fs_events{$db_event}{StorageId}"); $Event->StorageId($$fs_events{$db_event}->StorageId()); } - if ( $$fs_events{$db_event}->StartTime() ne $Event->StartTime() ) { + if ( ! $Event->StartTime() ) { Info("Updating StartTime on event $$Event{Id} from $$Event{StartTime} to $$fs_events{$db_event}{StartTime}"); - if ( $$Event{Scheme} eq 'Deep' ) { - $Event->StartTime($$fs_events{$db_event}->StartTime()); - } else { - $Event->StartTime($$fs_events{$db_event}->StartTime()); - } - $Event->save(); + $Event->StartTime($$fs_events{$db_event}->StartTime()); } $Event->save(); - } + } # end if Event exists in db and not in filesystem } # end if ! in fs_events } # foreach db_event } # end foreach db_monitor From e6785fca909bc4b004b508fcf968dd7ae82b5df2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 Feb 2020 17:10:08 -0500 Subject: [PATCH 27/33] fix repo when doing a release --- utils/do_debian_package.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index b60996a78..539205e3b 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -128,14 +128,14 @@ else fi; fi +IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE" if [ "$PPA" == "" ]; then if [ "$RELEASE" != "" ]; then # We need to use our official tarball for the original source, so grab it and overwrite our generated one. - IFS='.' read -r -a VERSION <<< "$RELEASE" - if [ "${VERSION[0]}.${VERSION[1]}" == "1.30" ]; then + if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then PPA="ppa:iconnor/zoneminder-stable" else - PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}" + PPA="ppa:iconnor/zoneminder-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" fi; else if [ "$BRANCH" == "" ]; then @@ -316,7 +316,7 @@ EOF read -p "Do you want to upload this binary to zmrepo? (y/N)" if [[ $REPLY == [yY] ]]; then if [ "$RELEASE" != "" ]; then - scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/stable/mini-dinstall/incoming/" + scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/release-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}/mini-dinstall/incoming/" else if [ "$BRANCH" == "" ]; then scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/" From cc9add931003d83f46e83a3d64cf9843fbad430b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 11 Feb 2020 17:40:33 -0500 Subject: [PATCH 28/33] fall back to ControlAddress if first ptz get fails --- .../lib/ZoneMinder/Control/Amcrest_HTTP.pm | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm index 3fd36a0c4..3bd469a48 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm @@ -135,16 +135,23 @@ sub sendCmd { $self->printMsg($cmd, 'Tx'); - my $req = HTTP::Request->new( GET=>"http://$$self{address}/$cmd" ); - my $res = $self->{ua}->request($req); + my $res = $self->{ua}->get("http://$$self{address}/$cmd"); if ( $res->is_success ) { $result = !undef; # Command to camera appears successful, write Info item to log - Info('Camera control: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + Info('Camera control: \''.$res->status_line().'\' for URL '.$$self{address}.'/'.$cmd); # TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status. } else { - Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$$self{address}.'/'.$cmd); + $res = $self->{ua}->get('http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + if ( $res->is_success ) { + $result = !undef; + # Command to camera appears successful, write Info item to log + Info('Camera control: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + } else { + Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + } } return $result; From 9c65a8b2932d8ef5f2337620174433a1f16ac993 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 12 Feb 2020 12:34:43 -0500 Subject: [PATCH 29/33] handle Amcrest cameras giving a 401 Unauthorized on the first attempt to send a command. Just send another. --- .../lib/ZoneMinder/Control/Amcrest_HTTP.pm | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm index 3bd469a48..dd30b2070 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Amcrest_HTTP.pm @@ -1,16 +1,6 @@ # ========================================================================== # -# ZoneMinder Amcrest HTTP API Control Protocol Module, 20180214, Rev 3.0 -# -# Change Log -# -# Rev 3.0: -# - Fixes incorrect method names -# - Updates control sequences to Amcrest HTTP Protocol API v 2.12 -# - Extends control features -# -# Rev 2.0: -# - Fixed installation instructions text, no changes to functionality. +# ZoneMinder Amcrest HTTP API Control Protocol Module # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -67,18 +57,20 @@ sub open { my $password; my $realm = 'Login to ' . $self->{Monitor}->{ControlDevice}; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); if ( $self->{Monitor}->{ControlAddress} =~ /(.*):(.*)@(.*)/ ) { $username = $1; $password = $2; $$self{address} = $3; + $self->{ua}->credentials($$self{address}, $realm, $username, $password); + # Testing seems to show that we need the username/password in each url as well as credentials + $$self{base_url} = 'http://'.$self->{Monitor}->{ControlDevice}; + Debug("Using initial credentials for $$self{address}, $realm, $username, $password"); } - $self->{ua} = LWP::UserAgent->new; - $self->{ua}->credentials($$self{address}, $realm, $username, $password); - $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); - - # Detect REALM - my $res = $self->{ua}->get($$self{address}.'/cgi-bin/ptz.cgi'); + # Detect REALM, has to be /cgi-bin/ptz.cgi because just / accepts no auth + my $res = $self->{ua}->get($$self{base_url}.'/cgi-bin/ptz.cgi'); if ( $res->is_success ) { $self->{state} = 'open'; @@ -94,21 +86,26 @@ sub open { if ( $$headers{'www-authenticate'} ) { my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/; - if ( $tokens =~ /\w+="([^"]+)"/i ) { + if ( $tokens =~ /realm="([^"]+)"/i ) { if ( $realm ne $1 ) { $realm = $1; - Debug("Changing REALM to $realm"); + Debug("Changing REALM to ($realm)"); $self->{ua}->credentials($$self{address}, $realm, $username, $password); $res = $self->{ua}->get($$self{address}.'/cgi-bin/ptz.cgi'); if ( $res->is_success() ) { $self->{state} = 'open'; return; + } elsif ( $res->status_line eq '400 Bad Request' ) { + # In testing, this second request fails with Bad Request, I assume because we didn't actually give it a command. + $self->{state} = 'open'; + return; + } else { + Error('Authentication still failed after updating REALM' . $res->status_line); + $headers = $res->headers(); + foreach my $k ( keys %$headers ) { + Debug("Header $k => $$headers{$k}"); + } # end foreach } - Error('Authentication still failed after updating REALM' . $res->status_line); - $headers = $res->headers(); - foreach my $k ( keys %$headers ) { - Debug("Initial Header $k => $$headers{$k}"); - } # end foreach } else { Error('Authentication failed, not a REALM problem'); } @@ -118,9 +115,12 @@ sub open { } else { Debug('No headers line'); } # end if headers + } else { + Error("Failed to get $$self{address}/cgi-bin/ptz.cgi ".$res->status_line()); + } # end if $res->status_line() eq '401 Unauthorized' - $self->{state} = 'open'; + $self->{state} = 'closed'; } sub close { @@ -143,14 +143,14 @@ sub sendCmd { Info('Camera control: \''.$res->status_line().'\' for URL '.$$self{address}.'/'.$cmd); # TODO: Add code to retrieve $res->message_decode or some such. Then we could do things like check the camera status. } else { - Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$$self{address}.'/'.$cmd); - $res = $self->{ua}->get('http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + # Try again + $res = $self->{ua}->get("http://$$self{address}/$cmd"); if ( $res->is_success ) { - $result = !undef; # Command to camera appears successful, write Info item to log - Info('Camera control: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + Info('Camera control: \''.$res->status_line().'\' for URL '.$$self{address}.'/'.$cmd); } else { - Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$self->{Monitor}->{ControlAddress}.'/'.$cmd); + Error('Camera control command FAILED: \''.$res->status_line().'\' for URL '.$$self{address}.'/'.$cmd); + $res = $self->{ua}->get('http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd); } } From 6abc91c11d10668585acee593ee0259666888f3b Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 15 Feb 2020 13:52:35 -0700 Subject: [PATCH 30/33] fix spelling --- docs/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 2aaeafb3e..76eadcaa3 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -127,7 +127,7 @@ If you are using the old credentials mechanism present in v1.0, then the credent Key lifetime (v2.0) ^^^^^^^^^^^^^^^^^^^^^^ -In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_exipres`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs. +In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_expires`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs. Understanding access/refresh tokens (v2.0) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 2c51b1436c9438e580a7988f4be06a9786edf685 Mon Sep 17 00:00:00 2001 From: zlodag <11688686+zlodag@users.noreply.github.com> Date: Sun, 16 Feb 2020 11:26:23 +1300 Subject: [PATCH 31/33] cleaned input for "zmcamtool.pl --export " to fix error when running in taint mode --- scripts/zmcamtool.pl.in | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/zmcamtool.pl.in b/scripts/zmcamtool.pl.in index 603c979bb..b7665d7d2 100644 --- a/scripts/zmcamtool.pl.in +++ b/scripts/zmcamtool.pl.in @@ -351,8 +351,11 @@ sub exportsql { } } - if ($ARGV[0]) { - $command .= qq( --where="Name = '$ARGV[0]'"); + my $name = $ARGV[0]; + if ($name) { + $name =~ /([A-Za-z0-9 -]*)/; # Only allow alphanumeric, dash and space + $name = $1; + $command .= qq( --where="Name = '$name'"); } $command .= " zm Controls MonitorPresets"; From a8615c1c85ab33d1a5e0a713e2aa4017433ae78b Mon Sep 17 00:00:00 2001 From: zlodag <11688686+zlodag@users.noreply.github.com> Date: Sun, 16 Feb 2020 11:30:06 +1300 Subject: [PATCH 32/33] fixed bug in control functions where left arrow panStep was inverted --- web/skins/classic/includes/control_functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/includes/control_functions.php b/web/skins/classic/includes/control_functions.php index 7554486af..80cfe4aff 100644 --- a/web/skins/classic/includes/control_functions.php +++ b/web/skins/classic/includes/control_functions.php @@ -125,7 +125,7 @@ function controlPanTilt($monitor, $cmds) { - + From c337fb921de376f39bfcc508d77939ccf85c095f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 17 Feb 2020 16:32:37 -0500 Subject: [PATCH 33/33] Don't die if no MetaDataConfigurations --- scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in index 33e708703..f4dedb3b9 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ONVIF.pm.in @@ -330,13 +330,22 @@ sub move { sub metadata { my ( $client ) = @_; - my $result = $client->get_endpoint('media')->GetMetadataConfigurations( { } ,, ); - die $result if not $result; - print $result . "\n"; + my $media = $client->get_endpoint('media'); + die 'No media endpoint.' if !$media; - $result = $client->get_endpoint('media')->GetVideoAnalyticsConfigurations( { } ,, ); - die $result if not $result; - print $result . "\n"; + my $result = $media->GetMetadataConfigurations( { } ,, ); + if ( ! $result ) { + print "No MetaDataConfigurations\n" if $verbose; + } else { + print $result . "\n"; + } + + $result = $media->GetVideoAnalyticsConfigurations( { } ,, ); + if ( ! $result ) { + print "No VideoAnalyticsConfigurations\n" if $verbose; + } else { + print $result . "\n"; + } # $result = $client->get_endpoint('analytics')->GetServiceCapabilities( { } ,, ); # die $result if not $result;