From be5b93a64e90b09c420eae73ff46e778ac09e673 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 2 Jun 2017 11:39:35 -0400 Subject: [PATCH] whitespace, braces, quotes --- scripts/zmfilter.pl.in | 1649 ++++++++++++++++++---------------------- 1 file changed, 751 insertions(+), 898 deletions(-) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index a60fc497e..ef4a2a43e 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -27,7 +27,7 @@ zmfilter.pl - ZoneMinder tool to filter events =head1 SYNOPSIS - zmfilter.pl [-f ,--filter=] | -v, --version +zmfilter.pl [-f ,--filter=] | -v, --version =head1 DESCRIPTION @@ -37,8 +37,8 @@ matching events. =head1 OPTIONS - -f{filter name}, --filter={filter name} - The name of a specific filter to run - -v, --version - Print ZoneMinder version +-f{filter name}, --filter={filter name} - The name of a specific filter to run +-v, --version - Print ZoneMinder version =cut use strict; @@ -70,60 +70,45 @@ use autouse 'Pod::Usage'=>qw(pod2usage); use autouse 'Data::Dumper'=>qw(Dumper); use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|) - ? $Config{ZM_DIR_EVENTS} - : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}) -; + ? $Config{ZM_DIR_EVENTS} + : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}) + ; -logInit(); -logSetSignal(); + logInit(); + logSetSignal(); -if ( $Config{ZM_OPT_UPLOAD} ) -{ - # Comment these out if you don't have them and don't want to upload - # or don't want to use that format - if ( $Config{ZM_UPLOAD_ARCH_FORMAT} eq "zip" ) - { - require Archive::Zip; - import Archive::Zip qw( :ERROR_CODES :CONSTANTS ); + if ( $Config{ZM_OPT_UPLOAD} ) { +# Comment these out if you don't have them and don't want to upload +# or don't want to use that format + if ( $Config{ZM_UPLOAD_ARCH_FORMAT} eq 'zip' ) { + require Archive::Zip; + import Archive::Zip qw( :ERROR_CODES :CONSTANTS ); + } else { + require Archive::Tar; } - else - { - require Archive::Tar; - } - if ( $Config{ZM_UPLOAD_PROTOCOL} eq "ftp" ) - { - require Net::FTP; - } - else - { - require Net::SFTP::Foreign; + if ( $Config{ZM_UPLOAD_PROTOCOL} eq 'ftp' ) { + require Net::FTP; + } else { + require Net::SFTP::Foreign; } + } + +if ( $Config{ZM_OPT_EMAIL} ) { + if ( $Config{ZM_NEW_MAIL_MODULES} ) { + require MIME::Lite; + require Net::SMTP; + } else { + require MIME::Entity; + } } -if ( $Config{ZM_OPT_EMAIL} ) -{ - if ( $Config{ZM_NEW_MAIL_MODULES} ) - { - require MIME::Lite; - require Net::SMTP; - } - else - { - require MIME::Entity; - } -} - -if ( $Config{ZM_OPT_MESSAGE} ) -{ - if ( $Config{ZM_NEW_MAIL_MODULES} ) - { - require MIME::Lite; - require Net::SMTP; - } - else - { - require MIME::Entity; - } +if ( $Config{ZM_OPT_MESSAGE} ) { + if ( $Config{ZM_NEW_MAIL_MODULES} ) { + require MIME::Lite; + require Net::SMTP; + } else { + require MIME::Entity; + } } $| = 1; @@ -134,7 +119,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL}; my $event_id = 0; -my $filter_parm = ""; +my $filter_parm = ''; my $version = 0; # @@ -142,925 +127,793 @@ my $version = 0; GetOptions( 'filter=s' =>\$filter_parm, 'version' =>\$version -) or pod2usage(-exitstatus => -1); + ) or pod2usage(-exitstatus => -1); if ( $version ) { - print ZoneMinder::Base::ZM_VERSION . "\n"; - exit(0); + print ZoneMinder::Base::ZM_VERSION . '\n'; + exit(0); } if ( ! EVENT_PATH ) { - Error( "No event path defined. Config was $Config{ZM_DIR_EVENTS}\n" ); - die; + Error( "No event path defined. Config was $Config{ZM_DIR_EVENTS}\n" ); + die; } chdir( EVENT_PATH ); my $dbh = zmDbConnect(); -if ( $filter_parm ) -{ - Info( "Scanning for events using filter '$filter_parm'\n" ); -} -else -{ - Info( "Scanning for events\n" ); +if ( $filter_parm ) { + Info( "Scanning for events using filter '$filter_parm'\n" ); +} else { + Info( "Scanning for events\n" ); } -if ( !$filter_parm ) -{ - sleep( START_DELAY ); +if ( !$filter_parm ) { + sleep( START_DELAY ); } my $filters; my $last_action = 0; -while( 1 ) -{ - my $now = time; - if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) - { - Debug( "Reloading filters\n" ); - $last_action = $now; - $filters = getFilters( $filter_parm ); - } +while( 1 ) { + my $now = time; + if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) { + Debug( "Reloading filters\n" ); + $last_action = $now; + $filters = getFilters( $filter_parm ); + } - foreach my $filter ( @$filters ) - { - checkFilter( $filter ); - } + foreach my $filter ( @$filters ) { + checkFilter( $filter ); + } - last if ( $filter_parm ); + last if ( $filter_parm ); - Debug( "Sleeping for $delay seconds\n" ); - sleep( $delay ); + Debug( "Sleeping for $delay seconds\n" ); + sleep( $delay ); } -sub getFilters -{ - my $filter_name = shift; +sub getFilters { + my $filter_name = shift; - my @filters; - my $sql = "SELECT * FROM Filters WHERE"; - if ( $filter_name ) - { - $sql .= " Name = ? and"; - } - else - { - $sql .= " Background = 1 and"; - } - $sql .= "( AutoArchive = 1 - or AutoVideo = 1 - or AutoUpload = 1 - or AutoEmail = 1 - or AutoMessage = 1 - or AutoExecute = 1 - or AutoDelete = 1 - ) ORDER BY Name"; - my $sth = $dbh->prepare_cached( $sql ) + my @filters; + my $sql = 'SELECT * FROM Filters WHERE'; + if ( $filter_name ) { + $sql .= ' Name = ? and'; + } else { + $sql .= ' Background = 1 and'; + } + $sql .= '( AutoArchive = 1 + or AutoVideo = 1 + or AutoUpload = 1 + or AutoEmail = 1 + or AutoMessage = 1 + or AutoExecute = 1 + or AutoDelete = 1 + ) ORDER BY Name'; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res; + if ( $filter_name ) { + $res = $sth->execute( $filter_name ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + } else { + $res = $sth->execute() + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + } +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 $sql = $filter->Sql(); + + 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 ); +} + +sub checkFilter { + my $filter = shift; + + Debug( "Checking filter '$filter->{Name}'". + ($filter->{AutoDelete}?', delete':''). + ($filter->{AutoArchive}?', archive':''). + ($filter->{AutoVideo}?', video':''). + ($filter->{AutoUpload}?', upload':''). + ($filter->{AutoEmail}?', email':''). + ($filter->{AutoMessage}?', message':''). + ($filter->{AutoExecute}?', execute':''). + '\n' + ); + + 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" ); +# Do it individually to avoid locking up the table for new events + my $sql = 'update Events set Archived = 1 where Id = ?'; + my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res; - if ( $filter_name ) - { - $res = $sth->execute( $filter_name ) - or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Error( "Can't execute '$sql': ".$sth->errstr() ); } - else - { - $res = $sth->execute() - or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) { + if ( !$event->{Videoed} ) { + $delete_ok = undef if ( !generateVideo( $filter, $event ) ); + } } - 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 $sql = $filter->Sql(); - - if ( ! $sql ) { - Error( "Error parsing Sql. skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - } - push( @filters, $filter ); + if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) { + if ( !$event->{Emailed} ) { + $delete_ok = undef if ( !sendEmail( $filter, $event ) ); + } } - $sth->finish(); - Debug( "Got " . @filters . " filters" ); - return( \@filters ); -} - -sub checkFilter -{ - my $filter = shift; - - Debug( "Checking filter '$filter->{Name}'". - ($filter->{AutoDelete}?", delete":""). - ($filter->{AutoArchive}?", archive":""). - ($filter->{AutoVideo}?", video":""). - ($filter->{AutoUpload}?", upload":""). - ($filter->{AutoEmail}?", email":""). - ($filter->{AutoMessage}?", message":""). - ($filter->{AutoExecute}?", execute":""). - "\n" - ); - - 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" ); - # Do it individually to avoid locking up the table for new events - my $sql = "update Events set Archived = 1 where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) - or Error( "Can't execute '$sql': ".$sth->errstr() ); - } - if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) - { - if ( !$event->{Videoed} ) - { - $delete_ok = undef if ( !generateVideo( $filter, $event ) ); - } - } - if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) - { - if ( !$event->{Emailed} ) - { - $delete_ok = undef if ( !sendEmail( $filter, $event ) ); - } - } - if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) - { - if ( !$event->{Messaged} ) - { - $delete_ok = undef if ( !sendMessage( $filter, $event ) ); - } - } - if ( $Config{ZM_OPT_UPLOAD} && $filter->{AutoUpload} ) - { - if ( !$event->{Uploaded} ) - { - $delete_ok = undef if ( !uploadArchFile( $filter, $event ) ); - } - } - if ( $filter->{AutoExecute} ) - { - if ( !$event->{Executed} ) - { - $delete_ok = undef if ( !executeCommand( $filter, $event ) ); - } - } - if ( $filter->{AutoDelete} ) - { - if ( $delete_ok ) - { - Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId}\n" ); - # Do it individually to avoid locking up the table for new events - my $sql = "delete from Events where Id = ?"; - 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() ); - - if ( ! $Config{ZM_OPT_FAST_DELETE} ) - { - my $sql = "delete from Frames where EventId = ?"; - 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() ); - - $sql = "delete from Stats where EventId = ?"; - $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - $res = $sth->execute( $event->{Id} ) - or Fatal( "Can't execute '$sql': ".$sth->errstr() ); - - deleteEventFiles( $event->{Id}, $event->{MonitorId} ); - } - } - else - { - Error( "Unable to delete event $event->{Id} as previous operations failed\n" ); - } - } + if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) { + if ( !$event->{Messaged} ) { + $delete_ok = undef if ( !sendMessage( $filter, $event ) ); + } } -} - -sub generateVideo -{ - my $filter = shift; - my $event = shift; - my $phone = shift; - - my $rate = $event->{DefaultRate}/100; - my $scale = $event->{DefaultScale}/100; - my $format; - - my @ffmpeg_formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} ); - my $default_video_format; - my $default_phone_format; - foreach my $ffmpeg_format( @ffmpeg_formats ) - { - if ( $ffmpeg_format =~ /^(.+)\*\*$/ ) - { - $default_phone_format = $1; - } - elsif ( $ffmpeg_format =~ /^(.+)\*$/ ) - { - $default_video_format = $1; - } + if ( $Config{ZM_OPT_UPLOAD} && $filter->{AutoUpload} ) { + if ( !$event->{Uploaded} ) { + $delete_ok = undef if ( !uploadArchFile( $filter, $event ) ); + } } - - if ( $phone && $default_phone_format ) - { - $format = $default_phone_format; + if ( $filter->{AutoExecute} ) { + if ( !$event->{Executed} ) { + $delete_ok = undef if ( !executeCommand( $filter, $event ) ); + } } - elsif ( $default_video_format ) - { - $format = $default_video_format; - } - else - { - $format = $ffmpeg_formats[0]; - } - - my $command = $Config{ZM_PATH_BIN}."/zmvideo.pl -e " - .$event->{Id}." -r ".$rate." -s ".$scale." -f ".$format; - my $output = qx($command); - chomp( $output ); - my $status = $? >> 8; - if ( $status || logDebugging() ) - { - Debug( "Output: $output\n" ); - } - if ( $status ) - { - Error( "Video generation '$command' failed with status: $status\n" ); - if ( wantarray() ) - { - return( undef, undef ); - } - return( 0 ); - } - else - { - my $sql = "update Events set Videoed = 1 where Id = ?"; + if ( $filter->{AutoDelete} ) { + if ( $delete_ok ) { + Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId}\n" ); +# Do it individually to avoid locking up the table for new events + my $sql = 'delete from Events where Id = ?'; my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + + if ( ! $Config{ZM_OPT_FAST_DELETE} ) { + my $sql = 'delete from Frames where EventId = ?'; + 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() ); - if ( wantarray() ) - { - return( $format, $output ); + + $sql = 'delete from Stats where EventId = ?'; + $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + + deleteEventFiles( $event->{Id}, $event->{MonitorId} ); } + } else { + Error( "Unable to delete event $event->{Id} as previous operations failed\n" ); + } } - return( 1 ); + } +} + +sub generateVideo { + my $filter = shift; + my $event = shift; + my $phone = shift; + + my $rate = $event->{DefaultRate}/100; + my $scale = $event->{DefaultScale}/100; + my $format; + + my @ffmpeg_formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} ); + my $default_video_format; + my $default_phone_format; + foreach my $ffmpeg_format( @ffmpeg_formats ) { + if ( $ffmpeg_format =~ /^(.+)\*\*$/ ) { + $default_phone_format = $1; + } elsif ( $ffmpeg_format =~ /^(.+)\*$/ ) { + $default_video_format = $1; + } + } + + if ( $phone && $default_phone_format ) { + $format = $default_phone_format; + } elsif ( $default_video_format ) { + $format = $default_video_format; + } else { + $format = $ffmpeg_formats[0]; + } + + my $command = join('', + $Config{ZM_PATH_BIN}, + '/zmvideo.pl -e ', + .$event->{Id}, + ' -r ',$rate,' -s ',$scale,' -f ',$format, + ); + my $output = qx($command); + chomp( $output ); + my $status = $? >> 8; + if ( $status || logDebugging() ) { + Debug( "Output: $output\n" ); + } + if ( $status ) { + Error( "Video generation '$command' failed with status: $status\n" ); + if ( wantarray() ) { + return( undef, undef ); + } + return( 0 ); + } else { + my $sql = 'update Events set Videoed = 1 where Id = ?'; + 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() ); + if ( wantarray() ) { + return( $format, $output ); + } + } + return( 1 ); } # Returns an image absolute path for given event and frame # Optionally an analyse image path may be returned if an analyse image exists # If neither capture nor analyse image exists it will try to extract a frame from .mp4 file if exists # An empty string is returned if no one from methods above works -sub generateImage -{ - my $event = shift; - my $frame = shift; - my $analyse = do { @_ ? shift : 0 }; # don't return analyse image by default +sub generateImage { + my $event = shift; + my $frame = shift; + my $analyse = do { @_ ? shift : 0 }; # don't return analyse image by default - my $capture_image_path = sprintf("%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg", getEventPath($event), $frame->{FrameId}); - my $analyse_image_path = sprintf("%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-analyse.jpg", getEventPath($event), $frame->{FrameId}) if $analyse; - my $video_path = sprintf("%s/%d-video.mp4", getEventPath($event), $event->{Id}); - my $image_path = ""; + my $capture_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-capture.jpg', getEventPath($event), $frame->{FrameId}); + my $analyse_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-analyse.jpg', getEventPath($event), $frame->{FrameId}) if $analyse; + my $video_path = sprintf('%s/%d-video.mp4', getEventPath($event), $event->{Id}); + my $image_path = ''; - # check if the image file exists. If the file doesn't exist and we use H264 try to extract it from .mp4 video - if ( $analyse && -r $analyse_image_path ) { - $image_path = $analyse_image_path; - } elsif ( -r $capture_image_path ) { - $image_path = $capture_image_path; - } elsif ( -e $video_path ) { - if ( !system("ffmpeg -y -v 0 -i ".$video_path." -vf 'select=gte(n\\,".$frame->{FrameId}."),setpts=PTS-STARTPTS' -vframes 1 -f image2 ".$capture_image_path) ) { - $image_path = $capture_image_path; - } +# check if the image file exists. If the file doesn't exist and we use H264 try to extract it from .mp4 video + if ( $analyse && -r $analyse_image_path ) { + $image_path = $analyse_image_path; + } elsif ( -r $capture_image_path ) { + $image_path = $capture_image_path; + } elsif ( -e $video_path ) { + if ( !system('ffmpeg -y -v 0 -i '.$video_path." -vf 'select=gte(n\\,".$frame->{FrameId}."),setpts=PTS-STARTPTS' -vframes 1 -f image2 ".$capture_image_path) ) { + $image_path = $capture_image_path; } - return $image_path; + } + return $image_path; } -sub uploadArchFile -{ - my $filter = shift; - my $event = shift; +sub uploadArchFile { + my $filter = shift; + my $event = shift; - if ( ! $Config{ZM_UPLOAD_HOST} ) - { - Error( "Cannot upload archive as no upload host defined" ); - return( 0 ); - } + if ( ! $Config{ZM_UPLOAD_HOST} ) { + Error( 'Cannot upload archive as no upload host defined' ); + return( 0 ); + } - my $archFile = $event->{MonitorName}.'-'.$event->{Id}; - my $archImagePath = getEventPath( $event ) - ."/" - .( - ( $Config{ZM_UPLOAD_ARCH_ANALYSE} ) - ? '{*analyse,*capture}' - : '*capture' - ) - .".jpg" + my $archFile = $event->{MonitorName}.'-'.$event->{Id}; + my $archImagePath = getEventPath( $event ) + .'/' + .( + ( $Config{ZM_UPLOAD_ARCH_ANALYSE} ) + ? '{*analyse,*capture}' + : '*capture' + ) + .'.jpg' ; - my @archImageFiles = glob($archImagePath); - my $archLocPath; + my @archImageFiles = glob($archImagePath); + my $archLocPath; - my $archError = 0; - if ( $Config{ZM_UPLOAD_ARCH_FORMAT} eq "zip" ) - { - $archFile .= '.zip'; - $archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile; - my $zip = Archive::Zip->new(); - Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" ); + my $archError = 0; + if ( $Config{ZM_UPLOAD_ARCH_FORMAT} eq 'zip' ) { + $archFile .= '.zip'; + $archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile; + my $zip = Archive::Zip->new(); + Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" ); - my $status = &AZ_OK; - foreach my $imageFile ( @archImageFiles ) - { - Debug( "Adding $imageFile\n" ); - my $member = $zip->addFile( $imageFile ); - if ( !$member ) - { - Error( "Unable to add image file $imageFile to zip archive $archLocPath" ); - $archError = 1; - last; - } - $member->desiredCompressionMethod( $Config{ZM_UPLOAD_ARCH_COMPRESS} - ? &COMPRESSION_DEFLATED - : &COMPRESSION_STORED - ); - } - if ( !$archError ) - { - $status = $zip->writeToFileNamed( $archLocPath ); - - if ( $archError = ($status != &AZ_OK) ) - { - Error( "Zip error: $status\n " ); - } - } - else - { - Error( "Error adding images to zip archive $archLocPath, not writing" ); - } + my $status = &AZ_OK; + foreach my $imageFile ( @archImageFiles ) { + Debug( "Adding $imageFile\n" ); + my $member = $zip->addFile( $imageFile ); + if ( !$member ) { + Error( "Unable to add image file $imageFile to zip archive $archLocPath" ); + $archError = 1; + last; + } + $member->desiredCompressionMethod( $Config{ZM_UPLOAD_ARCH_COMPRESS} + ? &COMPRESSION_DEFLATED + : &COMPRESSION_STORED + ); } - elsif ( $Config{ZM_UPLOAD_ARCH_FORMAT} eq "tar" ) - { - if ( $Config{ZM_UPLOAD_ARCH_COMPRESS} ) - { - $archFile .= '.tar.gz'; - } - else - { - $archFile .= '.tar'; - } - $archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile; - Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" ); + if ( !$archError ) { + $status = $zip->writeToFileNamed( $archLocPath ); - if ( $archError = !Archive::Tar->create_archive( - $archLocPath, - $Config{ZM_UPLOAD_ARCH_COMPRESS}, - @archImageFiles - ) - ) - { - Error( "Tar error: ".Archive::Tar->error()."\n " ); - } + if ( $archError = ($status != &AZ_OK) ) { + Error( "Zip error: $status\n " ); + } + } else { + Error( "Error adding images to zip archive $archLocPath, not writing" ); } + } elsif ( $Config{ZM_UPLOAD_ARCH_FORMAT} eq 'tar' ) { + if ( $Config{ZM_UPLOAD_ARCH_COMPRESS} ) { + $archFile .= '.tar.gz'; + } else { + $archFile .= '.tar'; + } + $archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile; + Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" ); - if ( $archError ) - { + if ( $archError = !Archive::Tar->create_archive( + $archLocPath, + $Config{ZM_UPLOAD_ARCH_COMPRESS}, + @archImageFiles + ) + ) { + Error( 'Tar error: '.Archive::Tar->error()."\n " ); + } + } + + if ( $archError ) { + return( 0 ); + } else { + if ( $Config{ZM_UPLOAD_PROTOCOL} eq 'ftp' ) { + Info( 'Uploading to '.$Config{ZM_UPLOAD_HOST}." using FTP\n" ); + my $ftp = Net::FTP->new( + $Config{ZM_UPLOAD_HOST}, + Timeout=>$Config{ZM_UPLOAD_TIMEOUT}, + Passive=>$Config{ZM_UPLOAD_FTP_PASSIVE}, + Debug=>$Config{ZM_UPLOAD_DEBUG} + ); + if ( !$ftp ) { + Error( "Can't create FTP connection: $@" ); return( 0 ); - } - else - { - if ( $Config{ZM_UPLOAD_PROTOCOL} eq "ftp" ) - { - Info( "Uploading to ".$Config{ZM_UPLOAD_HOST}." using FTP\n" ); - my $ftp = Net::FTP->new( - $Config{ZM_UPLOAD_HOST}, - Timeout=>$Config{ZM_UPLOAD_TIMEOUT}, - Passive=>$Config{ZM_UPLOAD_FTP_PASSIVE}, - Debug=>$Config{ZM_UPLOAD_DEBUG} - ); - if ( !$ftp ) - { - Error( "Can't create FTP connection: $@" ); - return( 0 ); - } - $ftp->login( $Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS} ) - or Error( "FTP - Can't login" ); - $ftp->binary() - or Error( "FTP - Can't go binary" ); - $ftp->cwd( $Config{ZM_UPLOAD_REM_DIR} ) - or Error( "FTP - Can't cwd" ) - if ( $Config{ZM_UPLOAD_REM_DIR} ); - $ftp->put( $archLocPath ) - or Error( "FTP - Can't upload '$archLocPath'" ); - $ftp->quit() - or Error( "FTP - Can't quit" ); - } - else - { - my $host = $Config{ZM_UPLOAD_HOST}; - $host .= ":".$Config{ZM_UPLOAD_PORT} - if $Config{ZM_UPLOAD_PORT}; - Info( "Uploading to ".$host." using SFTP\n" ); - my %sftpOptions = ( host=>$Config{ZM_UPLOAD_HOST}, user=>$Config{ZM_UPLOAD_USER} ); - $sftpOptions{password} = $Config{ZM_UPLOAD_PASS} - if $Config{ZM_UPLOAD_PASS}; - $sftpOptions{port} = $Config{ZM_UPLOAD_PORT} - if $Config{ZM_UPLOAD_PORT}; - $sftpOptions{timeout} = $Config{ZM_UPLOAD_TIMEOUT} - if $Config{ZM_UPLOAD_TIMEOUT}; - my @more_ssh_args; - push @more_ssh_args, '-o'=>'StrictHostKeyChecking=no' - if ! $Config{ZM_UPLOAD_STRICT}; - push @more_ssh_args, '-v' - if $Config{ZM_UPLOAD_DEBUG}; - $sftpOptions{more} = [@more_ssh_args]; - my $sftp = Net::SFTP::Foreign->new( $Config{ZM_UPLOAD_HOST}, %sftpOptions ); - if ( $sftp->error ) - { - Error( "Can't create SFTP connection: ".$sftp->error ); - return( 0 ); - } - $sftp->setcwd( $Config{ZM_UPLOAD_REM_DIR} ) - or Error( "SFTP - Can't setcwd: ".$sftp->error ) - if $Config{ZM_UPLOAD_REM_DIR}; - $sftp->put( $archLocPath, $archFile ) - or Error( "SFTP - Can't upload '$archLocPath': ".$sftp->error ); - } - unlink( $archLocPath ); - my $sql = "update Events set Uploaded = 1 where Id = ?"; - 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() ); - } - return( 1 ); -} - -sub substituteTags -{ - my $text = shift; - my $filter = shift; - my $event = shift; - my $attachments_ref = shift; - - # First we'd better check what we need to get - # We have a filter and an event, do we need any more - # monitor information? - my $need_monitor = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/; - - my $monitor = {}; - if ( $need_monitor ) - { - my $db_now = strftime( "%Y-%m-%d %H:%M:%S", localtime() ); - my $sql = "SELECT - M.Id, - count(E.Id) as EventCount, - count(if(E.Archived,1,NULL)) - as ArchEventCount, - count(if(E.StartTime>'$db_now' - INTERVAL 1 HOUR && E.Archived = 0,1,NULL)) - as HourEventCount, - count(if(E.StartTime>'$db_now' - INTERVAL 1 DAY && E.Archived = 0,1,NULL)) - as DayEventCount, - count(if(E.StartTime>'$db_now' - INTERVAL 7 DAY && E.Archived = 0,1,NULL)) - as WeekEventCount, - count(if(E.StartTime>'$db_now' - INTERVAL 1 MONTH && E.Archived = 0,1,NULL)) - as MonthEventCount - FROM Monitors as M LEFT JOIN Events as E on E.MonitorId = M.Id - WHERE MonitorId = ? - GROUP BY E.MonitorId - ORDER BY Id" - ; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{MonitorId} ) - or Fatal( "Can't execute '$sql': ".$sth->errstr() ); - $monitor = $sth->fetchrow_hashref(); - $sth->finish(); - return() if ( !$monitor ); - } - - # Do we need the image information too? - my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA)%/; - my $first_alarm_frame; - my $max_alarm_frame; - my $max_alarm_score = 0; - if ( $need_images ) - { - my $sql = "SELECT * FROM Frames - WHERE EventId = ? AND Type = 'Alarm' - ORDER BY FrameId" - ; - 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() ); - while( my $frame = $sth->fetchrow_hashref() ) - { - if ( !$first_alarm_frame ) - { - $first_alarm_frame = $frame; - } - if ( $frame->{Score} > $max_alarm_score ) - { - $max_alarm_frame = $frame; - $max_alarm_score = $frame->{Score}; - } - } - $sth->finish(); - } - - my $url = $Config{ZM_URL}; - $text =~ s/%ZP%/$url/g; - $text =~ s/%MN%/$event->{MonitorName}/g; - $text =~ s/%MET%/$monitor->{EventCount}/g; - $text =~ s/%MEH%/$monitor->{HourEventCount}/g; - $text =~ s/%MED%/$monitor->{DayEventCount}/g; - $text =~ s/%MEW%/$monitor->{WeekEventCount}/g; - $text =~ s/%MEM%/$monitor->{MonthEventCount}/g; - $text =~ s/%MEA%/$monitor->{ArchEventCount}/g; - $text =~ s/%MP%/$url?view=watch&mid=$event->{MonitorId}/g; - $text =~ s/%MPS%/$url?view=watchfeed&mid=$event->{MonitorId}&mode=stream/g; - $text =~ s/%MPI%/$url?view=watchfeed&mid=$event->{MonitorId}&mode=still/g; - $text =~ s/%EP%/$url?view=event&mid=$event->{MonitorId}&eid=$event->{Id}/g; - $text =~ s/%EPS%/$url?view=event&mode=stream&mid=$event->{MonitorId}&eid=$event->{Id}/g; - $text =~ s/%EPI%/$url?view=event&mode=still&mid=$event->{MonitorId}&eid=$event->{Id}/g; - $text =~ s/%EI%/$event->{Id}/g; - $text =~ s/%EN%/$event->{Name}/g; - $text =~ s/%EC%/$event->{Cause}/g; - $text =~ s/%ED%/$event->{Notes}/g; - $text =~ s/%ET%/$event->{StartTime}/g; - $text =~ s/%EL%/$event->{Length}/g; - $text =~ s/%EF%/$event->{Frames}/g; - $text =~ s/%EFA%/$event->{AlarmFrames}/g; - $text =~ s/%EST%/$event->{TotScore}/g; - $text =~ s/%ESA%/$event->{AvgScore}/g; - $text =~ s/%ESM%/$event->{MaxScore}/g; - if ( $first_alarm_frame ) - { - $text =~ s/%EPI1%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$first_alarm_frame->{FrameId}/g; - $text =~ s/%EPIM%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$max_alarm_frame->{FrameId}/g; - if ( $attachments_ref && $text =~ s/%EI1%//g ) - { - my $path = generateImage( $event, $first_alarm_frame); - if ( -e $path ) { - push( @$attachments_ref, - { - type=>"image/jpeg", - path=>$path - } - ); - } - } - if ( $attachments_ref && $text =~ s/%EIM%//g ) - { - # Don't attach the same image twice - if ( !@$attachments_ref - || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) - ) - { - my $path = generateImage( $event, $max_alarm_frame); - if ( -e $path ) { - push( @$attachments_ref, - { - type=>"image/jpeg", - path=>$path - } - ); - } - } - } - if ( $attachments_ref && $text =~ s/%EI1A%//g ) - { - my $path = generateImage( $event, $first_alarm_frame, "analyse" ); - if ( -e $path ) { - push( @$attachments_ref, - { - type=>"image/jpeg", - path=>$path - } - ); - } - } - if ( $attachments_ref && $text =~ s/%EIMA%//g ) - { - # Don't attach the same image twice - if ( !@$attachments_ref - || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) - ) - { - my $path = generateImage( $event, $max_alarm_frame, "analyse"); - if ( -e $path ) { - push( @$attachments_ref, - { - type=>"image/jpeg", - path=>$path - } - ); - } - } - } - } - if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} ) - { - if ( $text =~ s/%EV%//g ) - { - my ( $format, $path ) = generateVideo( $filter, $event ); - if ( !$format ) - { - return( undef ); - } - push( @$attachments_ref, { type=>"video/$format", path=>$path } ); - } - if ( $text =~ s/%EVM%//g ) - { - my ( $format, $path ) = generateVideo( $filter, $event, 1 ); - if ( !$format ) - { - return( undef ); - } - push( @$attachments_ref, { type=>"video/$format", path=>$path } ); - } - } - $text =~ s/%FN%/$filter->{Name}/g; - ( my $filter_name = $filter->{Name} ) =~ s/ /+/g; - $text =~ s/%FP%/$url?view=filter&mid=$event->{MonitorId}&filter_name=$filter_name/g; - - return( $text ); -} - -sub sendEmail -{ - my $filter = shift; - my $event = shift; - - if ( ! $Config{ZM_FROM_EMAIL} ) - { - Error( "No 'from' email address defined, not sending email" ); + } + $ftp->login( $Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS} ) + or Error( "FTP - Can't login" ); + $ftp->binary() + or Error( "FTP - Can't go binary" ); + $ftp->cwd( $Config{ZM_UPLOAD_REM_DIR} ) + or Error( "FTP - Can't cwd" ) + if ( $Config{ZM_UPLOAD_REM_DIR} ); + $ftp->put( $archLocPath ) + or Error( "FTP - Can't upload '$archLocPath'" ); + $ftp->quit() + or Error( "FTP - Can't quit" ); + } else { + my $host = $Config{ZM_UPLOAD_HOST}; + $host .= ':'.$Config{ZM_UPLOAD_PORT} + if $Config{ZM_UPLOAD_PORT}; + Info( 'Uploading to '.$host." using SFTP\n" ); + my %sftpOptions = ( host=>$Config{ZM_UPLOAD_HOST}, user=>$Config{ZM_UPLOAD_USER} ); + $sftpOptions{password} = $Config{ZM_UPLOAD_PASS} + if $Config{ZM_UPLOAD_PASS}; + $sftpOptions{port} = $Config{ZM_UPLOAD_PORT} + if $Config{ZM_UPLOAD_PORT}; + $sftpOptions{timeout} = $Config{ZM_UPLOAD_TIMEOUT} + if $Config{ZM_UPLOAD_TIMEOUT}; + my @more_ssh_args; + push @more_ssh_args, '-o'=>'StrictHostKeyChecking=no' + if ! $Config{ZM_UPLOAD_STRICT}; + push @more_ssh_args, '-v' + if $Config{ZM_UPLOAD_DEBUG}; + $sftpOptions{more} = [@more_ssh_args]; + my $sftp = Net::SFTP::Foreign->new( $Config{ZM_UPLOAD_HOST}, %sftpOptions ); + if ( $sftp->error ) { + Error( "Can't create SFTP connection: ".$sftp->error ); return( 0 ); + } + $sftp->setcwd( $Config{ZM_UPLOAD_REM_DIR} ) + or Error( "SFTP - Can't setcwd: ".$sftp->error ) + if $Config{ZM_UPLOAD_REM_DIR}; + $sftp->put( $archLocPath, $archFile ) + or Error( "SFTP - Can't upload '$archLocPath': ".$sftp->error ); } - if ( ! $Config{ZM_EMAIL_ADDRESS} ) - { - Error( "No email address defined, not sending email" ); - return( 0 ); - } - - Info( "Creating notification email\n" ); - - my $subject = substituteTags( $Config{ZM_EMAIL_SUBJECT}, $filter, $event ); - return( 0 ) if ( !$subject ); - my @attachments; - my $body = substituteTags( $Config{ZM_EMAIL_BODY}, $filter, $event, \@attachments ); - return( 0 ) if ( !$body ); - - Info( "Sending notification email '$subject'\n" ); - - eval - { - if ( $Config{ZM_NEW_MAIL_MODULES} ) - { - ### Create the multipart container - my $mail = MIME::Lite->new ( - From => $Config{ZM_FROM_EMAIL}, - To => $Config{ZM_EMAIL_ADDRESS}, - Subject => $subject, - Type => "multipart/mixed" - ); - ### Add the text message part - $mail->attach ( - Type => "TEXT", - Data => $body - ); - ### Add the attachments - foreach my $attachment ( @attachments ) - { - Info( "Attaching '$attachment->{path}\n" ); - $mail->attach( - Path => $attachment->{path}, - Type => $attachment->{type}, - Disposition => "attachment" - ); - } - ### Send the Message - if ( $Config{ZM_SSMTP_MAIL} ) { - my $ssmtp_location = $Config{ZM_SSMTP_PATH}; - if ( !$ssmtp_location ) { - if ( logDebugging() ) { - Debug( "which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message\n" ); - } - $ssmtp_location = qx('which ssmtp'); - } - if ( !$ssmtp_location ) { - Debug( "Can't find ssmtp, trying MIME::Lite->send" ); - MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 ); - $mail->send(); - } else { - ### Send using SSMTP - $mail->send( 'sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS} ); - } - } else { - MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 ); - $mail->send(); - } - } - else - { - my $mail = MIME::Entity->build( - From => $Config{ZM_FROM_EMAIL}, - To => $Config{ZM_EMAIL_ADDRESS}, - Subject => $subject, - Type => (($body=~//)?'text/html':'text/plain'), - Data => $body - ); - - foreach my $attachment ( @attachments ) - { - Info( "Attaching '$attachment->{path}\n" ); - $mail->attach( - Path => $attachment->{path}, - Type => $attachment->{type}, - Encoding => "base64" - ); - } - $mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL} ); - } - }; - if ( $@ ) - { - Error( "Can't send email: $@" ); - return( 0 ); - } - else - { - Info( "Notification email sent\n" ); - } - my $sql = "update Events set Emailed = 1 where Id = ?"; + unlink( $archLocPath ); + my $sql = 'update Events set Uploaded = 1 where Id = ?'; my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) - or Fatal( "Can't execute '$sql': ".$sth->errstr() ); - - return( 1 ); + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + } + return( 1 ); } -sub sendMessage -{ - my $filter = shift; - my $event = shift; +sub substituteTags { + my $text = shift; + my $filter = shift; + my $event = shift; + my $attachments_ref = shift; - if ( ! $Config{ZM_FROM_EMAIL} ) - { - Error( "No 'from' email address defined, not sending message" ); - return( 0 ); - } - if ( ! $Config{ZM_MESSAGE_ADDRESS} ) - { - Error( "No message address defined, not sending message" ); - return( 0 ); - } +# First we'd better check what we need to get +# We have a filter and an event, do we need any more +# monitor information? + my $need_monitor = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/; - Info( "Creating notification message\n" ); - - my $subject = substituteTags( $Config{ZM_MESSAGE_SUBJECT}, $filter, $event ); - return( 0 ) if ( !$subject ); - my @attachments; - my $body = substituteTags( $Config{ZM_MESSAGE_BODY}, $filter, $event, \@attachments ); - return( 0 ) if ( !$body ); - - Info( "Sending notification message '$subject'\n" ); - - eval - { - if ( $Config{ZM_NEW_MAIL_MODULES} ) - { - ### Create the multipart container - my $mail = MIME::Lite->new ( - From => $Config{ZM_FROM_EMAIL}, - To => $Config{ZM_MESSAGE_ADDRESS}, - Subject => $subject, - Type => "multipart/mixed" - ); - ### Add the text message part - $mail->attach ( - Type => "TEXT", - Data => $body - ); - ### Add the attachments - foreach my $attachment ( @attachments ) - { - Info( "Attaching '$attachment->{path}\n" ); - $mail->attach( - Path => $attachment->{path}, - Type => $attachment->{type}, - Disposition => "attachment" - ); - } - ### Send the Message - if ( $Config{ZM_SSMTP_MAIL} ) { - my $ssmtp_location = $Config{ZM_SSMTP_PATH}; - if ( !$ssmtp_location ) { - if ( logDebugging() ) { - Debug( "which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message\n" ); - } - $ssmtp_location = qx('which ssmtp'); - } - if ( !$ssmtp_location ) { - Debug( "Can't find ssmtp, trying MIME::Lite->send" ); - MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 ); - $mail->send(); - } else { - ### Send using SSMTP - $mail->send( 'sendmail', $ssmtp_location, $Config{ZM_MESSAGE_ADDRESS} ); - } - } else { - MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 ); - $mail->send(); - } - } - else - { - my $mail = MIME::Entity->build( - From => $Config{ZM_FROM_EMAIL}, - To => $Config{ZM_MESSAGE_ADDRESS}, - Subject => $subject, - Type => (($body=~//)?'text/html':'text/plain'), - Data => $body - ); - - foreach my $attachment ( @attachments ) - { - Info( "Attaching '$attachment->{path}\n" ); - $mail->attach( - Path => $attachment->{path}, - Type => $attachment->{type}, - Encoding => "base64" - ); - } - $mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, - MailFrom => $Config{ZM_FROM_EMAIL} - ); - } - }; - if ( $@ ) - { - Error( "Can't send email: $@" ); - return( 0 ); - } - else - { - Info( "Notification message sent\n" ); - } - my $sql = "update Events set Messaged = 1 where Id = ?"; + my $monitor = {}; + if ( $need_monitor ) { + my $db_now = strftime( "%Y-%m-%d %H:%M:%S", localtime() ); + my $sql = "SELECT + M.Id, + count(E.Id) as EventCount, + count(if(E.Archived,1,NULL)) + as ArchEventCount, + count(if(E.StartTime>'$db_now' - INTERVAL 1 HOUR && E.Archived = 0,1,NULL)) + as HourEventCount, + count(if(E.StartTime>'$db_now' - INTERVAL 1 DAY && E.Archived = 0,1,NULL)) + as DayEventCount, + count(if(E.StartTime>'$db_now' - INTERVAL 7 DAY && E.Archived = 0,1,NULL)) + as WeekEventCount, + count(if(E.StartTime>'$db_now' - INTERVAL 1 MONTH && E.Archived = 0,1,NULL)) + as MonthEventCount + FROM Monitors as M LEFT JOIN Events as E on E.MonitorId = M.Id + WHERE MonitorId = ? + GROUP BY E.MonitorId + ORDER BY Id" + ; my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{MonitorId} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + $monitor = $sth->fetchrow_hashref(); + $sth->finish(); + return() if ( !$monitor ); + } + +# Do we need the image information too? + my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA)%/; + my $first_alarm_frame; + my $max_alarm_frame; + my $max_alarm_score = 0; + if ( $need_images ) { + my $sql = "SELECT * FROM Frames + WHERE EventId = ? AND Type = 'Alarm' + ORDER BY FrameId" + ; + 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 Fatal( "Can't execute '$sql': ".$sth->errstr() ); + while( my $frame = $sth->fetchrow_hashref() ) { + if ( !$first_alarm_frame ) { + $first_alarm_frame = $frame; + } + if ( $frame->{Score} > $max_alarm_score ) { + $max_alarm_frame = $frame; + $max_alarm_score = $frame->{Score}; + } + } + $sth->finish(); + } - return( 1 ); + my $url = $Config{ZM_URL}; + $text =~ s/%ZP%/$url/g; + $text =~ s/%MN%/$event->{MonitorName}/g; + $text =~ s/%MET%/$monitor->{EventCount}/g; + $text =~ s/%MEH%/$monitor->{HourEventCount}/g; + $text =~ s/%MED%/$monitor->{DayEventCount}/g; + $text =~ s/%MEW%/$monitor->{WeekEventCount}/g; + $text =~ s/%MEM%/$monitor->{MonthEventCount}/g; + $text =~ s/%MEA%/$monitor->{ArchEventCount}/g; + $text =~ s/%MP%/$url?view=watch&mid=$event->{MonitorId}/g; + $text =~ s/%MPS%/$url?view=watchfeed&mid=$event->{MonitorId}&mode=stream/g; + $text =~ s/%MPI%/$url?view=watchfeed&mid=$event->{MonitorId}&mode=still/g; + $text =~ s/%EP%/$url?view=event&mid=$event->{MonitorId}&eid=$event->{Id}/g; + $text =~ s/%EPS%/$url?view=event&mode=stream&mid=$event->{MonitorId}&eid=$event->{Id}/g; + $text =~ s/%EPI%/$url?view=event&mode=still&mid=$event->{MonitorId}&eid=$event->{Id}/g; + $text =~ s/%EI%/$event->{Id}/g; + $text =~ s/%EN%/$event->{Name}/g; + $text =~ s/%EC%/$event->{Cause}/g; + $text =~ s/%ED%/$event->{Notes}/g; + $text =~ s/%ET%/$event->{StartTime}/g; + $text =~ s/%EL%/$event->{Length}/g; + $text =~ s/%EF%/$event->{Frames}/g; + $text =~ s/%EFA%/$event->{AlarmFrames}/g; + $text =~ s/%EST%/$event->{TotScore}/g; + $text =~ s/%ESA%/$event->{AvgScore}/g; + $text =~ s/%ESM%/$event->{MaxScore}/g; + + if ( $first_alarm_frame ) { + $text =~ s/%EPI1%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$first_alarm_frame->{FrameId}/g; + $text =~ s/%EPIM%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$max_alarm_frame->{FrameId}/g; + if ( $attachments_ref && $text =~ s/%EI1%//g ) { + my $path = generateImage( $event, $first_alarm_frame ); + if ( -e $path ) { + push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); + } + } + + if ( $attachments_ref && $text =~ s/%EIM%//g ) { + # Don't attach the same image twice + if ( !@$attachments_ref + || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) + ) { + my $path = generateImage( $event, $max_alarm_frame ); + if ( -e $path ) { + push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); + } + } + } + if ( $attachments_ref && $text =~ s/%EI1A%//g ) { + my $path = generateImage( $event, $first_alarm_frame, 'analyse' ); + if ( -e $path ) { + push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); + } + } + if ( $attachments_ref && $text =~ s/%EIMA%//g ) { + # Don't attach the same image twice + if ( !@$attachments_ref + || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) + ) { + my $path = generateImage( $event, $max_alarm_frame, 'analyse'); + if ( -e $path ) { + push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); + } + } + } + } # end if $first_alarm_frame + + if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} ) { + if ( $text =~ s/%EV%//g ) { + my ( $format, $path ) = generateVideo( $filter, $event ); + if ( !$format ) { + return( undef ); + } + push( @$attachments_ref, { type=>"video/$format", path=>$path } ); + } + if ( $text =~ s/%EVM%//g ) { + my ( $format, $path ) = generateVideo( $filter, $event, 1 ); + if ( !$format ) { + return( undef ); + } + push( @$attachments_ref, { type=>"video/$format", path=>$path } ); + } + } + $text =~ s/%FN%/$filter->{Name}/g; + ( my $filter_name = $filter->{Name} ) =~ s/ /+/g; + $text =~ s/%FP%/$url?view=filter&mid=$event->{MonitorId}&filter_name=$filter_name/g; + + return( $text ); +} # end subsitituteTags + +sub sendEmail { + my $filter = shift; + my $event = shift; + + if ( ! $Config{ZM_FROM_EMAIL} ) { + Error( "No 'from' email address defined, not sending email" ); + return( 0 ); + } + if ( ! $Config{ZM_EMAIL_ADDRESS} ) { + Error( 'No email address defined, not sending email' ); + return( 0 ); + } + + Info( "Creating notification email\n" ); + + my $subject = substituteTags( $Config{ZM_EMAIL_SUBJECT}, $filter, $event ); + return( 0 ) if ( !$subject ); + my @attachments; + my $body = substituteTags( $Config{ZM_EMAIL_BODY}, $filter, $event, \@attachments ); + return( 0 ) if ( !$body ); + + Info( "Sending notification email '$subject'\n" ); + + eval { + if ( $Config{ZM_NEW_MAIL_MODULES} ) { +### Create the multipart container + my $mail = MIME::Lite->new ( + From => $Config{ZM_FROM_EMAIL}, + To => $Config{ZM_EMAIL_ADDRESS}, + Subject => $subject, + Type => 'multipart/mixed' + ); +### Add the text message part + $mail->attach ( + Type => 'TEXT', + Data => $body + ); +### Add the attachments + foreach my $attachment ( @attachments ) { + Info( "Attaching '$attachment->{path}\n" ); + $mail->attach( + Path => $attachment->{path}, + Type => $attachment->{type}, + Disposition => 'attachment' + ); + } +### Send the Message + if ( $Config{ZM_SSMTP_MAIL} ) { + my $ssmtp_location = $Config{ZM_SSMTP_PATH}; + if ( !$ssmtp_location ) { + if ( logDebugging() ) { + Debug( "which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message\n" ); + } + $ssmtp_location = qx('which ssmtp'); + } + if ( !$ssmtp_location ) { + Debug( "Can't find ssmtp, trying MIME::Lite->send" ); + MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 ); + $mail->send(); + } else { +### Send using SSMTP + $mail->send( 'sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS} ); + } + } else { + MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 ); + $mail->send(); + } + } else { + my $mail = MIME::Entity->build( + From => $Config{ZM_FROM_EMAIL}, + To => $Config{ZM_EMAIL_ADDRESS}, + Subject => $subject, + Type => (($body=~//)?'text/html':'text/plain'), + Data => $body + ); + + foreach my $attachment ( @attachments ) { + Info( "Attaching '$attachment->{path}\n" ); + $mail->attach( + Path => $attachment->{path}, + Type => $attachment->{type}, + Encoding => 'base64' + ); + } + $mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL} ); + } + }; + if ( $@ ) { + Error( "Can't send email: $@" ); + return( 0 ); + } else { + Info( "Notification email sent\n" ); + } + my $sql = 'update Events set Emailed = 1 where Id = ?'; + 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() ); + + return( 1 ); } -sub executeCommand -{ - my $filter = shift; - my $event = shift; +sub sendMessage { + my $filter = shift; + my $event = shift; - my $event_path = getEventPath( $event ); + if ( ! $Config{ZM_FROM_EMAIL} ) { + Error( 'No 'from' email address defined, not sending message' ); + return( 0 ); + } + if ( ! $Config{ZM_MESSAGE_ADDRESS} ) { + Error( 'No message address defined, not sending message' ); + return( 0 ); + } - my $command = $filter->{AutoExecuteCmd}; - $command .= " $event_path"; - $command = substituteTags( $command, $filter, $event ); + Info( "Creating notification message\n" ); - Info( "Executing '$command'\n" ); - my $output = qx($command); - my $status = $? >> 8; - if ( $status || logDebugging() ) - { - chomp( $output ); - Debug( "Output: $output\n" ); + my $subject = substituteTags( $Config{ZM_MESSAGE_SUBJECT}, $filter, $event ); + return( 0 ) if ( !$subject ); + my @attachments; + my $body = substituteTags( $Config{ZM_MESSAGE_BODY}, $filter, $event, \@attachments ); + return( 0 ) if ( !$body ); + + Info( "Sending notification message '$subject'\n" ); + + eval { + if ( $Config{ZM_NEW_MAIL_MODULES} ) { +### Create the multipart container + my $mail = MIME::Lite->new ( + From => $Config{ZM_FROM_EMAIL}, + To => $Config{ZM_MESSAGE_ADDRESS}, + Subject => $subject, + Type => 'multipart/mixed' + ); +### Add the text message part + $mail->attach ( + Type => 'TEXT', + Data => $body + ); +### Add the attachments + foreach my $attachment ( @attachments ) { + Info( "Attaching '$attachment->{path}\n" ); + $mail->attach( + Path => $attachment->{path}, + Type => $attachment->{type}, + Disposition => 'attachment' + ); + } +### Send the Message + if ( $Config{ZM_SSMTP_MAIL} ) { + my $ssmtp_location = $Config{ZM_SSMTP_PATH}; + if ( !$ssmtp_location ) { + if ( logDebugging() ) { + Debug( "which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message\n" ); + } + $ssmtp_location = qx('which ssmtp'); + } + if ( !$ssmtp_location ) { + Debug( "Can't find ssmtp, trying MIME::Lite->send" ); + MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 ); + $mail->send(); + } else { +### Send using SSMTP + $mail->send( 'sendmail', $ssmtp_location, $Config{ZM_MESSAGE_ADDRESS} ); + } + } else { + MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 ); + $mail->send(); + } + } else { + my $mail = MIME::Entity->build( + From => $Config{ZM_FROM_EMAIL}, + To => $Config{ZM_MESSAGE_ADDRESS}, + Subject => $subject, + Type => (($body=~//)?'text/html':'text/plain'), + Data => $body + ); + + foreach my $attachment ( @attachments ) { + Info( "Attaching '$attachment->{path}\n" ); + $mail->attach( + Path => $attachment->{path}, + Type => $attachment->{type}, + Encoding => 'base64' + ); + } + $mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, + MailFrom => $Config{ZM_FROM_EMAIL} + ); } - if ( $status ) - { - Error( "Command '$command' exited with status: $status\n" ); - return( 0 ); - } - else - { - my $sql = "update Events set Executed = 1 where Id = ?"; - 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() ); - } - return( 1 ); + }; + if ( $@ ) { + Error( "Can't send email: $@" ); + return( 0 ); + } else { + Info( "Notification message sent\n" ); + } + my $sql = 'update Events set Messaged = 1 where Id = ?'; + 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() ); + + return( 1 ); +} + +sub executeCommand { + my $filter = shift; + my $event = shift; + + my $event_path = getEventPath( $event ); + + my $command = $filter->{AutoExecuteCmd}; + $command .= " $event_path"; + $command = substituteTags( $command, $filter, $event ); + + Info( "Executing '$command'\n" ); + my $output = qx($command); + my $status = $? >> 8; + if ( $status || logDebugging() ) { + chomp( $output ); + Debug( "Output: $output\n" ); + } + if ( $status ) { + Error( "Command '$command' exited with status: $status\n" ); + return( 0 ); + } else { + my $sql = 'update Events set Executed = 1 where Id = ?'; + 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() ); + } + return( 1 ); }