From 48df73fe358e8ff0a33fb1190e96b9569a7d7243 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 4 May 2020 13:24:48 -0400 Subject: [PATCH 01/47] Add UserId to Filters --- db/zm_create.sql.in | 1 + db/zm_update-1.35.3.sql | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 db/zm_update-1.35.3.sql diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index ce462ac4c..66cf8ba1a 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -282,6 +282,7 @@ DROP TABLE IF EXISTS `Filters`; CREATE TABLE `Filters` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', + `UserId` int(10) unsigned, `Query_json` text NOT NULL, `AutoArchive` tinyint(3) unsigned NOT NULL default '0', `AutoVideo` tinyint(3) unsigned NOT NULL default '0', diff --git a/db/zm_update-1.35.3.sql b/db/zm_update-1.35.3.sql new file mode 100644 index 000000000..940f334d5 --- /dev/null +++ b/db/zm_update-1.35.3.sql @@ -0,0 +1,12 @@ + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Filters' + AND column_name = 'UserId' + ) > 0, +"SELECT 'Column UserId already exists in Filters'", +"ALTER TABLE `Filters` ADD `UserId` int(10) unsigned AFTER `Name`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; From d1ca4ed81c69bc60c08d78f7f3a5f89ddfae616d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 7 May 2020 10:57:51 -0400 Subject: [PATCH 02/47] Add UserId to pre-populated Filters --- db/zm_create.sql.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 66cf8ba1a..6d3963fc5 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -784,6 +784,7 @@ INSERT INTO `Filters` `AutoCopy`, `AutoCopyTo`, `UpdateDiskSpace`, + `UserId`, `Background`, `Concurrent` ) @@ -804,6 +805,7 @@ INSERT INTO `Filters` 0/*AutoMove*/,0/*MoveTo*/, 0/*AutoCopy*/,0/*CopyTo*/, 0/*UpdateDiskSpace*/, + 1/*UserId = admin*/, 1/*Background*/, 0/*Concurrent*/ ); @@ -827,6 +829,7 @@ INSERT INTO `Filters` `AutoCopy`, `AutoCopyTo`, `UpdateDiskSpace`, + `UserId`, `Background`, `Concurrent` ) @@ -846,6 +849,7 @@ VALUES ( 0/*AutoMove*/,0/*MoveTo*/, 0/*AutoCopy*/,0/*CopyTo*/, 1/*UpdateDiskSpace*/, + 1/*UserId=admin*/, 1/*Background*/, 0/*Concurrent*/ ); From 58fae48736a11d927deab4839fb05489844f2adc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 7 May 2020 10:58:31 -0400 Subject: [PATCH 03/47] Add canEdit tests to delete,delete_files,CopyTo,MoveTo --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 36 ++++++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 589e4bbba..020c67562 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -352,6 +352,11 @@ sub GenerateVideo { sub delete { my $event = $_[0]; + if ( !$event->canEdit() ) { + Warning('No permission to delete event.'); + return 'No permission to delete event.'; + } + my $in_zmaudit = ( $0 =~ 'zmaudit.pl$'); if ( ! $in_zmaudit ) { @@ -402,6 +407,11 @@ sub delete { sub delete_files { my $event = shift; + if ( !$event->canEdit() ) { + Warning('No permission to delete event.'); + return 'No permission to delete event.'; + } + foreach my $Storage ( @_ ? ($_[0]) : ( new ZoneMinder::Storage($$event{StorageId}), @@ -570,6 +580,11 @@ sub DiskSpace { sub CopyTo { my ( $self, $NewStorage ) = @_; + if ( !$event->canEdit() ) { + Warning('No permission to copy event.'); + return 'No permission to copy event.'; + } + my $OldStorage = $self->Storage(undef); my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint if ( ! -e $OldPath ) { @@ -734,8 +749,13 @@ sub CopyTo { } # end sub CopyTo sub MoveTo { - my ( $self, $NewStorage ) = @_; + + if ( !$self->canEdit() ) { + Warning('No permission to move event.'); + return 'No permission to move event.'; + } + my $OldStorage = $self->Storage(undef); my $error = $self->CopyTo($NewStorage); @@ -857,16 +877,26 @@ sub files { sub has_capture_jpegs { @{$_[0]{capture_jpegs}} = grep(/^\d+\-capture\.jpg$/, $_[0]->files()); - Debug("have " . @{$_[0]{capture_jpegs}} . " capture jpegs"); + Debug('have ' . @{$_[0]{capture_jpegs}} . ' capture jpegs'); return @{$_[0]{capture_jpegs}} ? 1 : 0; } sub has_analyse_jpegs { @{$_[0]{analyse_jpegs}} = grep(/^\d+\-analyse\.jpg$/, $_[0]->files()); - Debug("have " . @{$_[0]{analyse_jpegs}} . " analyse jpegs"); + Debug('have ' . @{$_[0]{analyse_jpegs}} . ' analyse jpegs'); return @{$_[0]{analyse_jpegs}} ? 1 : 0; } +sub canEdit { + my $self = shift; + if ( !$ZoneMinder::user ) { + # No user loaded... assume running as system + return 1; + } + if ( !$$ZoneMinder::user{MonitorIds} ) { + # User has no +} + 1; __END__ From 4bd0cca4b81a2917dd5dd2270ee56580868c05e0 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 7 May 2020 10:59:06 -0400 Subject: [PATCH 04/47] Update Filter object, specifying fields and using parent's find methods --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 78 ++++++++++----------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index 21803d76a..568d303e9 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -49,49 +49,34 @@ use ZoneMinder::Database qw(:all); require ZoneMinder::Storage; require ZoneMinder::Server; -sub Name { - if ( @_ > 1 ) { - $_[0]{Name} = $_[1]; - } - return $_[0]{Name}; -} # end sub Path +use vars qw/ $table $primary_key %fields /; +$table = 'Users'; +$primary_key = 'Id'; -sub find { - shift if $_[0] eq 'ZoneMinder::Filter'; - my %sql_filters = @_; - - my $sql = 'SELECT * FROM Filters'; - my @sql_filters; - my @sql_values; - - if ( exists $sql_filters{Name} ) { - push @sql_filters , ' Name = ? '; - push @sql_values, $sql_filters{Name}; - } - - $sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters; - $sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit}; - - my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql) - or Fatal("Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr()); - my $res = $sth->execute(@sql_values) - or Fatal("Can't execute '$sql': ".$sth->errstr()); - - my @results; - - while( my $db_filter = $sth->fetchrow_hashref() ) { - my $filter = new ZoneMinder::Filter($$db_filter{Id}, $db_filter); - push @results, $filter; - } # end while - $sth->finish(); - - return @results; -} - -sub find_one { - my @results = find(@_); - return $results[0] if @results; -} +%fields = map { $_ => $_ } qw( +Id +Name +Query_json +AutoArchive +AutoVideo +AutoUpload +AutoEmail +EmailTo +EmailSubject +EmailBody +AutoMessage +AutoExecute +AutoExecuteCmd +AutoDelete +AutoMove +AutoMoveTo +AutoCopy +AutoCopyTo +UpdateDiskSpace +UserId +Background +Concurrent +); sub Execute { my $self = $_[0]; @@ -458,6 +443,15 @@ sub DateTimeToSQL { return POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime($dt_val)); } +sub User { + my $self = shift; + $$self{User} = shift if @_; + if ( ! $$self{User} and $$self{UserId} ) { + $$self{User} = ZoneMinder::User->find_one(Id=>$$self{UserId}); + } + return $$self{User}; +} + 1; __END__ # Below is stub documentation for your module. You'd better edit it! From a9a490126ae686ae6da1b2bc92f52425ee96c0f1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 7 May 2020 10:59:32 -0400 Subject: [PATCH 05/47] Add UserId to Filter object --- web/includes/Filter.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/includes/Filter.php b/web/includes/Filter.php index c15d8ba6a..ce9d11cd4 100644 --- a/web/includes/Filter.php +++ b/web/includes/Filter.php @@ -24,6 +24,7 @@ class Filter extends ZM_Object { 'AutoCopy' => 0, 'AutoCopyTo' => 0, 'UpdateDiskSpace' => 0, + 'UserId' => 0, 'Background' => 0, 'Concurrent' => 0, 'Query_json' => '', From e01538419f8ec2e5cfa84e9fd1b6b3cb19717ac2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 7 May 2020 10:59:50 -0400 Subject: [PATCH 06/47] Add FilterUser to language --- web/lang/en_gb.php | 1 + 1 file changed, 1 insertion(+) diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index b2a91cd45..0d5c341ff 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -373,6 +373,7 @@ $SLANG = array( 'Filters' => 'Filters', 'FilterUnset' => 'You must specify a filter width and height', 'FilterUploadEvents' => 'Upload all matches', + 'FilterUser' => 'User to run filter as', 'FilterVideoEvents' => 'Create video for all matches', 'First' => 'First', 'FlippedHori' => 'Flipped Horizontally', From d71de221aed80a9c57d1f32c9e2f2ab055916639 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 7 May 2020 11:00:50 -0400 Subject: [PATCH 07/47] If auth is turned on, add a UserId dropdown to filters --- web/skins/classic/views/filter.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index e6df44b76..56bd26d19 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -27,6 +27,7 @@ require_once('includes/Storage.php'); require_once('includes/Filter.php'); require_once('includes/Monitor.php'); require_once('includes/Zone.php'); +require_once('includes/User.php'); parseSort(); $filterNames = array(''=>translate('ChooseFilter')); @@ -200,6 +201,18 @@ if ( (null !== $filter->Concurrent()) and $filter->Concurrent() )

+ +

+UserId() ? $filter->UserId() : $user['Id'] +); ?> +

+ +

