From 7365c053e6bcd67ef960d6de89bf722b44a3280f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 17 Dec 2015 12:56:40 -0500 Subject: [PATCH 1/8] Create a Filter object, out of some of the code in zmfilter.pl. The purpose is to be able to use it elsewhere like zmvideo.pl. --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 494 +++++++++++++++++++ scripts/ZoneMinder/lib/ZoneMinder/Storage.pm | 160 ++++++ 2 files changed, 654 insertions(+) create mode 100644 scripts/ZoneMinder/lib/ZoneMinder/Filter.pm create mode 100644 scripts/ZoneMinder/lib/ZoneMinder/Storage.pm diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm new file mode 100644 index 000000000..4888636a6 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -0,0 +1,494 @@ +# ========================================================================== +# +# ZoneMinder Filter Module, $Date$, $Revision$ +# Copyright (C) 2001-2008 Philip Coombes +# +# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ========================================================================== +# +# This module contains the common definitions and functions used by the rest +# of the ZoneMinder scripts +# +package ZoneMinder::Filter; + +use 5.006; +use strict; +use warnings; + +require Exporter; +require ZoneMinder::Base; +require Date::Manip; + +our @ISA = qw(Exporter ZoneMinder::Base); + +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. + +# This allows declaration use ZoneMinder ':all'; +# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK +# will save memory. +our %EXPORT_TAGS = ( + 'functions' => [ qw( + ) ] +); +push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; + +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); + +our @EXPORT = qw(); + +our $VERSION = $ZoneMinder::Base::VERSION; + +# ========================================================================== +# +# General Utility Functions +# +# ========================================================================== + +use ZoneMinder::Config qw(:all); +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Database qw(:all); + +use POSIX; + +sub new { + my ( $parent, $id, $data ) = @_; + + my $self = {}; + bless $self, $parent; + $$self{dbh} = $ZoneMinder::Database::dbh; +#zmDbConnect(); + if ( ( $$self{Id} = $id ) or $data ) { +#$log->debug("loading $parent $id") if $debug or DEBUG_ALL; + $self->load( $data ); + } + return $self; +} # end sub new + +sub load { + my ( $self, $data ) = @_; + my $type = ref $self; + if ( ! $data ) { +#$log->debug("Object::load Loading from db $type"); + $data = $$self{dbh}->selectrow_hashref( 'SELECT * FROM Filter WHERE Id=?', {}, $$self{Id} ); + if ( ! $data ) { + Error( "Failure to load Filter record for $$self{Id}: Reason: " . $$self{dbh}->errstr ); + } else { + Debug( 3, "Loaded Filter $$self{Id}" ); + } # end if + } # end if ! $data + if ( $data and %$data ) { + @$self{keys %$data} = values %$data; + } # end if +} # end sub load + +sub Name { + if ( @_ > 1 ) { + $_[0]{Name} = $_[1]; + } + return $_[0]{Name}; +} # end sub Path + +sub find { + +} +sub Execute { + my $self = $_[0]; + + my $sql = $self->Sql(); + + if ( $self->{HasDiskPercent} ) + { + my $disk_percent = getDiskPercent(); + $sql =~ s/zmDiskPercent/$disk_percent/g; + } + if ( $self->{HasDiskBlocks} ) + { + my $disk_blocks = getDiskBlocks(); + $sql =~ s/zmDiskBlocks/$disk_blocks/g; + } + if ( $self->{HasSystemLoad} ) + { + my $load = getLoad(); + $sql =~ s/zmSystemLoad/$load/g; + } + + my $sth = $$self{dbh}->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$$self{dbh}->errstr() ); + my $res = $sth->execute(); + if ( !$res ) + { + Error( "Can't execute filter '$sql', ignoring: ".$sth->errstr() ); + return; + } + my @results; + + while( my $event = $sth->fetchrow_hashref() ) { + push @results, $event; + } + $sth->finish(); + return @results; +} + +sub Sql { + my $self = $_[0]; + if ( ! $$self{Sql} ) { + my $filter_expr = ZoneMinder::General::jsonDecode( $self->{Query} ); + my $sql = "SELECT E.Id, + E.MonitorId, + M.Name as MonitorName, + M.DefaultRate, + M.DefaultScale, + E.Name, + E.Cause, + E.Notes, + E.StartTime, + unix_timestamp(E.StartTime) as Time, + E.Length, + E.Frames, + E.AlarmFrames, + E.TotScore, + E.AvgScore, + E.MaxScore, + E.Archived, + E.Videoed, + E.Uploaded, + E.Emailed, + E.Messaged, + E.Executed + FROM Events as E + INNER JOIN Monitors as M on M.Id = E.MonitorId + "; + $self->{Sql} = ''; + + if ( $filter_expr->{terms} ) { + for ( my $i = 0; $i < @{$filter_expr->{terms}}; $i++ ) { + if ( exists($filter_expr->{terms}[$i]->{cnj}) ) { + $self->{Sql} .= " ".$filter_expr->{terms}[$i]->{cnj}." "; + } + if ( exists($filter_expr->{terms}[$i]->{obr}) ) { + $self->{Sql} .= " ".str_repeat( "(", $filter_expr->{terms}[$i]->{obr} )." "; + } + my $value = $filter_expr->{terms}[$i]->{val}; + my @value_list; + if ( $filter_expr->{terms}[$i]->{attr} ) { + if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) { + my ( $temp_attr_name ) = $filter_expr->{terms}[$i]->{attr} =~ /^Monitor(.+)$/; + $self->{Sql} .= "M.".$temp_attr_name; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) { + $self->{Sql} .= "E.StartTime"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) { + $self->{Sql} .= "to_days( E.StartTime )"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) { + $self->{Sql} .= "extract( hour_second from E.StartTime )"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Weekday' ) { + $self->{Sql} .= "weekday( E.StartTime )"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskPercent' ) { + $self->{Sql} .= "zmDiskPercent"; + $self->{HasDiskPercent} = !undef; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskBlocks' ) { + $self->{Sql} .= "zmDiskBlocks"; + $self->{HasDiskBlocks} = !undef; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'SystemLoad' ) { + $self->{Sql} .= "zmSystemLoad"; + $self->{HasSystemLoad} = !undef; + } else { + $self->{Sql} .= "E.".$filter_expr->{terms}[$i]->{attr}; + } + + ( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/; + foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) { + if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) { + $value = "'$temp_value'"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Name' + || $filter_expr->{terms}[$i]->{attr} eq 'Cause' + || $filter_expr->{terms}[$i]->{attr} eq 'Notes' + ) { + $value = "'$temp_value'"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) { + $value = DateTimeToSQL( $temp_value ); + if ( !$value ) { + Error( "Error parsing date/time '$temp_value', " + ."skipping filter '$self->{Name}'\n" ); + return; + } + $value = "'$value'"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) { + $value = DateTimeToSQL( $temp_value ); + if ( !$value ) { + Error( "Error parsing date/time '$temp_value', " + ."skipping filter '$self->{Name}'\n" ); + return; + } + $value = "to_days( '$value' )"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) { + $value = DateTimeToSQL( $temp_value ); + if ( !$value ) { + Error( "Error parsing date/time '$temp_value', " + ."skipping filter '$self->{Name}'\n" ); + return; + } + $value = "extract( hour_second from '$value' )"; + } else { + $value = $temp_value; + } + push( @value_list, $value ); + } # end foreach temp_value + } # end if has an attr + if ( $filter_expr->{terms}[$i]->{op} ) { + if ( $filter_expr->{terms}[$i]->{op} eq '=~' ) { + $self->{Sql} .= " regexp $value"; + } elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) { + $self->{Sql} .= " not regexp $value"; + } elsif ( $filter_expr->{terms}[$i]->{op} eq '=[]' ) { + $self->{Sql} .= " in (".join( ",", @value_list ).")"; + } elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) { + $self->{Sql} .= " not in (".join( ",", @value_list ).")"; + } else { + $self->{Sql} .= " ".$filter_expr->{terms}[$i]->{op}." $value"; + } + } # end if has an operator + if ( exists($filter_expr->{terms}[$i]->{cbr}) ) { + $self->{Sql} .= " ".str_repeat( ")", $filter_expr->{terms}[$i]->{cbr} )." "; + } + } # end foreach term + } # end if terms + + if ( $self->{Sql} ) + { + if ( $self->{AutoMessage} ) + { + # Include all events, including events that are still ongoing + # and have no EndTime yet + $sql .= " and ( ".$self->{Sql}." )"; + } + else + { + # Only include closed events (events with valid EndTime) + $sql .= " where not isnull(E.EndTime) and ( ".$self->{Sql}." )"; + } + } + my @auto_terms; + if ( $self->{AutoArchive} ) + { + push( @auto_terms, "E.Archived = 0" ) + } + if ( $self->{AutoVideo} ) + { + push( @auto_terms, "E.Videoed = 0" ) + } + if ( $self->{AutoUpload} ) + { + push( @auto_terms, "E.Uploaded = 0" ) + } + if ( $self->{AutoEmail} ) + { + push( @auto_terms, "E.Emailed = 0" ) + } + if ( $self->{AutoMessage} ) + { + push( @auto_terms, "E.Messaged = 0" ) + } + if ( $self->{AutoExecute} ) + { + push( @auto_terms, "E.Executed = 0" ) + } + if ( @auto_terms ) + { + $sql .= " and ( ".join( " or ", @auto_terms )." )"; + } + if ( !$filter_expr->{sort_field} ) + { + $filter_expr->{sort_field} = 'StartTime'; + $filter_expr->{sort_asc} = 0; + } + my $sort_column = ''; + if ( $filter_expr->{sort_field} eq 'Id' ) + { + $sort_column = "E.Id"; + } + elsif ( $filter_expr->{sort_field} eq 'MonitorName' ) + { + $sort_column = "M.Name"; + } + elsif ( $filter_expr->{sort_field} eq 'Name' ) + { + $sort_column = "E.Name"; + } + elsif ( $filter_expr->{sort_field} eq 'StartTime' ) + { + $sort_column = "E.StartTime"; + } + elsif ( $filter_expr->{sort_field} eq 'Secs' ) + { + $sort_column = "E.Length"; + } + elsif ( $filter_expr->{sort_field} eq 'Frames' ) + { + $sort_column = "E.Frames"; + } + elsif ( $filter_expr->{sort_field} eq 'AlarmFrames' ) + { + $sort_column = "E.AlarmFrames"; + } + elsif ( $filter_expr->{sort_field} eq 'TotScore' ) + { + $sort_column = "E.TotScore"; + } + elsif ( $filter_expr->{sort_field} eq 'AvgScore' ) + { + $sort_column = "E.AvgScore"; + } + elsif ( $filter_expr->{sort_field} eq 'MaxScore' ) + { + $sort_column = "E.MaxScore"; + } + else + { + $sort_column = "E.StartTime"; + } + my $sort_order = $filter_expr->{sort_asc}?"asc":"desc"; + $sql .= " order by ".$sort_column." ".$sort_order; + if ( $filter_expr->{limit} ) + { + $sql .= " limit 0,".$filter_expr->{limit}; + } + Debug( "SQL:$sql\n" ); + $self->{Sql} = $sql; + } # end if has Sql + return $self->{Sql}; +} # end sub Sql + +sub getDiskPercent +{ + my $command = "df ."; + my $df = qx( $command ); + my $space = -1; + if ( $df =~ /\s(\d+)%/ms ) + { + $space = $1; + } + return( $space ); +} +sub getDiskBlocks +{ + my $command = "df ."; + my $df = qx( $command ); + my $space = -1; + if ( $df =~ /\s(\d+)\s+\d+\s+\d+%/ms ) + { + $space = $1; + } + return( $space ); +} +sub getLoad +{ + my $command = "uptime ."; + my $uptime = qx( $command ); + my $load = -1; + if ( $uptime =~ /load average:\s+([\d.]+)/ms ) + { + $load = $1; + Info( "Load: $load" ); + } + return( $load ); +} + +# +# More or less replicates the equivalent PHP function +# +sub strtotime +{ + my $dt_str = shift; + return( Date::Manip::UnixDate( $dt_str, '%s' ) ); +} + +# +# More or less replicates the equivalent PHP function +# +sub str_repeat +{ + my $string = shift; + my $count = shift; + return( ${string}x${count} ); +} + +# Formats a date into MySQL format +sub DateTimeToSQL +{ + my $dt_str = shift; + my $dt_val = strtotime( $dt_str ); + if ( !$dt_val ) + { + Error( "Unable to parse date string '$dt_str'\n" ); + return( undef ); + } + return( strftime( "%Y-%m-%d %H:%M:%S", localtime( $dt_val ) ) ); +} + +1; +__END__ +# Below is stub documentation for your module. You'd better edit it! + +=head1 NAME + +ZoneMinder::Database - Perl extension for blah blah blah + +=head1 SYNOPSIS + + use ZoneMinder::Filter; + blah blah blah + +=head1 DESCRIPTION + +Stub documentation for ZoneMinder, created by h2xs. It looks like the +author of the extension was negligent enough to leave the stub +unedited. + +Blah blah blah. + +=head2 EXPORT + +None by default. + + + +=head1 SEE ALSO + +Mention other useful documentation such as the documentation of +related modules or operating system documentation (such as man pages +in UNIX), or any relevant external documentation such as RFCs or +standards. + +If you have a mailing list set up for your module, mention it here. + +If you have a web site set up for your module, mention it here. + +=head1 AUTHOR + +Philip Coombes, Ephilip.coombes@zoneminder.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2008 Philip Coombes + +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 diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm b/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm new file mode 100644 index 000000000..d89599e61 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm @@ -0,0 +1,160 @@ +# ========================================================================== +# +# ZoneMinder Storage Module, $Date$, $Revision$ +# Copyright (C) 2001-2008 Philip Coombes +# +# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ========================================================================== +# +# This module contains the common definitions and functions used by the rest +# of the ZoneMinder scripts +# +package ZoneMinder::Storage; + +use 5.006; +use strict; +use warnings; + +require Exporter; +require ZoneMinder::Base; + +our @ISA = qw(Exporter ZoneMinder::Base); + +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. + +# This allows declaration use ZoneMinder ':all'; +# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK +# will save memory. +our %EXPORT_TAGS = ( + 'functions' => [ qw( + ) ] +); +push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; + +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); + +our @EXPORT = qw(); + +our $VERSION = $ZoneMinder::Base::VERSION; + +# ========================================================================== +# +# General Utility Functions +# +# ========================================================================== + +use ZoneMinder::Config qw(:all); +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Database qw(:all); + +use POSIX; + +sub new { + my ( $parent, $id, $data ) = @_; + + my $self = {}; + bless $self, $parent; + if ( ( $$self{id} = $id ) or $data ) { +#$log->debug("loading $parent $id") if $debug or DEBUG_ALL; + $self->load( $data ); + } +} # end sub new + +sub load { + my ( $self, $data ) = @_; + my $type = ref $self; + if ( ! $data ) { +#$log->debug("Object::load Loading from db $type"); + $data = $ZoneMinder::dbh->selectrow_hashref( 'SELECT * FROM Storage WHERE Id=?', {}, $$self{Id} ); + if ( ! $data ) { + if ( $ZoneMinder::dbh->errstr ) { + Error( "Failure to load Storage record for $$self{id}: Reason: " . $ZoneMinder::dbh->errstr ); + } # end if + } # end if + } # end if ! $data + if ( $data and %$data ) { + @$self{keys %$data} = values %$data; + } # end if +} # end sub load + +sub Path { + if ( @_ > 1 ) { + $_[0]{Path} = $_[1]; + } + return $_[0]{Path}; +} # end sub Path + +sub Name { + if ( @_ > 1 ) { + $_[0]{Name} = $_[1]; + } + return $_[0]{Name}; +} # end sub Path + +1; +__END__ +# Below is stub documentation for your module. You'd better edit it! + +=head1 NAME + +ZoneMinder::Database - Perl extension for blah blah blah + +=head1 SYNOPSIS + + use ZoneMinder::Storage; + blah blah blah + +=head1 DESCRIPTION + +Stub documentation for ZoneMinder, created by h2xs. It looks like the +author of the extension was negligent enough to leave the stub +unedited. + +Blah blah blah. + +=head2 EXPORT + +None by default. + + + +=head1 SEE ALSO + +Mention other useful documentation such as the documentation of +related modules or operating system documentation (such as man pages +in UNIX), or any relevant external documentation such as RFCs or +standards. + +If you have a mailing list set up for your module, mention it here. + +If you have a web site set up for your module, mention it here. + +=head1 AUTHOR + +Philip Coombes, Ephilip.coombes@zoneminder.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2008 Philip Coombes + +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 64f65b55cc3c1b98a1b47347fbe6b794a99ca2e5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 17 Dec 2015 12:57:14 -0500 Subject: [PATCH 2/8] use new Filter object to do some of the heavy lifting in zmfilter.pl --- scripts/zmfilter.pl.in | 362 ++--------------------------------------- 1 file changed, 13 insertions(+), 349 deletions(-) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 54f372262..7e6c63563 100755 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -60,10 +60,11 @@ use constant START_DELAY => 5; # How long to wait before starting @EXTRA_PERL_LIB@ use ZoneMinder; +require ZoneMinder::Filter; use DBI; use POSIX; use Time::HiRes qw/gettimeofday/; -use Date::Manip; +#use Date::Manip; use Getopt::Long; use autouse 'Pod::Usage'=>qw(pod2usage); use autouse 'Data::Dumper'=>qw(Dumper); @@ -137,36 +138,6 @@ my $filter_parm = ""; my $version = 0; # -# More or less replicates the equivalent PHP function -# -sub strtotime -{ - my $dt_str = shift; - return( UnixDate( $dt_str, '%s' ) ); -} - -# -# More or less replicates the equivalent PHP function -# -sub str_repeat -{ - my $string = shift; - my $count = shift; - return( ${string}x${count} ); -} - -# Formats a date into MySQL format -sub DateTimeToSQL -{ - my $dt_str = shift; - my $dt_val = strtotime( $dt_str ); - if ( !$dt_val ) - { - Error( "Unable to parse date string '$dt_str'\n" ); - return( undef ); - } - return( strftime( "%Y-%m-%d %H:%M:%S", localtime( $dt_val ) ) ); -} GetOptions( 'filter=s' =>\$filter_parm, @@ -299,298 +270,18 @@ sub getFilters } FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) { + my $filter = new ZoneMinder::Filter( $$db_filter{Id}, $db_filter ); Debug( "Found filter '$db_filter->{Name}'\n" ); - my $filter_expr = jsonDecode( $db_filter->{Query} ); - my $sql = "SELECT E.Id, - E.MonitorId, - M.Name as MonitorName, - M.DefaultRate, - M.DefaultScale, - E.Name, - E.Cause, - E.Notes, - E.StartTime, - unix_timestamp(E.StartTime) as Time, - E.Length, - E.Frames, - E.AlarmFrames, - E.TotScore, - E.AvgScore, - E.MaxScore, - E.Archived, - E.Videoed, - E.Uploaded, - E.Emailed, - E.Messaged, - E.Executed - FROM Events as E - INNER JOIN Monitors as M on M.Id = E.MonitorId - "; - $db_filter->{Sql} = ''; + my $sql = $filter->Sql(); - if ( $filter_expr->{terms} ) - { - for ( my $i = 0; $i < @{$filter_expr->{terms}}; $i++ ) - { - if ( exists($filter_expr->{terms}[$i]->{cnj}) ) - { - $db_filter->{Sql} .= " ".$filter_expr->{terms}[$i]->{cnj}." "; - } - if ( exists($filter_expr->{terms}[$i]->{obr}) ) - { - $db_filter->{Sql} .= " ".str_repeat( "(", $filter_expr->{terms}[$i]->{obr} )." "; - } - my $value = $filter_expr->{terms}[$i]->{val}; - my @value_list; - if ( $filter_expr->{terms}[$i]->{attr} ) - { - if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) - { - my ( $temp_attr_name ) = $filter_expr->{terms}[$i]->{attr} =~ /^Monitor(.+)$/; - $db_filter->{Sql} .= "M.".$temp_attr_name; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) - { - $db_filter->{Sql} .= "E.StartTime"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) - { - $db_filter->{Sql} .= "to_days( E.StartTime )"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) - { - $db_filter->{Sql} .= "extract( hour_second from E.StartTime )"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Weekday' ) - { - $db_filter->{Sql} .= "weekday( E.StartTime )"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskPercent' ) - { - $db_filter->{Sql} .= "zmDiskPercent"; - $db_filter->{HasDiskPercent} = !undef; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskBlocks' ) - { - $db_filter->{Sql} .= "zmDiskBlocks"; - $db_filter->{HasDiskBlocks} = !undef; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'SystemLoad' ) - { - $db_filter->{Sql} .= "zmSystemLoad"; - $db_filter->{HasSystemLoad} = !undef; - } - else - { - $db_filter->{Sql} .= "E.".$filter_expr->{terms}[$i]->{attr}; - } - - ( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/; - foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) - { - if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) - { - $value = "'$temp_value'"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Name' - || $filter_expr->{terms}[$i]->{attr} eq 'Cause' - || $filter_expr->{terms}[$i]->{attr} eq 'Notes' - ) - { - $value = "'$temp_value'"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) - { - $value = DateTimeToSQL( $temp_value ); - if ( !$value ) - { - Error( "Error parsing date/time '$temp_value', " - ."skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - } - $value = "'$value'"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) - { - $value = DateTimeToSQL( $temp_value ); - if ( !$value ) - { - Error( "Error parsing date/time '$temp_value', " - ."skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - } - $value = "to_days( '$value' )"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) - { - $value = DateTimeToSQL( $temp_value ); - if ( !$value ) - { - Error( "Error parsing date/time '$temp_value', " - ."skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - } - $value = "extract( hour_second from '$value' )"; - } - else - { - $value = $temp_value; - } - push( @value_list, $value ); - } - } - if ( $filter_expr->{terms}[$i]->{op} ) - { - if ( $filter_expr->{terms}[$i]->{op} eq '=~' ) - { - $db_filter->{Sql} .= " regexp $value"; - } - elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) - { - $db_filter->{Sql} .= " not regexp $value"; - } - elsif ( $filter_expr->{terms}[$i]->{op} eq '=[]' ) - { - $db_filter->{Sql} .= " in (".join( ",", @value_list ).")"; - } - elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) - { - $db_filter->{Sql} .= " not in (".join( ",", @value_list ).")"; - } - else - { - $db_filter->{Sql} .= " ".$filter_expr->{terms}[$i]->{op}." $value"; - } - } - if ( exists($filter_expr->{terms}[$i]->{cbr}) ) - { - $db_filter->{Sql} .= " ".str_repeat( ")", $filter_expr->{terms}[$i]->{cbr} )." "; - } - } - } - if ( $db_filter->{Sql} ) - { - if ( $db_filter->{AutoMessage} ) - { - # Include all events, including events that are still ongoing - # and have no EndTime yet - $sql .= " and ( ".$db_filter->{Sql}." )"; - } - else - { - # Only include closed events (events with valid EndTime) - $sql .= " where not isnull(E.EndTime) and ( ".$db_filter->{Sql}." )"; - } - } - my @auto_terms; - if ( $db_filter->{AutoArchive} ) - { - push( @auto_terms, "E.Archived = 0" ) - } - if ( $db_filter->{AutoVideo} ) - { - push( @auto_terms, "E.Videoed = 0" ) - } - if ( $db_filter->{AutoUpload} ) - { - push( @auto_terms, "E.Uploaded = 0" ) - } - if ( $db_filter->{AutoEmail} ) - { - push( @auto_terms, "E.Emailed = 0" ) - } - if ( $db_filter->{AutoMessage} ) - { - push( @auto_terms, "E.Messaged = 0" ) - } - if ( $db_filter->{AutoExecute} ) - { - push( @auto_terms, "E.Executed = 0" ) - } - if ( @auto_terms ) - { - $sql .= " and ( ".join( " or ", @auto_terms )." )"; - } - if ( !$filter_expr->{sort_field} ) - { - $filter_expr->{sort_field} = 'StartTime'; - $filter_expr->{sort_asc} = 0; - } - my $sort_column = ''; - if ( $filter_expr->{sort_field} eq 'Id' ) - { - $sort_column = "E.Id"; - } - elsif ( $filter_expr->{sort_field} eq 'MonitorName' ) - { - $sort_column = "M.Name"; - } - elsif ( $filter_expr->{sort_field} eq 'Name' ) - { - $sort_column = "E.Name"; - } - elsif ( $filter_expr->{sort_field} eq 'StartTime' ) - { - $sort_column = "E.StartTime"; - } - elsif ( $filter_expr->{sort_field} eq 'Secs' ) - { - $sort_column = "E.Length"; - } - elsif ( $filter_expr->{sort_field} eq 'Frames' ) - { - $sort_column = "E.Frames"; - } - elsif ( $filter_expr->{sort_field} eq 'AlarmFrames' ) - { - $sort_column = "E.AlarmFrames"; - } - elsif ( $filter_expr->{sort_field} eq 'TotScore' ) - { - $sort_column = "E.TotScore"; - } - elsif ( $filter_expr->{sort_field} eq 'AvgScore' ) - { - $sort_column = "E.AvgScore"; - } - elsif ( $filter_expr->{sort_field} eq 'MaxScore' ) - { - $sort_column = "E.MaxScore"; - } - else - { - $sort_column = "E.StartTime"; - } - my $sort_order = $filter_expr->{sort_asc}?"asc":"desc"; - $sql .= " order by ".$sort_column." ".$sort_order; - if ( $filter_expr->{limit} ) - { - $sql .= " limit 0,".$filter_expr->{limit}; - } - Debug( "SQL:$sql\n" ); - $db_filter->{Sql} = $sql; - if ( $db_filter->{AutoExecute} ) - { - my $script = $db_filter->{AutoExecuteCmd}; - $script =~ s/\s.*$//; - if ( !-e $script ) - { - Error( "Auto execute script '$script' not found, " - ."skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - - } - elsif ( !-x $script ) - { - Error( "Auto execute script '$script' not executable, " - ."skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - } - } - push( @filters, $db_filter ); + if ( ! $sql ) { + Error( "Error parsing Sql. skipping filter '$db_filter->{Name}'\n" ); + next FILTER; + } + push( @filters, $filter ); } $sth->finish(); - + Debug( "Got " . @filters . " filters" ); return( \@filters ); } @@ -608,37 +299,11 @@ sub checkFilter ($filter->{AutoExecute}?", execute":""). "\n" ); - my $sql = $filter->{Sql}; - if ( $filter->{HasDiskPercent} ) - { - my $disk_percent = getDiskPercent(); - $sql =~ s/zmDiskPercent/$disk_percent/g; - } - if ( $filter->{HasDiskBlocks} ) - { - my $disk_blocks = getDiskBlocks(); - $sql =~ s/zmDiskBlocks/$disk_blocks/g; - } - if ( $filter->{HasSystemLoad} ) - { - my $load = getLoad(); - $sql =~ s/zmSystemLoad/$load/g; - } - - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute(); - if ( !$res ) - { - Error( "Can't execute filter '$sql', ignoring: ".$sth->errstr() ); - return; - } - - while( my $event = $sth->fetchrow_hashref() ) - { + foreach my $event ( $filter->Execute() ) { Debug( "Checking event $event->{Id}\n" ); my $delete_ok = !undef; +$dbh->ping(); if ( $filter->{AutoArchive} ) { Info( "Archiving event $event->{Id}\n" ); @@ -647,7 +312,7 @@ sub checkFilter my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) - or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + or Error( "Can't execute '$sql': ".$sth->errstr() ); } if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) { @@ -719,7 +384,6 @@ sub checkFilter } } } - $sth->finish(); } sub generateVideo From 7a979bb8919b7372dfdbcfd701b22fbfc05db63f Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 17 Dec 2015 13:11:15 -0500 Subject: [PATCH 3/8] remove duplicated routines --- scripts/zmfilter.pl.in | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 7e6c63563..ca0e1565e 100755 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -196,43 +196,6 @@ while( 1 ) sleep( $delay ); } -sub getDiskPercent -{ - my $command = "df ."; - my $df = qx( $command ); - my $space = -1; - if ( $df =~ /\s(\d+)%/ms ) - { - $space = $1; - } - return( $space ); -} - -sub getDiskBlocks -{ - my $command = "df ."; - my $df = qx( $command ); - my $space = -1; - if ( $df =~ /\s(\d+)\s+\d+\s+\d+%/ms ) - { - $space = $1; - } - return( $space ); -} - -sub getLoad -{ - my $command = "uptime ."; - my $uptime = qx( $command ); - my $load = -1; - if ( $uptime =~ /load average:\s+([\d.]+)/ms ) - { - $load = $1; - Info( "Load: $load" ); - } - return( $load ); -} - sub getFilters { my $filter_name = shift; From 6f3402261341db1792a4b33dbbd69b013bc5d672 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 17 Dec 2015 13:11:32 -0500 Subject: [PATCH 4/8] add find and find_one routines --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 34 ++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index 4888636a6..b6985c4aa 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -104,8 +104,40 @@ sub Name { } # end sub Path 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; + + 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 + return @results; } + +sub find_one { + my @results = find(@_); + return $results[0] if @results; +} + sub Execute { my $self = $_[0]; From 9db9d243f2c926f86ab4253ca06da07520edf0cc Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 17 Dec 2015 13:12:37 -0500 Subject: [PATCH 5/8] Fix --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index b6985c4aa..e7b8fd0f6 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -112,7 +112,7 @@ sub find { my @sql_values; if ( exists $sql_filters{Name} ) { - push @sql_filters .= ' Name = ? '; + push @sql_filters , ' Name = ? '; push @sql_values, $sql_filters{Name}; } From c71d1e52ac915642010acb48f0d870f37ab6a690 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 17 Dec 2015 13:13:04 -0500 Subject: [PATCH 6/8] Fix --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index e7b8fd0f6..b7806ec4a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -117,7 +117,7 @@ sub find { } $sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters; - $sql .= ' LIMIT ' . $sql_filters{limit} 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() ); From 5553760222981f19371b80b536aa3f665b15b471 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 21 Dec 2015 10:23:37 -0500 Subject: [PATCH 7/8] Add special cases for ServerId, ServerName, ServerHost to handle supplying the value of the host that the script is running on. --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index b7806ec4a..a32b9b07e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -221,6 +221,8 @@ sub Sql { if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) { my ( $temp_attr_name ) = $filter_expr->{terms}[$i]->{attr} =~ /^Monitor(.+)$/; $self->{Sql} .= "M.".$temp_attr_name; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'ServerHost' ) { + $self->{Sql} .= "M.ServerHost"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) { $self->{Sql} .= "E.StartTime"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) { @@ -246,6 +248,24 @@ sub Sql { foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) { if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) { $value = "'$temp_value'"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'ServerHost' ) { + if ( $temp_value eq 'ZM_SERVER_HOST' ) { + $value = "'$Config{ZM_SERVER_HOST}'"; + } else { + $value = "'$temp_value'"; + } + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'ServerName' ) { + if ( $temp_value eq 'ZM_SERVER_NAME' ) { + $value = "'$Config{ZM_SERVER_NAME}'"; + } else { + $value = "'$temp_value'"; + } + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'ServerId' ) { + if ( $temp_value eq 'ZM_SERVER_ID' ) { + $value = "'$Config{ZM_SERVER_ID}'"; + } else { + $value = "'$temp_value'"; + } } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Name' || $filter_expr->{terms}[$i]->{attr} eq 'Cause' || $filter_expr->{terms}[$i]->{attr} eq 'Notes' From 30353fbcf90f22aba870e8c5235677ca9431f0a5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Mon, 21 Dec 2015 10:33:19 -0500 Subject: [PATCH 8/8] Add special case for Server Attr's because they are on the Monitor, not the event --- scripts/ZoneMinder/lib/ZoneMinder/Filter.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index a32b9b07e..6c782197b 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -221,8 +221,8 @@ sub Sql { if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) { my ( $temp_attr_name ) = $filter_expr->{terms}[$i]->{attr} =~ /^Monitor(.+)$/; $self->{Sql} .= "M.".$temp_attr_name; - } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'ServerHost' ) { - $self->{Sql} .= "M.ServerHost"; + } elsif ( $filter_expr->{terms}[$i]->{attr} =~ /^Server/ ) { + $self->{Sql} .= "M.".$filter_expr->{terms}[$i]->{attr}; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) { $self->{Sql} .= "E.StartTime"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) {