Date: Thu, 7 May 2020 11:07:27 -0400 Subject: [PATCH 08/47] Add a User object script --- scripts/ZoneMinder/lib/ZoneMinder/User.pm | 80 +++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 scripts/ZoneMinder/lib/ZoneMinder/User.pm diff --git a/scripts/ZoneMinder/lib/ZoneMinder/User.pm b/scripts/ZoneMinder/lib/ZoneMinder/User.pm new file mode 100644 index 000000000..47ba895a9 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/User.pm @@ -0,0 +1,80 @@ +# ========================================================================== +# +# ZoneMinder User Module +# Copyright (C) 2020 ZoneMinder LLC +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# ========================================================================== + +package ZoneMinder::Frame; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Object; + +use parent qw(ZoneMinder::Object); + +use vars qw/ $table $primary_key %fields /; +$table = 'Users'; +$primary_key = 'Id'; + +%fields = map { $_ => $_ } qw( +Id +Username +Password +Language +Enabled +Stream +Events +Control +Monitors +Groups +Devices +System +MaxBandwidth +MonitorIds +TokenMinExpiry +APIEnabled +); + +1; +__END__ + +=head1 NAME + +ZoneMinder::User - Perl Class for Users + +=head1 SYNOPSIS + +use ZoneMinder::User; + +=head1 AUTHOR + +Isaac Connor, Eisaac@zoneminder.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2017 ZoneMinder LLC + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + + +=cut From cc04ced40690bebbc99349d04b47075eacaa4f8e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 19 May 2020 16:56:58 -0400 Subject: [PATCH 09/47] load zmMontageLayout ddm explicitly instead of passing event element --- web/skins/classic/views/js/montage.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index d0e0c348c..3df853c76 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -212,7 +212,8 @@ function Monitor(monitorData) { * @param {*} element - the event data passed by onchange callback */ function selectLayout(element) { - layout = $j(element).val(); + var ddm = $j('#zmMontageLayout'); + layout = ddm.val(); if ( layout_id = parseInt(layout) ) { layout = layouts[layout]; @@ -231,6 +232,7 @@ function selectLayout(element) { if ( layout.Positions['default'] ) { styles = layout.Positions['default']; for ( style in styles ) { + console.log("Applying " + style + ' ' + styles[style]); monitor_frame.css(style, styles[style]); } } else { @@ -241,7 +243,6 @@ function selectLayout(element) { styles = layout.Positions['mId'+monitor.id]; for ( style in styles ) { monitor_frame.css(style, styles[style]); - console.log("Applying " + style + ' : ' + styles[style]); } } else { console.log("No Monitor styles to apply"); From ce15e1c921c8d949be9fd24fec5c988577de353c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 19 May 2020 16:57:45 -0400 Subject: [PATCH 10/47] remove debug, don't pass element through event handler anymore on layout ddm --- web/skins/classic/views/montage.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index e04ce8f68..5a20ca18c 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -75,10 +75,8 @@ zm_session_start(); $layout_id = ''; if ( isset($_COOKIE['zmMontageLayout']) ) { $layout_id = $_SESSION['zmMontageLayout'] = $_COOKIE['zmMontageLayout']; - ZM\Logger::Debug("Using layout $layout_id"); } elseif ( isset($_SESSION['zmMontageLayout']) ) { $layout_id = $_SESSION['zmMontageLayout']; - ZM\Logger::Debug("Using layout $layout_id from session"); } $options = array(); @@ -88,7 +86,7 @@ if ( $layout_id and is_numeric($layout_id) and isset($layoutsById[$layout_id]) ) $Layout = $layoutsById[$layout_id]; $Positions = json_decode($Layout->Positions(), true); } else { - ZM\Logger::Debug("Layout not found"); + ZM\Logger::Debug('Layout not found'); } if ( $Layout and ( $Layout->Name() != 'Freeform' ) ) { // Use layout instead of other options @@ -185,7 +183,7 @@ if ( $showZones ) { - 'selectLayout')); ?> + 'selectLayout')); ?> From 2c347d051801b24dd633f54b379fbbfc61af39dd Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 20 May 2020 10:41:22 -0400 Subject: [PATCH 11/47] Bump version to 1.34.15 for release --- distros/redhat/zoneminder.spec | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index c3458074c..45cc54846 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.34.14 +Version: 1.34.15 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index 46a99f18d..f17b9ba4d 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.34.14 +1.34.15 From eb41e4fb393e723edfbf89b02c6aaf5572d2b8d9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 24 May 2020 11:07:52 -0400 Subject: [PATCH 12/47] Fix the array for the de-interlacing options --- web/skins/classic/views/monitor.php | 58 ++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 5ac52494e..795c60332 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -320,31 +320,31 @@ $orientations = array( ); $deinterlaceopts = array( - 'Disabled' => 0x00000000, - 'Four field motion adaptive - Soft' => 0x00001E04, /* 30 change */ - 'Four field motion adaptive - Medium' => 0x00001404, /* 20 change */ - 'Four field motion adaptive - Hard' => 0x00000A04, /* 10 change */ - 'Discard' => 0x00000001, - 'Linear' => 0x00000002, - 'Blend' => 0x00000003, - 'Blend (25%)' => 0x00000205 - ); + 0x00000000 => 'Disabled', + 0x00001E04 => 'Four field motion adaptive - Soft', /* 30 change */ + 0x00001404 => 'Four field motion adaptive - Medium', /* 20 change */ + 0x00000A04 => 'Four field motion adaptive - Hard', /* 10 change */ + 0x00000001 => 'Discard', + 0x00000002 => 'Linear', + 0x00000003 => 'Blend', + 0x00000205 => 'Blend (25%)', +); $deinterlaceopts_v4l2 = array( - 'Disabled' => 0x00000000, - 'Four field motion adaptive - Soft' => 0x00001E04, /* 30 change */ - 'Four field motion adaptive - Medium' => 0x00001404, /* 20 change */ - 'Four field motion adaptive - Hard' => 0x00000A04, /* 10 change */ - 'Discard' => 0x00000001, - 'Linear' => 0x00000002, - 'Blend' => 0x00000003, - 'Blend (25%)' => 0x00000205, - 'V4L2: Capture top field only' => 0x02000000, - 'V4L2: Capture bottom field only' => 0x03000000, - 'V4L2: Alternate fields (Bob)' => 0x07000000, - 'V4L2: Progressive' => 0x01000000, - 'V4L2: Interlaced' => 0x04000000 - ); + 0x00000000 => 'Disabled', + 0x00001E04 => 'Four field motion adaptive - Soft', /* 30 change */ + 0x00001404 => 'Four field motion adaptive - Medium', /* 20 change */ + 0x00000A04 => 'Four field motion adaptive - Hard', /* 10 change */ + 0x00000001 => 'Discard', + 0x00000002 => 'Linear', + 0x00000003 => 'Blend', + 0x00000205 => 'Blend (25%)', + 0x02000000 => 'V4L2: Capture top field only', + 0x03000000 => 'V4L2: Capture bottom field only', + 0x07000000 => 'V4L2: Alternate fields (Bob)', + 0x01000000 => 'V4L2: Progressive', + 0x04000000 => 'V4L2: Interlaced', +); $fastblendopts = array( 'No blending' => 0, @@ -729,8 +729,8 @@ switch ( $tab ) { continue; if ( $optCount && ($optCount%$breakCount == 0) ) echo '
'; - echo 'Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"' : ''). '/> '. $optTrigger; + echo 'Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"' : ''). '/> '. $optTrigger; $optCount ++; } # end foreach trigger option if ( !$optCount ) { @@ -787,7 +787,7 @@ include('_monitor_source_nvsocket.php');
- - From 8121787ee6b4059446c8d83651ce3844b49b1e70 Mon Sep 17 00:00:00 2001 From: Romain ODDONE Date: Wed, 27 May 2020 10:08:08 +0200 Subject: [PATCH 13/47] make monitors table (console view) responsive --- web/skins/classic/views/console.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index 71c60b6f0..98420783b 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -205,6 +205,7 @@ getBodyTopHTML(); +
-Protocol() || $monitor->Protocol() == 'http' ) { echo htmlSelect('newMonitor[Method]', $httpMethods, $monitor->Method() ); } else { @@ -1052,7 +1052,7 @@ include('_monitor_source_nvsocket.php');
ControlId()); + ControlId()); if ( canEdit('Control') ) { echo ' '.makePopupLink('?view=controlcaps', 'zmControlCaps', 'controlcaps', translate('Edit')); } @@ -1080,7 +1080,7 @@ if ( canEdit('Control') ) {
translate('None'), '0' => translate('Home'), @@ -1182,7 +1182,7 @@ echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnL      - sync + sync
@@ -390,9 +391,10 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { - + -
+ + From 2637c887f8b1e4ab9503e3044b4083f872e1f9bc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 May 2020 11:44:34 -0400 Subject: [PATCH 14/47] Fix segfault when timestamp contains an unimplemented character. Fixes #2943 --- src/zm_image.cpp | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 31c71037f..02cdd1ad8 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -1905,11 +1905,11 @@ void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour /* RGB32 compatible: complete */ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int size, const Rgb fg_colour, const Rgb bg_colour ) { - strncpy( text, p_text, sizeof(text)-1 ); + strncpy(text, p_text, sizeof(text)-1); unsigned int index = 0; unsigned int line_no = 0; - unsigned int text_len = strlen( text ); + unsigned int text_len = strlen(text); unsigned int line_len = 0; const char *line = text; @@ -1928,10 +1928,10 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int const bool bg_trans = (bg_colour == RGB_TRANSPARENT); int zm_text_bitmask = 0x80; - if (size == 2) + if ( size == 2 ) zm_text_bitmask = 0x8000; - while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) { + while ( (index < text_len) && (line_len = strcspn(line, "\n")) ) { unsigned int line_width = line_len * ZM_CHAR_WIDTH * size; @@ -1967,10 +1967,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int unsigned char *temp_ptr = ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f; - if (size == 2) + if ( size == 2 ) { + if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; - else + } else { + if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; + } for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) { if ( f & (zm_text_bitmask >> i) ) { if ( !fg_trans ) @@ -1989,10 +1998,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int unsigned char *temp_ptr = ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f; - if (size == 2) + if ( size == 2 ) { + if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; - else + } else { + if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; + } for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr += colours ) { if ( f & (zm_text_bitmask >> i) ) { if ( !fg_trans ) { @@ -2016,10 +2034,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int Rgb* temp_ptr = (Rgb*)ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f; - if (size == 2) + if ( size == 2 ) { + if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; - else + } else { + if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; + } for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) { if ( f & (zm_text_bitmask >> i) ) { if ( !fg_trans ) { From bd287d7c7c6caac34a73679f9469e4e513d1b12a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 May 2020 11:44:34 -0400 Subject: [PATCH 15/47] Fix segfault when timestamp contains an unimplemented character. Fixes #2943 --- src/zm_image.cpp | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 31c71037f..02cdd1ad8 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -1905,11 +1905,11 @@ void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour /* RGB32 compatible: complete */ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int size, const Rgb fg_colour, const Rgb bg_colour ) { - strncpy( text, p_text, sizeof(text)-1 ); + strncpy(text, p_text, sizeof(text)-1); unsigned int index = 0; unsigned int line_no = 0; - unsigned int text_len = strlen( text ); + unsigned int text_len = strlen(text); unsigned int line_len = 0; const char *line = text; @@ -1928,10 +1928,10 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int const bool bg_trans = (bg_colour == RGB_TRANSPARENT); int zm_text_bitmask = 0x80; - if (size == 2) + if ( size == 2 ) zm_text_bitmask = 0x8000; - while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) { + while ( (index < text_len) && (line_len = strcspn(line, "\n")) ) { unsigned int line_width = line_len * ZM_CHAR_WIDTH * size; @@ -1967,10 +1967,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int unsigned char *temp_ptr = ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f; - if (size == 2) + if ( size == 2 ) { + if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; - else + } else { + if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; + } for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) { if ( f & (zm_text_bitmask >> i) ) { if ( !fg_trans ) @@ -1989,10 +1998,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int unsigned char *temp_ptr = ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f; - if (size == 2) + if ( size == 2 ) { + if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; - else + } else { + if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; + } for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr += colours ) { if ( f & (zm_text_bitmask >> i) ) { if ( !fg_trans ) { @@ -2016,10 +2034,19 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int Rgb* temp_ptr = (Rgb*)ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f; - if (size == 2) + if ( size == 2 ) { + if ( (line[c] * ZM_CHAR_HEIGHT * size) + r > sizeof(bigfontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = bigfontdata[(line[c] * ZM_CHAR_HEIGHT * size) + r]; - else + } else { + if ( (line[c] * ZM_CHAR_HEIGHT) + r > sizeof(fontdata) ) { + Warning("Unsupported character %c in %s", line[c], line); + continue; + } f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; + } for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) { if ( f & (zm_text_bitmask >> i) ) { if ( !fg_trans ) { From 58a015e20ac3d3680935e23cd7a3415c9690e192 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 9 May 2020 11:49:02 -0400 Subject: [PATCH 16/47] fix use of printMsg --- scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm index 4f14e787a..2b2cf3fd2 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm @@ -69,9 +69,9 @@ sub sendCmd { my $result = undef; - printMsg($cmd, 'Tx'); + $self->printMsg($cmd, 'Tx'); - my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" ); + my $req = HTTP::Request->new(POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi"); $req->content($cmd); my $res = $self->{ua}->request($req); From 20870e22fa37fb916fecdba603da50bed050f836 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 May 2020 13:28:33 -0400 Subject: [PATCH 17/47] fix eslint errors --- web/skins/classic/views/js/event.js | 3 +-- web/skins/classic/views/js/filter.js | 4 ++-- web/skins/classic/views/js/timeline.js | 4 +++- web/skins/classic/views/js/watch.js | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 69b34d055..93da7e9f2 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -308,8 +308,7 @@ function pauseClicked() { streamPause(); } -function streamPause( ) { - +function streamPause() { $j('#modeValue').html('Paused'); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('playBtn'), 'inactive' ); diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js index 5b01d150f..10064d6ef 100644 --- a/web/skins/classic/views/js/filter.js +++ b/web/skins/classic/views/js/filter.js @@ -143,8 +143,8 @@ function deleteFilter( element ) { } var escape = document.createElement('textarea'); function escapeHTML(html) { - escape.textContent = html; - return escape.innerHTML; + escape.textContent = html; + return escape.innerHTML; } function parseRows(rows) { diff --git a/web/skins/classic/views/js/timeline.js b/web/skins/classic/views/js/timeline.js index 0b4d1d5fc..8742ac67b 100644 --- a/web/skins/classic/views/js/timeline.js +++ b/web/skins/classic/views/js/timeline.js @@ -208,7 +208,9 @@ window.addEventListener('DOMContentLoaded', function() { el.onmouseover = window[el.getAttribute('data-on-mouseover-this')].bind(el, el); }); document.querySelectorAll('div.zoom').forEach(function(el) { - el.onclick = function(ev) { window[el.getAttribute('data-on-click')](ev); }; + el.onclick = function(ev) { + window[el.getAttribute('data-on-click')](ev); + }; }); }); diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 0056f0a08..19f74333b 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -635,7 +635,7 @@ function getEventCmdResponse( respObj, respText ) { row.removeClass('recent'); } row.addClass('updated'); - } // end foreach event + } // end foreach event var rows = $(eventListBody).getElements('tr'); for ( var i = 0; i < rows.length; i++ ) { @@ -651,7 +651,7 @@ function getEventCmdResponse( respObj, respText ) { } } else { checkStreamForErrors('getEventCmdResponse', respObj); - } // end if objresult == ok + } // end if objresult == ok var eventCmdTimeout = eventsRefreshTimeout; if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { From a7ef8e3dc8766b9aecdfa591283ec0244e9f3d7a Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 29 May 2020 14:53:42 -0400 Subject: [PATCH 18/47] detaint command before performing backup. Fixes #2945 --- scripts/zmupdate.pl.in | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 3662b6655..297648a8b 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -384,21 +384,22 @@ if ( $version ) { my $command = 'mysqldump'; if ( defined($portOrSocket) ) { if ( $portOrSocket =~ /^\// ) { - $command .= " -S".$portOrSocket; + $command .= ' -S'.$portOrSocket; } else { - $command .= " -h".$host." -P".$portOrSocket; + $command .= ' -h'.$host.' -P'.$portOrSocket; } } else { - $command .= " -h".$host; + $command .= ' -h'.$host; } if ( $dbUser ) { $command .= ' -u'.$dbUser; $command .= ' -p"'.$dbPass.'"' if $dbPass; } - my $backup = "@ZM_TMPDIR@/".$Config{ZM_DB_NAME}."-".$version.".dump"; - $command .= " --add-drop-table --databases ".$Config{ZM_DB_NAME}." > ".$backup; - print( "Creating backup to $backup. This may take several minutes.\n" ); - print( "Executing '$command'\n" ) if ( logDebugging() ); + my $backup = '@ZM_TMPDIR@/'.$Config{ZM_DB_NAME}.'-'.$version.'.dump'; + $command .= ' --add-drop-table --databases '.$Config{ZM_DB_NAME}.' > '.$backup; + print("Creating backup to $backup. This may take several minutes.\n"); + ($command) = $command =~ /(.*)/; # detaint + print("Executing '$command'\n") if logDebugging(); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { From 0e6ff1ad09abd9f4ffd0472880f8063de5ee882c Mon Sep 17 00:00:00 2001 From: Kevin Stolp Date: Mon, 1 Jun 2020 22:54:29 -0700 Subject: [PATCH 19/47] Detaint mysql commands in update script --- scripts/zmupdate.pl.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 3662b6655..adbfde8a3 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -399,6 +399,7 @@ if ( $version ) { $command .= " --add-drop-table --databases ".$Config{ZM_DB_NAME}." > ".$backup; print( "Creating backup to $backup. This may take several minutes.\n" ); print( "Executing '$command'\n" ) if ( logDebugging() ); + ($command) = $command =~ /(.*)/; # detaint my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { @@ -993,6 +994,7 @@ sub patchDB { $command .= '/zm_update-'.$version.'.sql'; print("Executing '$command'\n") if logDebugging(); + ($command) = $command =~ /(.*)/; # detaint my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { From a9c52a21ec6d5b421c96e8eacd3cc9c619e7a3d8 Mon Sep 17 00:00:00 2001 From: Romain ODDONE Date: Tue, 2 Jun 2020 17:25:07 +0200 Subject: [PATCH 20/47] call to changeScale() on page init when "scale to fit" is selected --- web/skins/classic/views/js/watch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 8a38afea4..bb6f535d8 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -849,7 +849,7 @@ function initPage() { if ( refreshApplet && appletRefreshTime ) { appletRefresh.delay(appletRefreshTime*1000); } - if ( scale == 'auto' ) changeScale(); + if ( scale == '0' || scale == 'auto' ) changeScale(); if ( window.history.length == 1 ) { $j('#closeControl').html(''); } From 6f42cb22d665b5cd4be785672fbf0b7fc5843ab8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 3 Jun 2020 15:36:47 -0400 Subject: [PATCH 21/47] Use single quotes when escaping password so that $ has no effect. Also escape any single quotes in the password. --- scripts/zmupdate.pl.in | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 3662b6655..69e277a19 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -121,6 +121,8 @@ GetOptions( my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } ); $Config{ZM_DB_USER} = $dbUser; $Config{ZM_DB_PASS} = $dbPass; +# we escape dbpass with single quotes so that $ in the password has no effect, but dbpass could have a ' in it. +$dbPass =~ s/'/\\'/g; if ( ! ($check || $freshen || $rename || $zoneFix || $migrateEvents || $version) ) { if ( $Config{ZM_DYN_DB_VERSION} ) { @@ -384,21 +386,21 @@ if ( $version ) { my $command = 'mysqldump'; if ( defined($portOrSocket) ) { if ( $portOrSocket =~ /^\// ) { - $command .= " -S".$portOrSocket; + $command .= ' -S'.$portOrSocket; } else { - $command .= " -h".$host." -P".$portOrSocket; + $command .= ' -h'.$host.' -P'.$portOrSocket; } } else { - $command .= " -h".$host; + $command .= ' -h'.$host; } if ( $dbUser ) { $command .= ' -u'.$dbUser; - $command .= ' -p"'.$dbPass.'"' if $dbPass; + $command .= ' -p\''.$dbPass.'\'' if $dbPass; } - my $backup = "@ZM_TMPDIR@/".$Config{ZM_DB_NAME}."-".$version.".dump"; - $command .= " --add-drop-table --databases ".$Config{ZM_DB_NAME}." > ".$backup; - print( "Creating backup to $backup. This may take several minutes.\n" ); - print( "Executing '$command'\n" ) if ( logDebugging() ); + my $backup = '@ZM_TMPDIR@/'.$Config{ZM_DB_NAME}.'-'.$version.'.dump'; + $command .= ' --add-drop-table --databases '.$Config{ZM_DB_NAME}.' > '.$backup; + print("Creating backup to $backup. This may take several minutes.\n"); + print("Executing '$command'\n") if logDebugging(); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { @@ -982,7 +984,7 @@ sub patchDB { } if ( $dbUser ) { $command .= ' -u'.$dbUser; - $command .= ' -p"'.$dbPass.'"' if $dbPass; + $command .= ' -p\''.$dbPass.'\'' if $dbPass; } $command .= ' '.$Config{ZM_DB_NAME}.' < '; if ( $updateDir ) { From 0644358d0a7c0ac41c55e36eb7804cf2aa9c4bee Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 3 Jun 2020 17:41:03 -0400 Subject: [PATCH 22/47] in getDiskPercent we have calls to Error without the ZM namespace. Fixes #2954 --- web/includes/functions.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index f640569c3..d5c98cb3f 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -1499,15 +1499,15 @@ function getLoad() { function getDiskPercent($path = ZM_DIR_EVENTS) { $total = disk_total_space($path); if ( $total === false ) { - Error('disk_total_space returned false. Verify the web account user has access to ' . $path); + ZM\Error('disk_total_space returned false. Verify the web account user has access to ' . $path); return 0; } elseif ( $total == 0 ) { - Error('disk_total_space indicates the following path has a filesystem size of zero bytes ' . $path); + ZM\Error('disk_total_space indicates the following path has a filesystem size of zero bytes ' . $path); return 100; } $free = disk_free_space($path); if ( $free === false ) { - Error('disk_free_space returned false. Verify the web account user has access to ' . $path); + ZM\Error('disk_free_space returned false. Verify the web account user has access to ' . $path); } $space = round((($total - $free) / $total) * 100); return $space; @@ -2063,7 +2063,7 @@ function logState() { if ( $count['Level'] <= ZM\Logger::PANIC ) $count['Level'] = ZM\Logger::FATAL; if ( !($levelCount = $levelCounts[$count['Level']]) ) { - Error('Unexpected Log level '.$count['Level']); + ZM\Error('Unexpected Log level '.$count['Level']); next; } if ( $levelCount[1] && $count['LevelCount'] >= $levelCount[1] ) { From 6869710747ebfd2fda72a51452fc92d5b64aa0c6 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jun 2020 11:28:26 -0400 Subject: [PATCH 23/47] Handle no db_host specified, meaning local unix socket --- scripts/zmupdate.pl.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index beb3cf8a0..c1d7c32b7 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -972,7 +972,7 @@ sub patchDB { my $dbh = shift; my $version = shift; - my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); + my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ) if $Config{ZM_DB_HOST}; my $command = 'mysql'; if ( defined($portOrSocket) ) { if ( $portOrSocket =~ /^\// ) { @@ -980,7 +980,7 @@ sub patchDB { } else { $command .= ' -h'.$host.' -P'.$portOrSocket; } - } else { + } elsif ( $host ) { $command .= ' -h'.$host; } if ( $dbUser ) { From c4b2aa4a8532dc766c2505addbcf40f2cba1a56f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jun 2020 11:34:16 -0400 Subject: [PATCH 24/47] Handle no DB_HOST specifed so default to localhost --- web/includes/database.php | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/web/includes/database.php b/web/includes/database.php index d0124656d..2fb2db108 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -25,20 +25,26 @@ define('DB_LOG_DEBUG', 2); $GLOBALS['dbLogLevel'] = DB_LOG_OFF; $GLOBALS['dbConn'] = false; +require_once('logger.php'); function dbConnect() { global $dbConn; - if ( strpos(ZM_DB_HOST, ':') ) { - // Host variable may carry a port or socket. - list($host, $portOrSocket) = explode(':', ZM_DB_HOST, 2); - if ( ctype_digit($portOrSocket) ) { - $socket = ':host='.$host . ';port='.$portOrSocket; + $socket = ''; + if ( ZM_DB_HOST ) { + if ( strpos(ZM_DB_HOST, ':') ) { + // Host variable may carry a port or socket. + list($host, $portOrSocket) = explode(':', ZM_DB_HOST, 2); + if ( ctype_digit($portOrSocket) ) { + $socket = ':host='.$host . ';port='.$portOrSocket.';'; + } else { + $socket = ':unix_socket='.$portOrSocket.';'; + } } else { - $socket = ':unix_socket='.$portOrSocket; + $socket = ':host='.ZM_DB_HOST.';'; } } else { - $socket = ':host='.ZM_DB_HOST; + $socket = ':host=localhost;'; } try { @@ -49,9 +55,9 @@ function dbConnect() { PDO::MYSQL_ATTR_SSL_KEY => ZM_DB_SSL_CLIENT_KEY, PDO::MYSQL_ATTR_SSL_CERT => ZM_DB_SSL_CLIENT_CERT, ); - $dbConn = new PDO(ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions); + $dbConn = new PDO(ZM_DB_TYPE.$socket.'dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions); } else { - $dbConn = new PDO(ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS); + $dbConn = new PDO(ZM_DB_TYPE.$socket.'dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS); } $dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); @@ -61,9 +67,12 @@ function dbConnect() { error_log('Unable to connect to ZM DB ' . $ex->getMessage()); $dbConn = null; } -} + return $dbConn; +} // end function dbConnect -dbConnect(); +if ( !dbConnect() ) { + ZM\Fatal("Failed db connection to $socket"); +} function dbDisconnect() { global $dbConn; From ef3fc758cbc7267b149ce50a07af8352fa1c07b2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 3 Jun 2020 17:41:03 -0400 Subject: [PATCH 25/47] in getDiskPercent we have calls to Error without the ZM namespace. Fixes #2954 --- web/includes/functions.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/includes/functions.php b/web/includes/functions.php index c31347a6f..3904534a2 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -1484,15 +1484,15 @@ function getLoad() { function getDiskPercent($path = ZM_DIR_EVENTS) { $total = disk_total_space($path); if ( $total === false ) { - Error('disk_total_space returned false. Verify the web account user has access to ' . $path); + ZM\Error('disk_total_space returned false. Verify the web account user has access to ' . $path); return 0; } elseif ( $total == 0 ) { - Error('disk_total_space indicates the following path has a filesystem size of zero bytes ' . $path); + ZM\Error('disk_total_space indicates the following path has a filesystem size of zero bytes ' . $path); return 100; } $free = disk_free_space($path); if ( $free === false ) { - Error('disk_free_space returned false. Verify the web account user has access to ' . $path); + ZM\Error('disk_free_space returned false. Verify the web account user has access to ' . $path); } $space = round((($total - $free) / $total) * 100); return $space; @@ -2048,7 +2048,7 @@ function logState() { if ( $count['Level'] <= ZM\Logger::PANIC ) $count['Level'] = ZM\Logger::FATAL; if ( !($levelCount = $levelCounts[$count['Level']]) ) { - Error('Unexpected Log level '.$count['Level']); + ZM\Error('Unexpected Log level '.$count['Level']); next; } if ( $levelCount[1] && $count['LevelCount'] >= $levelCount[1] ) { From f8a11633bb3bf0b72df89853b5b135dca17d06f8 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jun 2020 11:28:26 -0400 Subject: [PATCH 26/47] Handle no db_host specified, meaning local unix socket --- scripts/zmupdate.pl.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 297648a8b..b6d104e01 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -970,7 +970,7 @@ sub patchDB { my $dbh = shift; my $version = shift; - my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); + my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ) if $Config{ZM_DB_HOST}; my $command = 'mysql'; if ( defined($portOrSocket) ) { if ( $portOrSocket =~ /^\// ) { @@ -978,7 +978,7 @@ sub patchDB { } else { $command .= ' -h'.$host.' -P'.$portOrSocket; } - } else { + } elsif ( $host ) { $command .= ' -h'.$host; } if ( $dbUser ) { From 8802447b104522ff891f1a166601d609f22fb603 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jun 2020 11:34:16 -0400 Subject: [PATCH 27/47] Handle no DB_HOST specifed so default to localhost --- web/includes/database.php | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/web/includes/database.php b/web/includes/database.php index d0124656d..2fb2db108 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -25,20 +25,26 @@ define('DB_LOG_DEBUG', 2); $GLOBALS['dbLogLevel'] = DB_LOG_OFF; $GLOBALS['dbConn'] = false; +require_once('logger.php'); function dbConnect() { global $dbConn; - if ( strpos(ZM_DB_HOST, ':') ) { - // Host variable may carry a port or socket. - list($host, $portOrSocket) = explode(':', ZM_DB_HOST, 2); - if ( ctype_digit($portOrSocket) ) { - $socket = ':host='.$host . ';port='.$portOrSocket; + $socket = ''; + if ( ZM_DB_HOST ) { + if ( strpos(ZM_DB_HOST, ':') ) { + // Host variable may carry a port or socket. + list($host, $portOrSocket) = explode(':', ZM_DB_HOST, 2); + if ( ctype_digit($portOrSocket) ) { + $socket = ':host='.$host . ';port='.$portOrSocket.';'; + } else { + $socket = ':unix_socket='.$portOrSocket.';'; + } } else { - $socket = ':unix_socket='.$portOrSocket; + $socket = ':host='.ZM_DB_HOST.';'; } } else { - $socket = ':host='.ZM_DB_HOST; + $socket = ':host=localhost;'; } try { @@ -49,9 +55,9 @@ function dbConnect() { PDO::MYSQL_ATTR_SSL_KEY => ZM_DB_SSL_CLIENT_KEY, PDO::MYSQL_ATTR_SSL_CERT => ZM_DB_SSL_CLIENT_CERT, ); - $dbConn = new PDO(ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions); + $dbConn = new PDO(ZM_DB_TYPE.$socket.'dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions); } else { - $dbConn = new PDO(ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS); + $dbConn = new PDO(ZM_DB_TYPE.$socket.'dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS); } $dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); @@ -61,9 +67,12 @@ function dbConnect() { error_log('Unable to connect to ZM DB ' . $ex->getMessage()); $dbConn = null; } -} + return $dbConn; +} // end function dbConnect -dbConnect(); +if ( !dbConnect() ) { + ZM\Fatal("Failed db connection to $socket"); +} function dbDisconnect() { global $dbConn; From 20e6a1f53d48c5e5b62d4000f4126288b59a736c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 3 Jun 2020 15:36:47 -0400 Subject: [PATCH 28/47] Use single quotes when escaping password so that $ has no effect. Also escape any single quotes in the password. --- scripts/zmupdate.pl.in | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index b6d104e01..925e58006 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -121,6 +121,8 @@ GetOptions( my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } ); $Config{ZM_DB_USER} = $dbUser; $Config{ZM_DB_PASS} = $dbPass; +# we escape dbpass with single quotes so that $ in the password has no effect, but dbpass could have a ' in it. +$dbPass =~ s/'/\\'/g; if ( ! ($check || $freshen || $rename || $zoneFix || $migrateEvents || $version) ) { if ( $Config{ZM_DYN_DB_VERSION} ) { @@ -393,7 +395,7 @@ if ( $version ) { } if ( $dbUser ) { $command .= ' -u'.$dbUser; - $command .= ' -p"'.$dbPass.'"' if $dbPass; + $command .= ' -p\''.$dbPass.'\'' if $dbPass; } my $backup = '@ZM_TMPDIR@/'.$Config{ZM_DB_NAME}.'-'.$version.'.dump'; $command .= ' --add-drop-table --databases '.$Config{ZM_DB_NAME}.' > '.$backup; @@ -983,7 +985,7 @@ sub patchDB { } if ( $dbUser ) { $command .= ' -u'.$dbUser; - $command .= ' -p"'.$dbPass.'"' if $dbPass; + $command .= ' -p\''.$dbPass.'\'' if $dbPass; } $command .= ' '.$Config{ZM_DB_NAME}.' < '; if ( $updateDir ) { From 910d4b51c934b9f23910cf3d024750111818cb1f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 4 Jun 2020 11:44:59 -0400 Subject: [PATCH 29/47] Escape table names for mysql-8 --- web/includes/Group.php | 10 +++++----- web/includes/Object.php | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/web/includes/Group.php b/web/includes/Group.php index 3a34e0af4..f5a78f875 100644 --- a/web/includes/Group.php +++ b/web/includes/Group.php @@ -19,9 +19,9 @@ class Group extends ZM_Object { public function delete() { if ( property_exists($this, 'Id') ) { - dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($this->{'Id'})); - dbQuery('UPDATE Groups SET ParentId=NULL WHERE ParentId=?', array($this->{'Id'})); - dbQuery('DELETE FROM Groups WHERE Id=?', array($this->{'Id'})); + dbQuery('DELETE FROM `Groups_Monitors` WHERE `GroupId`=?', array($this->{'Id'})); + dbQuery('UPDATE `Groups` SET `ParentId`=NULL WHERE `ParentId`=?', array($this->{'Id'})); + dbQuery('DELETE FROM `Groups` WHERE Id=?', array($this->{'Id'})); if ( isset($_COOKIE['zmGroup']) ) { if ( $this->{'Id'} == $_COOKIE['zmGroup'] ) { unset($_COOKIE['zmGroup']); @@ -47,7 +47,7 @@ class Group extends ZM_Object { public function MonitorIds( ) { if ( ! property_exists($this, 'MonitorIds') ) { - $this->{'MonitorIds'} = dbFetchAll('SELECT MonitorId FROM Groups_Monitors WHERE GroupId=?', 'MonitorId', array($this->{'Id'})); + $this->{'MonitorIds'} = dbFetchAll('SELECT `MonitorId` FROM `Groups_Monitors` WHERE `GroupId`=?', 'MonitorId', array($this->{'Id'})); } return $this->{'MonitorIds'}; } @@ -65,7 +65,7 @@ class Group extends ZM_Object { session_write_close(); } - return htmlSelect( 'GroupId[]', Group::get_dropdown_options(), isset($_SESSION['GroupId'])?$_SESSION['GroupId']:null, array( + return htmlSelect('GroupId[]', Group::get_dropdown_options(), isset($_SESSION['GroupId'])?$_SESSION['GroupId']:null, array( 'data-on-change' => 'submitThisForm', 'class'=>'chosen', 'multiple'=>'multiple', diff --git a/web/includes/Object.php b/web/includes/Object.php index 09be86a9e..1fd704ec9 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -306,7 +306,7 @@ class ZM_Object { $fields = array_keys($fields); if ( $this->Id() ) { - $sql = 'UPDATE '.$table.' SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?'; + $sql = 'UPDATE `'.$table.'` SET '.implode(', ', array_map(function($field) {return '`'.$field.'`=?';}, $fields)).' WHERE Id=?'; $values = array_map(function($field){ return $this->{$field};}, $fields); $values[] = $this->{'Id'}; if ( dbQuery($sql, $values) ) @@ -314,8 +314,8 @@ class ZM_Object { } else { unset($fields['Id']); - $sql = 'INSERT INTO '.$table. - ' ('.implode(', ', array_map(function($field) {return '`'.$field.'`';}, $fields)). + $sql = 'INSERT INTO `'.$table. + '` ('.implode(', ', array_map(function($field) {return '`'.$field.'`';}, $fields)). ') VALUES ('. implode(', ', array_map(function($field){return '?';}, $fields)).')'; @@ -331,7 +331,7 @@ class ZM_Object { public function delete() { $class = get_class($this); $table = $class::$table; - dbQuery("DELETE FROM $table WHERE Id=?", array($this->{'Id'})); + dbQuery("DELETE FROM `$table` WHERE Id=?", array($this->{'Id'})); if ( isset($object_cache[$class]) and isset($object_cache[$class][$this->{'Id'}]) ) unset($object_cache[$class][$this->{'Id'}]); } From 402ab9ad9f92e29e879e07d95a92a6d070c88e1b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 5 Jun 2020 16:38:20 -0400 Subject: [PATCH 30/47] Bump version to 1.34.16 for release --- distros/redhat/zoneminder.spec | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 45cc54846..4038b9b55 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.34.15 +Version: 1.34.16 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index f17b9ba4d..4229129fb 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.34.15 +1.34.16 From caa48e7b800f503641b44a656fcbbda9d6d35d92 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 5 Jun 2020 17:50:52 -0400 Subject: [PATCH 31/47] Finish canEdit function --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 020c67562..2ff51d704 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -894,8 +894,14 @@ sub canEdit { return 1; } if ( !$$ZoneMinder::user{MonitorIds} ) { - # User has no -} + # User has no monitor limitations + return 1; + } + if ( $$ZoneMinder::user{Events} eq 'Edit' ) { + return 1; + } + return 0; +} # end sub canEdit 1; __END__ From 65d7c9e51dde9bfdb7fc353519f0ac2b6ac61c79 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 5 Jun 2020 17:51:06 -0400 Subject: [PATCH 32/47] bump version 1.35.5 --- distros/redhat/zoneminder.spec | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 5776f3adc..78c7f6cb3 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.35.4 +Version: 1.35.5 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/version b/version index f508d5bf0..00bedd170 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.35.4 +1.35.5 From d0a24ae32be103e1d73aafd7933d2536bd5467f9 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 5 Jun 2020 18:01:20 -0400 Subject: [PATCH 33/47] Make all Filters operate on in-progress events --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index 21803d76a..95cad2982 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -324,14 +324,9 @@ sub Sql { } # end if terms if ( $self->{Sql} ) { - if ( $self->{AutoMessage} ) { # Include all events, including events that are still ongoing # and have no EndTime yet - $sql .= ' WHERE ( '.$self->{Sql}.' )'; - } else { -# Only include closed events (events with valid EndTime) - $sql .= ' WHERE (E.EndTime IS NOT NULL) AND ( '.$self->{Sql}.' )'; - } + $sql .= ' WHERE ( '.$self->{Sql}.' )'; } my @auto_terms; if ( $self->{AutoArchive} ) { From d59697b9b5f0543ad198460379a1d61b068d848e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 7 Jun 2020 11:57:08 -0400 Subject: [PATCH 34/47] fix event->self --- scripts/ZoneMinder/lib/ZoneMinder/Event.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index 2ff51d704..1629fa929 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -580,7 +580,7 @@ sub DiskSpace { sub CopyTo { my ( $self, $NewStorage ) = @_; - if ( !$event->canEdit() ) { + if ( !$self->canEdit() ) { Warning('No permission to copy event.'); return 'No permission to copy event.'; } From 58ae7bb3422ab92a0ec589ed53e94ceb2420e7c5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sun, 7 Jun 2020 11:57:20 -0400 Subject: [PATCH 35/47] die early if db connection fails --- scripts/zmupdate.pl.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 895e8bd64..619527128 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -119,6 +119,9 @@ GetOptions( ) or pod2usage(-exitstatus => -1); my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } ); +if ( !$dbh ) { + die "Unable to connect to db\n"; +} $Config{ZM_DB_USER} = $dbUser; $Config{ZM_DB_PASS} = $dbPass; # we escape dbpass with single quotes so that $ in the password has no effect, but dbpass could have a ' in it. From d4488ac4e195d4b32d79752f154d7a92ec5a0aaa Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 9 Jun 2020 12:18:29 -0400 Subject: [PATCH 36/47] spacing --- scripts/zmupdate.pl.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 619527128..ad2f0150f 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -329,8 +329,8 @@ if ( $interactive ) { # Now check for MyISAM Tables my @MyISAM_Tables; my $sql = "SELECT `table_name` FROM INFORMATION_SCHEMA.TABLES WHERE `table_schema`='zm' AND `engine` = 'MyISAM'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute() or die("Can't execute: ".$sth->errstr()); while( my $dbTable = $sth->fetchrow() ) { push @MyISAM_Tables, $dbTable; From 5e0f1a03c572b663a1df8c7a6e090c19cee2d956 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 9 Jun 2020 13:19:19 -0400 Subject: [PATCH 37/47] Detect if system is running and prompt to stop it before doing the update. --- scripts/ZoneMinder/lib/ZoneMinder/General.pm | 33 ++++ scripts/zmupdate.pl.in | 183 +++++++++++-------- 2 files changed, 138 insertions(+), 78 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/General.pm b/scripts/ZoneMinder/lib/ZoneMinder/General.pm index 5b94dea20..6437ebbdf 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/General.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/General.pm @@ -28,6 +28,7 @@ our %EXPORT_TAGS = ( makePath jsonEncode jsonDecode + status ) ] ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; @@ -531,6 +532,38 @@ sub jsonDecode { return $result; } +sub packageControl { + my $command = shift; + my $string = $Config{ZM_PATH_BIN}.'/zmpkg.pl '.$command; + $string .= ' 2>/dev/null >&- <&- >/dev/null'; + executeShellCommand($string); +} + +sub daemonControl { + my ($command, $daemon, $args) = @_; + my $string = $Config{ZM_PATH_BIN}.'/zmdc.pl '.$command; + if ( $daemon ) { + $string .= ' ' . $daemon; + if ( $args ) { + $string .= ' ' . $args; + } + } + #$string .= ' 2>/dev/null >&- <&- >/dev/null'; + executeShellCommand($string); +} + +sub systemStatus { + my $command = shift; + my $output = qx($command); + my $status = $? >> 8; + if ( $status || logDebugging() ) { + $output = '' if !defined($output); + chomp($output); + Debug("Command: $command Output: $output"); + } + return $output; +} + 1; __END__ # Below is stub documentation for your module. You'd better edit it! diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index ad2f0150f..fc95e44d6 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -131,18 +131,18 @@ if ( ! ($check || $freshen || $rename || $zoneFix || $migrateEvents || $version) if ( $Config{ZM_DYN_DB_VERSION} ) { $version = $Config{ZM_DYN_DB_VERSION}; } else { - print( STDERR "Please give a valid option\n" ); + print(STDERR "Please give a valid option\n"); pod2usage(-exitstatus => -1); } } if ( ($check + $freshen + $rename + $zoneFix + $migrateEvents + ($version?1:0)) > 1 ) { - print( STDERR "Please give only one option\n" ); + print(STDERR "Please give only one option\n"); pod2usage(-exitstatus => -1); } if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) { - print( "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); + print('Update agent starting at '.strftime('%y/%m/%d %H:%M:%S', localtime() )."\n"); my $currVersion = $Config{ZM_DYN_CURR_VERSION}; my $lastVersion = $Config{ZM_DYN_LAST_VERSION}; @@ -152,23 +152,23 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) { $currVersion = $Config{ZM_VERSION}; my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( "$currVersion" ) or die( "Can't execute: ".$sth->errstr() ); + my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($currVersion) or die("Can't execute: ".$sth->errstr()); $sth->finish(); } - while( 1 ) { + while ( 1 ) { my $now = time(); if ( !$lastVersion || !$lastCheck || (($now-$lastCheck) > CHECK_INTERVAL) ) { - Info( "Checking for updates\n" ); + Info('Checking for updates'); use LWP::UserAgent; my $ua = LWP::UserAgent->new; - $ua->agent( "ZoneMinder Update Agent/".ZM_VERSION ); + $ua->agent('ZoneMinder Update Agent/'.ZM_VERSION); if ( $Config{ZM_UPDATE_CHECK_PROXY} ) { - $ua->proxy( 'http', $Config{ZM_UPDATE_CHECK_PROXY} ); + $ua->proxy('http', $Config{ZM_UPDATE_CHECK_PROXY}); } - my $req = HTTP::Request->new( GET=>'https://update.zoneminder.com/version.txt' ); + my $req = HTTP::Request->new(GET=>'https://update.zoneminder.com/version.txt'); my $res = $ua->request($req); if ( $res->is_success ) { @@ -176,63 +176,63 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) { chomp($lastVersion); $lastCheck = $now; - Info( "Got version: '".$lastVersion."'\n" ); + Info('Got version: '.$lastVersion); - my $lv_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_VERSION'"; - my $lv_sth = $dbh->prepare_cached( $lv_sql ) or die( "Can't prepare '$lv_sql': ".$dbh->errstr() ); - my $lv_res = $lv_sth->execute( $lastVersion ) or die( "Can't execute: ".$lv_sth->errstr() ); + my $lv_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_VERSION\''; + my $lv_sth = $dbh->prepare_cached($lv_sql) or die("Can't prepare '$lv_sql': ".$dbh->errstr()); + my $lv_res = $lv_sth->execute($lastVersion) or die("Can't execute: ".$lv_sth->errstr()); $lv_sth->finish(); - my $lc_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_CHECK'"; - my $lc_sth = $dbh->prepare_cached( $lc_sql ) or die( "Can't prepare '$lc_sql': ".$dbh->errstr() ); - my $lc_res = $lc_sth->execute( $lastCheck ) or die( "Can't execute: ".$lc_sth->errstr() ); + my $lc_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_CHECK\''; + my $lc_sth = $dbh->prepare_cached($lc_sql) or die("Can't prepare '$lc_sql': ".$dbh->errstr()); + my $lc_res = $lc_sth->execute($lastCheck) or die("Can't execute: ".$lc_sth->errstr()); $lc_sth->finish(); } else { - Error( "Error check failed: '".$res->status_line()."'\n" ); + Error('Error check failed: \''.$res->status_line().'\''); } } - sleep( 3600 ); + sleep(3600); } - print( "Update agent exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); + print('Update agent exiting at '.strftime('%y/%m/%d %H:%M:%S', localtime())."\n"); } if ( $rename ) { require File::Find; - chdir( EVENT_PATH ); + chdir(EVENT_PATH); sub renameImage { my $file = $_; # Ignore directories if ( -d $file ) { - print( "Checking directory '$file'\n" ); + print("Checking directory '$file'\n"); return; } if ( $file !~ /(capture|analyse)-(\d+)(\.jpg)/ ) { return; } - my $newFile = "$2-$1$3"; + my $newFile = $2.'-'.$1.$3; - print( "Renaming '$file' to '$newFile'\n" ); - rename( $file, $newFile ) or warn( "Can't rename '$file' to '$newFile'" ); + print("Renaming '$file' to '$newFile'\n"); + rename($file, $newFile) or warn("Can't rename '$file' to '$newFile'"); } - File::Find::find( \&renameImage, '.' ); -} -if ( $zoneFix ) { + File::Find::find(\&renameImage, '.'); +} # end if rename - my $sql = "select Z.*, M.Width as MonitorWidth, M.Height as MonitorHeight from Zones as Z inner join Monitors as M on Z.MonitorId = M.Id where Z.Units = 'Percent'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); +if ( $zoneFix ) { + my $sql = "SELECT Z.*, M.Width AS MonitorWidth, M.Height AS MonitorHeight FROM Zones AS Z INNER JOIN Monitors AS M ON Z.MonitorId = M.Id WHERE Z.Units = 'Percent'"; + my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute() or die("Can't execute: ".$sth->errstr()); my @zones; - while( my $zone = $sth->fetchrow_hashref() ) { - push( @zones, $zone ); + while ( my $zone = $sth->fetchrow_hashref() ) { + push(@zones, $zone); } $sth->finish(); - $sql = 'update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?'; - $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $sql = 'UPDATE Zones SET MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? WHERE Id = ?'; + $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); foreach my $zone ( @zones ) { my $zone_width = (($zone->{HiX}*$zone->{MonitorWidth})-($zone->{LoX}*$zone->{MonitorWidth}))/100; my $zone_height = (($zone->{HiY}*$zone->{MonitorHeight})-($zone->{LoY}*$zone->{MonitorHeight}))/100; @@ -246,78 +246,84 @@ if ( $zoneFix ) { ($zone->{MinBlobPixels}*$monitor_area)/$zone_area, ($zone->{MaxBlobPixels}*$monitor_area)/$zone_area, $zone->{Id} - ) or die( "Can't execute: ".$sth->errstr() ); + ) or die("Can't execute: ".$sth->errstr()); } $sth->finish(); -} +} # end if zoneFix + if ( $migrateEvents ) { - my $webUid = (getpwnam( $Config{ZM_WEB_USER} ))[2]; - my $webGid = (getgrnam( $Config{ZM_WEB_USER} ))[2]; + my $webUid = (getpwnam($Config{ZM_WEB_USER}))[2]; + my $webGid = (getgrnam($Config{ZM_WEB_USER}))[2]; if ( !(($> == 0) || ($> == $webUid)) ) { - print( "Error, migrating events can only be done as user root or ".$Config{ZM_WEB_USER}.".\n" ); - exit( -1 ); + print("Error, migrating events can only be done as user root or ".$Config{ZM_WEB_USER}.".\n"); + exit(-1); } # Run as web user/group $( = $webGid; - $) = $webGid; + $) = $webGid; $< = $webUid; $> = $webUid; - print( "\nAbout to convert saved events to deep storage, please ensure that ZoneMinder is fully stopped before proceeding.\nThis process is not easily reversible. Are you sure you wish to proceed?\n\nPress 'y' to continue or 'n' to abort : " ); + print(' +About to convert saved events to deep storage, please ensure that ZoneMinder is fully stopped before proceeding. +This process is not easily reversible. Are you sure you wish to proceed? + +Press \'y\' to continue or \'n\' to abort : '); my $response = ; - chomp( $response ); + chomp($response); while ( $response !~ /^[yYnN]$/ ) { - print( "Please press 'y' to continue or 'n' to abort only : " ); + print("Please press 'y' to continue or 'n' to abort only : "); $response = ; - chomp( $response ); + chomp($response); } if ( $response =~ /^[yY]$/ ) { - print( "Converting all events to deep storage.\n" ); + print("Converting all events to deep storage.\n"); - chdir( $Config{ZM_PATH_WEB} ); - my $sql = "select *, unix_timestamp(StartTime) as UnixStartTime from Events"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + chdir($Config{ZM_PATH_WEB}); + my $sql = 'SELECT *, unix_timestamp(StartTime) AS UnixStartTime FROM Events'; + my $sth = $dbh->prepare_cached($sql) or Fatal("Can't prepare '$sql': ".$dbh->errstr()); my $res = $sth->execute(); if ( !$res ) { - Fatal( "Can't fetch Events: ".$sth->errstr() ); + Fatal("Can't fetch Events: ".$sth->errstr()); } while( my $event = $sth->fetchrow_hashref() ) { my $oldEventPath = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.$event->{Id}; if ( !-d $oldEventPath ) { - print( "Warning, can't find old event path '$oldEventPath', already converted?\n" ); + print("Warning, can't find old event path '$oldEventPath', already converted?\n"); next; } - print( "Converting event ".$event->{Id}."\n" ); - my $newDatePath = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.strftime( "%y/%m/%d", localtime($event->{UnixStartTime}) ); - my $newTimePath = strftime( "%H/%M/%S", localtime($event->{UnixStartTime}) ); + print('Converting event '.$event->{Id}."\n"); + my $newDatePath = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.strftime('%y/%m/%d', localtime($event->{UnixStartTime})); + my $newTimePath = strftime('%H/%M/%S', localtime($event->{UnixStartTime})); my $newEventPath = $newDatePath.'/'.$newTimePath; ( my $truncEventPath = $newEventPath ) =~ s|/\d+$||; - makePath( $truncEventPath, $Config{ZM_PATH_WEB} ); + makePath($truncEventPath, $Config{ZM_PATH_WEB}); my $idLink = $newDatePath.'/.'.$event->{Id}; - symlink( $newTimePath, $idLink ) or die( "Can't symlink $newTimePath -> $idLink: $!" ); - rename( $oldEventPath, $newEventPath ) or die( "Can't move $oldEventPath -> $newEventPath: $!" ); + symlink($newTimePath, $idLink) or die("Can't symlink $newTimePath -> $idLink: $!"); + rename($oldEventPath, $newEventPath) or die("Can't move $oldEventPath -> $newEventPath: $!"); } $sth->finish(); - print( "Updating configuration.\n" ); + print("Updating configuration.\n"); $sql = "UPDATE `Config` SET `Value` = ? WHERE `Name` = 'ZM_USE_DEEP_STORAGE'"; - $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - $res = $sth->execute( 1 ) or die( "Can't execute: ".$sth->errstr() ); + $sth = $dbh->prepare_cached($sql) or die( "Can't prepare '$sql': ".$dbh->errstr()); + $res = $sth->execute(1) or die("Can't execute: ".$sth->errstr()); $sth->finish(); - print( "All events converted.\n\n" ); + print("All events converted.\n\n"); } else { - print( "Aborting event conversion.\n\n" ); + print("Aborting event conversion.\n\n"); } } + if ( $freshen ) { - print( "\nFreshening configuration in database\n" ); + print("\nFreshening configuration in database\n"); migratePaths(); migratePasswords(); ZoneMinder::Config::loadConfigFromDB(); @@ -338,17 +344,22 @@ if ( $interactive ) { $sth->finish(); if ( @MyISAM_Tables ) { - print( "\nPrevious versions of ZoneMinder used the MyISAM database engine.\nHowever, the recommended database engine is InnoDB.\n"); - print( "\nHint: InnoDB tables are much less likely to be corrupted during an unclean shutdown.\n\nPress 'y' to convert your tables to InnoDB or 'n' to skip : "); + print(' +Previous versions of ZoneMinder used the MyISAM database engine. +However, the recommended database engine is InnoDB. + +Hint: InnoDB tables are much less likely to be corrupted during an unclean shutdown. + +Press \'y\' to convert your tables to InnoDB or \'n\' to skip : '); my $response = ; chomp( $response ); if ( $response =~ /^[yY]$/ ) { $dbh->do(q|SET sql_mode='traditional'|); # Elevate warnings to errors - print "\nConverting MyISAM tables to InnoDB. Please wait.\n"; - foreach (@MyISAM_Tables) { + print "\nConverting MyISAM tables to InnoDB. Please wait.\n"; + foreach ( @MyISAM_Tables ) { my $sql = "ALTER TABLE `$_` ENGINE = InnoDB"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute() or die("Can't execute: ".$sth->errstr()); $sth->finish(); } $dbh->do(q|SET sql_mode=''|); # Set mode back to default @@ -365,23 +376,34 @@ if ( $version ) { exit(0); } - print( "\nInitiating database upgrade to version ".ZM_VERSION." from version $version\n" ); + my $start_zm = 0; + if ( (systemStatus() eq 'running') and $interactive ) { + print "\nZoneMinder system appears to be running. While not strictly required, it is advised to stop ZM during the update process. Would you like to stop ZM now? [Yn]:"; + my $response = ; + chomp($response); + if ( $response !~ /Yy/ ) { + packageControl('stop'); + $start_zm = 1; + } + } + + print("\nInitiating database upgrade to version ".ZM_VERSION." from version $version\n"); if ( $interactive ) { - if ( $Config{ZM_DYN_DB_VERSION} && $Config{ZM_DYN_DB_VERSION} ne $version ) { - print( "\nWARNING - You have specified an upgrade from version $version but the database version found is ".$Config{ZM_DYN_DB_VERSION}.". Is this correct?\nPress enter to continue or ctrl-C to abort : " ); + if ( $Config{ZM_DYN_DB_VERSION} && ($Config{ZM_DYN_DB_VERSION} ne $version) ) { + print("\nWARNING - You have specified an upgrade from version $version but the database version found is $Config{ZM_DYN_DB_VERSION}. Is this correct?\nPress enter to continue or ctrl-C to abort : "); my $response = ; } - print( "\nPlease ensure that ZoneMinder is stopped on your system prior to upgrading the database.\nPress enter to continue or ctrl-C to stop : " ); + print("\nPlease ensure that ZoneMinder is stopped on your system prior to upgrading the database.\nPress enter to continue or ctrl-C to stop : "); my $response = ; - print( "\nDo you wish to take a backup of your database prior to upgrading?\nThis may result in a large file in @ZM_TMPDIR@ if you have a lot of events.\nPress 'y' for a backup or 'n' to continue : " ); + print("\nDo you wish to take a backup of your database prior to upgrading?\nThis may result in a large file in @ZM_TMPDIR@ if you have a lot of events.\nPress 'y' for a backup or 'n' to continue : "); $response = ; - chomp( $response ); + chomp($response); while ( $response !~ /^[yYnN]$/ ) { - print( "Please press 'y' for a backup or 'n' to continue only : " ); + print("Please press 'y' for a backup or 'n' to continue only : "); $response = ; - chomp( $response ); + chomp($response); } if ( $response =~ /^[yY]$/ ) { @@ -939,7 +961,12 @@ if ( $version ) { #my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); #my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() ); #$sth->finish(); + print("\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n"); + if ( $start_zm ) { + print("Starting ZM since we stopped it for the update\n"); + packageControl('start'); + } } # end if version zmDbDisconnect(); From a2a28d389e2d1a7bb3f067bc0bf8b53bfbed984b Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 9 Jun 2020 13:35:52 -0400 Subject: [PATCH 38/47] Finish exporting and fixing new systemStatus packageControl and daemonControl functions --- scripts/ZoneMinder/lib/ZoneMinder/General.pm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/General.pm b/scripts/ZoneMinder/lib/ZoneMinder/General.pm index 6437ebbdf..cd6463add 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/General.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/General.pm @@ -28,7 +28,9 @@ our %EXPORT_TAGS = ( makePath jsonEncode jsonDecode - status + systemStatus + packageControl + daemonControl ) ] ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; @@ -553,7 +555,7 @@ sub daemonControl { } sub systemStatus { - my $command = shift; + my $command = $Config{ZM_PATH_BIN}.'/zmdc.pl check'; my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { @@ -575,7 +577,6 @@ ZoneMinder::General - Utility Functions for ZoneMinder =head1 SYNOPSIS use ZoneMinder::General; -blah blah blah =head1 DESCRIPTION @@ -594,6 +595,9 @@ of the ZoneMinder scripts makePath jsonEncode jsonDecode + packageControl + daemonControl + systemStatus ) ] From d984c33b926c58c078301835cab478bcca512999 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 9 Jun 2020 13:36:33 -0400 Subject: [PATCH 39/47] Replace old message abou ensuring that ZM is stopped with our new one, which only pauses if ZMIS running. --- scripts/zmupdate.pl.in | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index fc95e44d6..b89f6a8f8 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -377,16 +377,6 @@ if ( $version ) { } my $start_zm = 0; - if ( (systemStatus() eq 'running') and $interactive ) { - print "\nZoneMinder system appears to be running. While not strictly required, it is advised to stop ZM during the update process. Would you like to stop ZM now? [Yn]:"; - my $response = ; - chomp($response); - if ( $response !~ /Yy/ ) { - packageControl('stop'); - $start_zm = 1; - } - } - print("\nInitiating database upgrade to version ".ZM_VERSION." from version $version\n"); if ( $interactive ) { if ( $Config{ZM_DYN_DB_VERSION} && ($Config{ZM_DYN_DB_VERSION} ne $version) ) { @@ -394,11 +384,18 @@ if ( $version ) { my $response = ; } - print("\nPlease ensure that ZoneMinder is stopped on your system prior to upgrading the database.\nPress enter to continue or ctrl-C to stop : "); - my $response = ; + if ( systemStatus() eq 'running' ) { + print"\nZoneMinder system appears to be running. While not strictly required, it is advised to stop ZM during the update process. Would you like to stop ZM now? [Yn]:"; + my $response = ; + chomp($response); + if ( $response !~ /Yy/ ) { + packageControl('stop'); + $start_zm = 1; + } + } print("\nDo you wish to take a backup of your database prior to upgrading?\nThis may result in a large file in @ZM_TMPDIR@ if you have a lot of events.\nPress 'y' for a backup or 'n' to continue : "); - $response = ; + my $response = ; chomp($response); while ( $response !~ /^[yYnN]$/ ) { print("Please press 'y' for a backup or 'n' to continue only : "); From 1b9402239ac4963bd18ff294b22d0876ea7d0790 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 15 Jun 2020 14:59:33 -0400 Subject: [PATCH 40/47] Add libvncclient0 as an option for jessie --- distros/ubuntu1604/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distros/ubuntu1604/control b/distros/ubuntu1604/control index 00a8bd9ac..34af7cef1 100644 --- a/distros/ubuntu1604/control +++ b/distros/ubuntu1604/control @@ -83,7 +83,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libssl | libssl1.0.0 | libssl1.1 ,libcrypt-eksblowfish-perl ,libdata-entropy-perl - ,libvncclient1 + ,libvncclient1|libvncclient0 Recommends: ${misc:Recommends} ,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm ,mysql-server | mariadb-server | virtual-mysql-server From ae5116ff9c357674716ed4dd3836f3b0b4737052 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 15 Jun 2020 15:29:26 -0400 Subject: [PATCH 41/47] improve efficiency of outputting device query. The constant strlen's were terrible. --- src/zm_local_camera.cpp | 387 ++++++++++++++++++++++------------------ 1 file changed, 214 insertions(+), 173 deletions(-) diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index f06f9f958..7b746a009 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -1274,56 +1274,62 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { #define capString(test,prefix,yesString,noString,capability) \ (test) ? (prefix yesString " " capability "\n") : (prefix noString " " capability "\n") -bool LocalCamera::GetCurrentSettings( const char *device, char *output, int version, bool verbose ) { +bool LocalCamera::GetCurrentSettings(const char *device, char *output, int version, bool verbose) { output[0] = 0; + char *output_ptr = output; char queryDevice[PATH_MAX] = ""; int devIndex = 0; do { - if ( device ) + if ( device ) { strncpy(queryDevice, device, sizeof(queryDevice)-1); - else + } else { sprintf(queryDevice, "/dev/video%d", devIndex); + } if ( (vid_fd = open(queryDevice, O_RDWR)) <= 0 ) { if ( device ) { Error("Failed to open video device %s: %s", queryDevice, strerror(errno)); if ( verbose ) - sprintf(output+strlen(output), "Error, failed to open video device %s: %s\n", + output_ptr += sprintf(output_ptr, "Error, failed to open video device %s: %s\n", queryDevice, strerror(errno)); else - sprintf(output+strlen(output), "error%d\n", errno); + output_ptr += sprintf(output_ptr, "error%d\n", errno); return false; } else { return true; } } - if ( verbose ) - sprintf(output+strlen(output), "Video Device: %s\n", queryDevice); - else - sprintf(output+strlen(output), "d:%s|", queryDevice); + if ( verbose ) { + output_ptr += sprintf(output_ptr, "Video Device: %s\n", queryDevice); + } else { + output_ptr += sprintf(output_ptr, "d:%s|", queryDevice); + } #if ZM_HAS_V4L2 if ( version == 2 ) { struct v4l2_capability vid_cap; if ( vidioctl(vid_fd, VIDIOC_QUERYCAP, &vid_cap) < 0 ) { Error("Failed to query video device: %s", strerror(errno)); - if ( verbose ) - sprintf(output, "Error, failed to query video capabilities %s: %s\n", + if ( verbose ) { + output_ptr += sprintf(output_ptr, "Error, failed to query video capabilities %s: %s\n", queryDevice, strerror(errno)); - else - sprintf(output, "error%d\n", errno); - return false; + } else { + output_ptr += sprintf(output_ptr, "error%d\n", errno); + } + if ( device ) + return false; } if ( verbose ) { - sprintf(output+strlen(output), "General Capabilities\n"); - sprintf(output+strlen(output), " Driver: %s\n", vid_cap.driver); - sprintf(output+strlen(output), " Card: %s\n", vid_cap.card); - sprintf(output+strlen(output), " Bus: %s\n", vid_cap.bus_info); - sprintf(output+strlen(output), " Version: %u.%u.%u\n", - (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff); - sprintf(output+strlen(output), " Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + output_ptr += sprintf(output_ptr, "General Capabilities\n" + " Driver: %s\n" + " Card: %s\n" + " Bus: %s\n" + " Version: %u.%u.%u\n" + " Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + vid_cap.driver, vid_cap.card, vid_cap.bus_info, + (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff, vid_cap.capabilities, capString(vid_cap.capabilities&V4L2_CAP_VIDEO_CAPTURE, " ", "Supports", "Does not support", "video capture (X)"), capString(vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT, " ", "Supports", "Does not support", "video output"), @@ -1345,17 +1351,16 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers capString(vid_cap.capabilities&V4L2_CAP_STREAMING, " ", "Supports", "Does not support", "streaming i/o (X)") ); } else { - sprintf(output+strlen(output), "D:%s|", vid_cap.driver); - sprintf(output+strlen(output), "C:%s|", vid_cap.card); - sprintf(output+strlen(output), "B:%s|", vid_cap.bus_info); - sprintf(output+strlen(output), "V:%u.%u.%u|", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff); - sprintf(output+strlen(output), "T:0x%x|", vid_cap.capabilities); + output_ptr += sprintf(output_ptr, "D:%s|C:%s|B:%s|V:%u.%u.%u|T:0x%x|" + , vid_cap.driver + , vid_cap.card + , vid_cap.bus_info + , (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff + , vid_cap.capabilities); } - if ( verbose ) - sprintf(output+strlen(output), " Standards:\n"); - else - sprintf(output+strlen(output), "S:"); + output_ptr += sprintf(output_ptr, verbose ? " Standards:\n" : "S:"); + struct v4l2_standard standard; int standardIndex = 0; do { @@ -1370,25 +1375,20 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers } else { Error("Failed to enumerate standard %d: %d %s", standard.index, errno, strerror(errno)); if ( verbose ) - sprintf(output, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno)); + output_ptr += sprintf(output_ptr, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno)); else - sprintf(output, "error%d\n", errno); + output_ptr += sprintf(output_ptr, "error%d\n", errno); + // Why return? Why not continue trying other things? return false; } } - if ( verbose ) - sprintf(output+strlen(output), " %s\n", standard.name); - else - sprintf(output+strlen(output), "%s/", standard.name); - } - while ( standardIndex++ >= 0 ); - if ( !verbose && output[strlen(output)-1] == '/') - output[strlen(output)-1] = '|'; + output_ptr += sprintf(output_ptr, (verbose ? " %s\n" : "%s/"), standard.name); + } while ( standardIndex++ >= 0 ); - if ( verbose ) - sprintf(output+strlen(output), " Formats:\n"); - else - sprintf(output+strlen(output), "F:"); + if ( !verbose && (*(output_ptr-1) == '/') ) + *(output_ptr-1) = '|'; + + output_ptr += sprintf(output_ptr, verbose ? " Formats:\n" : "F:"); struct v4l2_fmtdesc format; int formatIndex = 0; do { @@ -1403,15 +1403,15 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers } else { Error("Failed to enumerate format %d: %s", format.index, strerror(errno)); if ( verbose ) - sprintf(output, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno)); + output_ptr += sprintf(output_ptr, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno)); else - sprintf(output, "error%d\n", errno); + output_ptr += sprintf(output_ptr, "error%d\n", errno); return false; } } if ( verbose ) - sprintf( - output+strlen(output), + output_ptr += sprintf( + output_ptr, " %s (0x%02hhx%02hhx%02hhx%02hhx)\n", format.description, (format.pixelformat>>24)&0xff, @@ -1419,33 +1419,34 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers (format.pixelformat>>8)&0xff, format.pixelformat&0xff); else - sprintf( - output+strlen(output), + output_ptr += sprintf( + output_ptr, "0x%02hhx%02hhx%02hhx%02hhx/", (format.pixelformat>>24)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat)&0xff); } while ( formatIndex++ >= 0 ); + if ( !verbose ) - output[strlen(output)-1] = '|'; + *(output_ptr-1) = '|'; else - sprintf(output+strlen(output), "Crop Capabilities\n"); + output_ptr += sprintf(output_ptr, "Crop Capabilities\n"); struct v4l2_cropcap cropcap; memset(&cropcap, 0, sizeof(cropcap)); cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if ( vidioctl( vid_fd, VIDIOC_CROPCAP, &cropcap ) < 0 ) { + if ( vidioctl(vid_fd, VIDIOC_CROPCAP, &cropcap) < 0 ) { if ( errno != EINVAL ) { /* Failed querying crop capability, write error to the log and continue as if crop is not supported */ Error("Failed to query crop capabilities: %s", strerror(errno)); } if ( verbose ) { - sprintf(output+strlen(output), " Cropping is not supported\n"); + output_ptr += sprintf(output_ptr, " Cropping is not supported\n"); } else { /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ - sprintf(output+strlen(output), "B:%dx%d|",0,0); + output_ptr += sprintf(output_ptr, "B:%dx%d|", 0, 0); } } else { struct v4l2_crop crop; @@ -1459,19 +1460,23 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers } if ( verbose ) { - sprintf(output+strlen(output), " Cropping is not supported\n"); + output_ptr += sprintf(output_ptr, " Cropping is not supported\n"); } else { /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ - sprintf(output+strlen(output), "B:%dx%d|",0,0); + output_ptr += sprintf(output_ptr, "B:%dx%d|",0,0); } } else { /* Cropping supported */ if ( verbose ) { - sprintf(output+strlen(output), " Bounds: %d x %d\n", cropcap.bounds.width, cropcap.bounds.height); - sprintf(output+strlen(output), " Default: %d x %d\n", cropcap.defrect.width, cropcap.defrect.height); - sprintf(output+strlen(output), " Current: %d x %d\n", crop.c.width, crop.c.height); + output_ptr += sprintf(output_ptr, + " Bounds: %d x %d\n" + " Default: %d x %d\n" + " Current: %d x %d\n" + , cropcap.bounds.width, cropcap.bounds.height + , cropcap.defrect.width, cropcap.defrect.height + , crop.c.width, crop.c.height); } else { - sprintf(output+strlen(output), "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height); + output_ptr += sprintf(output_ptr, "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height); } } } /* Crop code */ @@ -1488,17 +1493,14 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers } Error("Failed to enumerate input %d: %s", input.index, strerror(errno)); if ( verbose ) - sprintf(output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno)); + output_ptr += sprintf(output_ptr, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno)); else - sprintf(output, "error%d\n", errno); + output_ptr += sprintf(output_ptr, "error%d\n", errno); return false; } } while ( inputIndex++ >= 0 ); - if ( verbose ) - sprintf(output+strlen(output), "Inputs: %d\n", inputIndex); - else - sprintf(output+strlen(output), "I:%d|", inputIndex); + output_ptr += sprintf(output_ptr, verbose?"Inputs: %d\n":"I:%d|", inputIndex); inputIndex = 0; do { @@ -1512,65 +1514,84 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers } Error("Failed to enumerate input %d: %s", input.index, strerror(errno)); if ( verbose ) - sprintf(output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno)); + output_ptr += sprintf(output_ptr, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno)); else - sprintf(output, "error%d\n", errno); + output_ptr += sprintf(output_ptr, "error%d\n", errno); return false; } if ( vidioctl(vid_fd, VIDIOC_S_INPUT, &input.index) < 0 ) { Error("Failed to set video input %d: %s", input.index, strerror(errno)); if ( verbose ) - sprintf(output, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno)); + output_ptr += sprintf(output_ptr, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno)); else - sprintf(output, "error%d\n", errno); + output_ptr += sprintf(output_ptr, "error%d\n", errno); return false; } if ( verbose ) { - sprintf( output+strlen(output), " Input %d\n", input.index ); - sprintf( output+strlen(output), " Name: %s\n", input.name ); - sprintf( output+strlen(output), " Type: %s\n", input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); - sprintf( output+strlen(output), " Audioset: %08x\n", input.audioset ); - sprintf( output+strlen(output), " Standards: 0x%llx\n", input.std ); + output_ptr += sprintf( output, + " Input %d\n" + " Name: %s\n" + " Type: %s\n" + " Audioset: %08x\n" + " Standards: 0x%llx\n" + , input.index + , input.name + , input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") + , input.audioset + , input.std ); } else { - sprintf( output+strlen(output), "i%d:%s|", input.index, input.name ); - sprintf( output+strlen(output), "i%dT:%s|", input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); - sprintf( output+strlen(output), "i%dS:%llx|", input.index, input.std ); + output_ptr += sprintf( output_ptr, "i%d:%s|i%dT:%s|i%dS:%llx|" + , input.index, input.name + , input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") + , input.index, input.std); } if ( verbose ) { - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)" ) ); - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)" ) ); - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "" ) ); - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", "" ) ); + output_ptr += sprintf( output_ptr, " %s %s %s %s" + , capString(input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)") + , capString(input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)") + , capString(input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "") + , capString(input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", "")); } else { - sprintf( output+strlen(output), "i%dSP:%d|", input.index, (input.status&V4L2_IN_ST_NO_POWER)?0:1 ); - sprintf( output+strlen(output), "i%dSS:%d|", input.index, (input.status&V4L2_IN_ST_NO_SIGNAL)?0:1 ); - sprintf( output+strlen(output), "i%dSC:%d|", input.index, (input.status&V4L2_IN_ST_NO_COLOR)?0:1 ); - sprintf( output+strlen(output), "i%dHP:%d|", input.index, (input.status&V4L2_IN_ST_NO_H_LOCK)?0:1 ); + output_ptr += sprintf( output_ptr, "i%dSP:%d|i%dSS:%d|i%dSC:%d|i%dHP:%d|" + , input.index, (input.status&V4L2_IN_ST_NO_POWER)?0:1 + , input.index, (input.status&V4L2_IN_ST_NO_SIGNAL)?0:1 + , input.index, (input.status&V4L2_IN_ST_NO_COLOR)?0:1 + , input.index, (input.status&V4L2_IN_ST_NO_H_LOCK)?0:1 ); } } while ( inputIndex++ >= 0 ); if ( !verbose ) - output[strlen(output)-1] = '\n'; + *(output_ptr-1) = '\n'; } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( version == 1 ) { struct video_capability vid_cap; - memset( &vid_cap, 0, sizeof(video_capability) ); - if ( ioctl( vid_fd, VIDIOCGCAP, &vid_cap ) < 0 ) { - Error( "Failed to get video capabilities: %s", strerror(errno) ); + memset(&vid_cap, 0, sizeof(video_capability)); + if ( ioctl(vid_fd, VIDIOCGCAP, &vid_cap) < 0 ) { + Error("Failed to get video capabilities: %s", strerror(errno)); if ( verbose ) - sprintf( output, "Error, failed to get video capabilities %s: %s\n", queryDevice, strerror(errno) ); + output_ptr += sprintf(output_ptr, + "Error, failed to get video capabilities %s: %s\n", + queryDevice, strerror(errno)); else - sprintf( output, "error%d\n", errno ); - return( false ); + output_ptr += sprintf(output_ptr, "error%d\n", errno); + return false; } if ( verbose ) { - sprintf( output+strlen(output), "Video Capabilities\n" ); - sprintf( output+strlen(output), " Name: %s\n", vid_cap.name ); - sprintf( output+strlen(output), " Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.type, + output_ptr += sprintf( output_ptr, "Video Capabilities\n" + " Name: %s\n" + " Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s" + " Video Channels: %d\n" + " Audio Channels: %d\n" + " Maximum Width: %d\n" + " Maximum Height: %d\n" + " Minimum Width: %d\n" + " Minimum Height: %d\n", + vid_cap.name, + vid_cap.type, (vid_cap.type&VID_TYPE_CAPTURE)?" Can capture\n":"", (vid_cap.type&VID_TYPE_TUNER)?" Can tune\n":"", (vid_cap.type&VID_TYPE_TELETEXT)?" Does teletext\n":"", @@ -1584,63 +1605,72 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers (vid_cap.type&VID_TYPE_MPEG_DECODER)?" Can decode MPEG streams\n":"", (vid_cap.type&VID_TYPE_MPEG_ENCODER)?" Can encode MPEG streams\n":"", (vid_cap.type&VID_TYPE_MJPEG_DECODER)?" Can decode MJPEG streams\n":"", - (vid_cap.type&VID_TYPE_MJPEG_ENCODER)?" Can encode MJPEG streams\n":"" - ); - sprintf( output+strlen(output), " Video Channels: %d\n", vid_cap.channels ); - sprintf( output+strlen(output), " Audio Channels: %d\n", vid_cap.audios ); - sprintf( output+strlen(output), " Maximum Width: %d\n", vid_cap.maxwidth ); - sprintf( output+strlen(output), " Maximum Height: %d\n", vid_cap.maxheight ); - sprintf( output+strlen(output), " Minimum Width: %d\n", vid_cap.minwidth ); - sprintf( output+strlen(output), " Minimum Height: %d\n", vid_cap.minheight ); - } - else - { - sprintf( output+strlen(output), "N:%s|", vid_cap.name ); - sprintf( output+strlen(output), "T:%d|", vid_cap.type ); - sprintf( output+strlen(output), "nC:%d|", vid_cap.channels ); - sprintf( output+strlen(output), "nA:%d|", vid_cap.audios ); - sprintf( output+strlen(output), "mxW:%d|", vid_cap.maxwidth ); - sprintf( output+strlen(output), "mxH:%d|", vid_cap.maxheight ); - sprintf( output+strlen(output), "mnW:%d|", vid_cap.minwidth ); - sprintf( output+strlen(output), "mnH:%d|", vid_cap.minheight ); + (vid_cap.type&VID_TYPE_MJPEG_ENCODER)?" Can encode MJPEG streams\n":"", + vid_cap.channels, + vid_cap.audios, + vid_cap.maxwidth, + vid_cap.maxheight, + vid_cap.minwidth, + vid_cap.minheight ); + } else { + output_ptr += sprintf(output_ptr, "N:%s|T:%d|nC:%d|nA:%d|mxW:%d|mxH:%d|mnW:%d|mnH:%d|" + , vid_cap.name + , vid_cap.type + , vid_cap.channels + , vid_cap.audios + , vid_cap.maxwidth + , vid_cap.maxheight + , vid_cap.minwidth + , vid_cap.minheight); } struct video_window vid_win; - memset( &vid_win, 0, sizeof(video_window) ); - if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win ) < 0 ) { - Error( "Failed to get window attributes: %s", strerror(errno) ); + memset(&vid_win, 0, sizeof(video_window)); + if ( ioctl(vid_fd, VIDIOCGWIN, &vid_win) < 0 ) { + Error("Failed to get window attributes: %s", strerror(errno)); if ( verbose ) - sprintf( output, "Error, failed to get window attributes: %s\n", strerror(errno) ); + output_ptr += sprintf(output_ptr, "Error, failed to get window attributes: %s\n", strerror(errno)); else - sprintf( output, "error%d\n", errno ); + output_ptr += sprintf(output_ptr, "error%d\n", errno); return false; } if ( verbose ) { - sprintf( output+strlen(output), "Window Attributes\n" ); - sprintf( output+strlen(output), " X Offset: %d\n", vid_win.x ); - sprintf( output+strlen(output), " Y Offset: %d\n", vid_win.y ); - sprintf( output+strlen(output), " Width: %d\n", vid_win.width ); - sprintf( output+strlen(output), " Height: %d\n", vid_win.height ); + output_ptr += sprintf(output_ptr, + "Window Attributes\n" + " X Offset: %d\n" + " Y Offset: %d\n" + " Width: %d\n" + " Height: %d\n" + , vid_win.x + , vid_win.y + , vid_win.width + , vid_win.height ); } else { - sprintf( output+strlen(output), "X:%d|", vid_win.x ); - sprintf( output+strlen(output), "Y:%d|", vid_win.y ); - sprintf( output+strlen(output), "W:%d|", vid_win.width ); - sprintf( output+strlen(output), "H:%d|", vid_win.height ); + output_ptr += sprintf(output_ptr, "X:%d|Y:%d|W:%d|H:%d|", + vid_win.height, vid_win.x, vid_win.y, vid_win.width); } struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic ) < 0 ) { - Error( "Failed to get picture attributes: %s", strerror(errno) ); + memset(&vid_pic, 0, sizeof(video_picture)); + if ( ioctl(vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) { + Error("Failed to get picture attributes: %s", strerror(errno)); if ( verbose ) - sprintf( output, "Error, failed to get picture attributes: %s\n", strerror(errno) ); + output_ptr += sprintf(output_ptr, "Error, failed to get picture attributes: %s\n", strerror(errno)); else - sprintf( output, "error%d\n", errno ); + output_ptr += sprintf(output_ptr, "error%d\n", errno); return false; } if ( verbose ) { - sprintf( output+strlen(output), "Picture Attributes\n" ); - sprintf( output+strlen(output), " Palette: %d - %s\n", vid_pic.palette, + output_ptr += sprintf(output_ptr, + "Picture Attributes\n" + " Palette: %d - %s\n" + " Colour Depth: %d\n" + " Brightness: %d\n" + " Hue: %d\n" + " Colour :%d\n" + " Contrast: %d\n" + " Whiteness: %d\n" + , vid_pic.palette, vid_pic.palette==VIDEO_PALETTE_GREY?"Linear greyscale":( vid_pic.palette==VIDEO_PALETTE_HI240?"High 240 cube (BT848)":( vid_pic.palette==VIDEO_PALETTE_RGB565?"565 16 bit RGB":( @@ -1659,66 +1689,77 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers vid_pic.palette==VIDEO_PALETTE_YUV411P?"YUV 4:1:1 Planar":( vid_pic.palette==VIDEO_PALETTE_YUV420P?"YUV 4:2:0 Planar":( vid_pic.palette==VIDEO_PALETTE_YUV410P?"YUV 4:1:0 Planar":"Unknown" - )))))))))))))))))); - sprintf( output+strlen(output), " Colour Depth: %d\n", vid_pic.depth ); - sprintf( output+strlen(output), " Brightness: %d\n", vid_pic.brightness ); - sprintf( output+strlen(output), " Hue: %d\n", vid_pic.hue ); - sprintf( output+strlen(output), " Colour :%d\n", vid_pic.colour ); - sprintf( output+strlen(output), " Contrast: %d\n", vid_pic.contrast ); - sprintf( output+strlen(output), " Whiteness: %d\n", vid_pic.whiteness ); + ))))))))))))))))), + vid_pic.depth, + vid_pic.brightness, + vid_pic.hue, + vid_pic.colour, + vid_pic.contrast, + vid_pic.whiteness + ); } else { - sprintf( output+strlen(output), "P:%d|", vid_pic.palette ); - sprintf( output+strlen(output), "D:%d|", vid_pic.depth ); - sprintf( output+strlen(output), "B:%d|", vid_pic.brightness ); - sprintf( output+strlen(output), "h:%d|", vid_pic.hue ); - sprintf( output+strlen(output), "Cl:%d|", vid_pic.colour ); - sprintf( output+strlen(output), "Cn:%d|", vid_pic.contrast ); - sprintf( output+strlen(output), "w:%d|", vid_pic.whiteness ); + output_ptr += sprintf(output_ptr, "P:%d|D:%d|B:%d|h:%d|Cl:%d|Cn:%d|w:%d|", + vid_pic.palette, + vid_pic.depth, + vid_pic.brightness, + vid_pic.hue, + vid_pic.colour, + vid_pic.contrast, + vid_pic.whiteness + ); } for ( int chan = 0; chan < vid_cap.channels; chan++ ) { struct video_channel vid_src; - memset( &vid_src, 0, sizeof(video_channel) ); + memset(&vid_src, 0, sizeof(video_channel)); vid_src.channel = chan; - if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src ) < 0 ) { - Error( "Failed to get channel %d attributes: %s", chan, strerror(errno) ); + if ( ioctl(vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) { + Error("Failed to get channel %d attributes: %s", chan, strerror(errno)); if ( verbose ) - sprintf( output, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno) ); + output_ptr += sprintf(output_ptr, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno)); else - sprintf( output, "error%d\n", errno ); + output_ptr += sprintf(output_ptr, "error%d\n", errno); return false; } if ( verbose ) { - sprintf( output+strlen(output), "Channel %d Attributes\n", chan ); - sprintf( output+strlen(output), " Name: %s\n", vid_src.name ); - sprintf( output+strlen(output), " Channel: %d\n", vid_src.channel ); - sprintf( output+strlen(output), " Flags: %d\n%s%s", vid_src.flags, - (vid_src.flags&VIDEO_VC_TUNER)?" Channel has a tuner\n":"", - (vid_src.flags&VIDEO_VC_AUDIO)?" Channel has audio\n":"" - ); - sprintf( output+strlen(output), " Type: %d - %s\n", vid_src.type, + output_ptr += sprintf(output_ptr, + "Channel %d Attributes\n" + " Name: %s\n" + " Channel: %d\n" + " Flags: %d\n%s%s" + " Type: %d - %s\n" + " Format: %d - %s\n" + , chan + , vid_src.name + , vid_src.channel + , vid_src.flags + , (vid_src.flags&VIDEO_VC_TUNER)?" Channel has a tuner\n":"" + , (vid_src.flags&VIDEO_VC_AUDIO)?" Channel has audio\n":"" + , vid_src.type, vid_src.type==VIDEO_TYPE_TV?"TV":( vid_src.type==VIDEO_TYPE_CAMERA?"Camera":"Unknown" - )); - sprintf( output+strlen(output), " Format: %d - %s\n", vid_src.norm, + ) + , vid_src.norm, vid_src.norm==VIDEO_MODE_PAL?"PAL":( vid_src.norm==VIDEO_MODE_NTSC?"NTSC":( vid_src.norm==VIDEO_MODE_SECAM?"SECAM":( vid_src.norm==VIDEO_MODE_AUTO?"AUTO":"Unknown" )))); } else { - sprintf( output+strlen(output), "n%d:%s|", chan, vid_src.name ); - sprintf( output+strlen(output), "C%d:%d|", chan, vid_src.channel ); - sprintf( output+strlen(output), "Fl%d:%x|", chan, vid_src.flags ); - sprintf( output+strlen(output), "T%d:%d|", chan, vid_src.type ); - sprintf( output+strlen(output), "F%d:%d%s|", chan, vid_src.norm, chan==(vid_cap.channels-1)?"":"," ); + output_ptr += sprintf(output_ptr, "n%d:%s|C%d:%d|Fl%d:%x|T%d:%d|F%d:%d%s|" + , chan, vid_src.name + , chan, vid_src.channel + , chan, vid_src.flags + , chan, vid_src.type + , chan, vid_src.norm, chan==(vid_cap.channels-1)?"":"," + ); } } if ( !verbose ) - output[strlen(output)-1] = '\n'; + *output_ptr = '\n'; } #endif // ZM_HAS_V4L1 - close( vid_fd ); + close(vid_fd); if ( device ) break; } while ( ++devIndex < 32 ); From 30072cf9e16a8ffcc0921505759c5c1b943e8d89 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 15 Jun 2020 16:06:51 -0400 Subject: [PATCH 42/47] Apply input limits when switching between percent and pixels. pixels must be integer, percent can be decimal. --- web/skins/classic/views/js/zone.js | 6 +++- web/skins/classic/views/zone.php | 51 +++++++++++++++++------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/web/skins/classic/views/js/zone.js b/web/skins/classic/views/js/zone.js index 2ea02cfd9..e8dd32926 100644 --- a/web/skins/classic/views/js/zone.js +++ b/web/skins/classic/views/js/zone.js @@ -179,12 +179,16 @@ function toPixels(field, maxValue) { if ( field.value != '' ) { field.value = Math.round((field.value*maxValue)/100); } + field.setAttribute('step', 1); + field.setAttribute('max', monitorArea); } function toPercent(field, maxValue) { if ( field.value != '' ) { field.value = Math.round((100*100*field.value)/maxValue)/100; } + field.setAttribute('step', 0.01); + field.setAttribute('max', 100); } function applyZoneUnits() { @@ -384,7 +388,7 @@ function saveChanges(element) { if ( validateForm(form) ) { submitForm(form); if ( form.elements['newZone[Type]'].value == 'Privacy' ) { - alert( 'Capture process for this monitor will be restarted for the Privacy zone changes to take effect.' ); + alert('Capture process for this monitor will be restarted for the Privacy zone changes to take effect.'); } return true; } diff --git a/web/skins/classic/views/zone.php b/web/skins/classic/views/zone.php index 6b5801744..aafda2b32 100644 --- a/web/skins/classic/views/zone.php +++ b/web/skins/classic/views/zone.php @@ -174,29 +174,36 @@ xhtmlHeaders(__FILE__, translate('Zone')); 'applyZoneType()')); ?> + array('data-on-change'=>'applyZoneType')); ?> 'applyPreset()', 'onblur'=>'this.selectedIndex=0') ) + array('data-on-change'=>'applyPreset', 'onblur'=>'this.selectedIndex=0') ) ?> - 'applyZoneUnits()') ) ?> + +'applyZoneUnits') + ); + # Used later for number inputs + $step = $newZone['Units'] == 'Percent' ? ' step="0.01" max="100"' : ''; +?> + - + / - + / - + @@ -205,45 +212,45 @@ xhtmlHeaders(__FILE__, translate('Zone')); - - + + - - + + - + - - + min="0"/> + min="0"/> - - + min="0"/> + min="0"/> - - + min="0"/> + min="0"/> - - + + - + - + From aba403e3f27726908dbf2abd61ddaa5e8a58e014 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 16 Jun 2020 16:48:17 -0400 Subject: [PATCH 43/47] Correct handling of Transport in onvif results --- web/skins/classic/views/onvifprobe.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/onvifprobe.php b/web/skins/classic/views/onvifprobe.php index 95e1abeba..2426a3892 100644 --- a/web/skins/classic/views/onvifprobe.php +++ b/web/skins/classic/views/onvifprobe.php @@ -96,12 +96,14 @@ function probeProfiles($device_ep, $soapversion, $username, $password) { if ( $lines = @execONVIF("profiles $device_ep $soapversion $username $password") ) { foreach ( $lines as $line ) { $line = rtrim( $line ); - if ( preg_match('|^(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+),\s*(.+)\s*$|', $line, $matches) ) { - $stream_uri = $matches[7]; + + if ( preg_match('|^([^,]+),\s*([^,]+),\s*([^,]+),\s*(\d+),\s*(\d+),\s*(\d+),\s*([^,]+),\s*(.+)\s*$|', $line, $matches) ) { + $stream_uri = $matches[8]; // add user@pass to URI if ( preg_match('|^(\S+://)(.+)$|', $stream_uri, $tokens) ) { $stream_uri = $tokens[1].$username.':'.$password.'@'.$tokens[2]; } + ZM\Warning(print_r($matches,1)); $profile = array( # 'monitor' part of camera 'Type' => 'Ffmpeg', @@ -113,6 +115,7 @@ function probeProfiles($device_ep, $soapversion, $username, $password) { 'Profile' => $matches[1], 'Name' => $matches[2], 'Encoding' => $matches[3], + 'Transport' => $matches[7], ); $profiles[] = $profile; } else { @@ -261,10 +264,17 @@ if ( !isset($_REQUEST['step']) || ($_REQUEST['step'] == '1') ) { $monitor = $camera['monitor']; $sourceString = "${profile['Name']} : ${profile['Encoding']}" . - " (${profile['Width']}x${profile['Height']} @ ${profile['MaxFPS']}fps)"; + " (${profile['Width']}x${profile['Height']} @ ${profile['MaxFPS']}fps ${profile['Transport']})"; // copy technical details $monitor['Width'] = $profile['Width']; $monitor['Height'] = $profile['Height']; + if ( $profile['Transport'] == 'RTP-Multicast' ) { + $monitor['Method'] = 'rtpMulti'; + } else if ( $profile['Transport'] == 'RTP-Unicast' ) { + $monitor['Method'] = 'rtpUni'; + } else { + $monitor['Method'] = 'rtpRtsp'; + } // The maxfps fields do not work for ip streams. Can re-enable if that is fixed. // $monitor['MaxFPS'] = $profile['MaxFPS']; // $monitor['AlarmMaxFPS'] = $profile['AlarmMaxFPS']; From dcb77cdaea3da31b6a0400ef50ae5f1269bd587c Mon Sep 17 00:00:00 2001 From: Romain ODDONE <34140141+roddone@users.noreply.github.com> Date: Wed, 17 Jun 2020 14:46:10 +0200 Subject: [PATCH 44/47] apply stream scale in zoom coordinates --- web/skins/classic/views/js/watch.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index d1af6082d..7fb92fed5 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -747,9 +747,11 @@ function fetchImage( streamImage ) { } function handleClick( event ) { - var target = event.target; - var x = event.page.x - $(target).getLeft(); - var y = event.page.y - $(target).getTop(); + var $target = $(event.target); + var scaleX = parseInt(monitorWidth / $target.getWidth()); + var scaleY = parseInt(monitorHeight / $target.getHeight()); + var x = (event.page.x - $target.getLeft()) * scaleX; + var y = (event.page.y - $target.getTop()) * scaleY; if ( showMode == 'events' || !imageControlMode ) { if ( event.shift ) { From 8efe00157d014b2921cdadbace476cb15d2bc61c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 17 Jun 2020 12:50:22 -0400 Subject: [PATCH 45/47] add a User object --- web/includes/User.php | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 web/includes/User.php diff --git a/web/includes/User.php b/web/includes/User.php new file mode 100644 index 000000000..a80e8ed00 --- /dev/null +++ b/web/includes/User.php @@ -0,0 +1,50 @@ + null, + 'Username' => '', + 'Password' => '', + 'Language' => '', + 'Enabled' => 1, + 'Stream' => 'None', + 'Events' => 'None', + 'Control' => 'None', + 'Monitors' => 'None', + 'Groups' => 'None', + 'Devices' => 'None', + 'System'=> 'None', + 'MaxBandwidth' =>'', + 'MonitorIds' =>'', + 'TokenMinExpiry' => 0, + 'APIEnabled'=> 1, + ); + + public static function find( $parameters = array(), $options = array() ) { + return ZM_Object::_find(get_class(), $parameters, $options); + } + + public static function find_one( $parameters = array(), $options = array() ) { + return ZM_Object::_find_one(get_class(), $parameters, $options); + } + + public function Name( ) { + return $this->{'Username'}; + } + + public static function Indexed_By_Id() { + $results = array(); + foreach ( ZM_Object::_find('ZM\User', null, array('order'=>'lower(Username)')) as $Object ) { + $results[$Object->Id()] = $Object; + } + return $results; + } + +} # end class User +?> From d3c474fc8b6d4be5d000c94347a3944997882797 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Wed, 17 Jun 2020 12:50:36 -0400 Subject: [PATCH 46/47] use translate on Show/Hide Zones --- web/skins/classic/views/montage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index 5a20ca18c..5801b04c6 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -151,11 +151,11 @@ if ( $showControl ) { } if ( $showZones ) { ?> - Hide Zones + - Show Zones + From 7354b4d30f70d1ba265fdc4bd1315babe3c3dad2 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 23 Jun 2020 14:43:11 -0400 Subject: [PATCH 47/47] Fix compile on old ffmpeg --- src/zm_ffmpeg_camera.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 11c25addc..1d51ade20 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -976,10 +976,12 @@ int FfmpegCamera::CaptureAndRecord( return -1; } #if HAVE_LIBAVUTIL_HWCONTEXT_H +#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0) if ( (ret == AVERROR_INVALIDDATA ) && (hw_pix_fmt != AV_PIX_FMT_NONE) ) { use_hwaccel = false; return -1; } +#endif #endif } zm_av_packet_unref(&packet);