diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 48d1a2fcd..650ffb1d5 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -63,7 +63,7 @@ DROP TABLE IF EXISTS `Controls`; CREATE TABLE `Controls` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Local','Remote','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local', + `Type` enum('Local','Remote','Ffmpeg','Libvlc','cURL','WebSite') NOT NULL default 'Local', `Protocol` varchar(64) default NULL, `CanWake` tinyint(3) unsigned NOT NULL default '0', `CanSleep` tinyint(3) unsigned NOT NULL default '0', @@ -892,7 +892,7 @@ INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('4 Wide', '{ "default":{ INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' ); -- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts. -source @ZM_PATH_DATA@/db/triggers.sql +source @PKGDATADIR@/db/triggers.sql -- -- Apply the initial configuration -- diff --git a/db/zm_update-1.31.43.sql b/db/zm_update-1.31.43.sql new file mode 100644 index 000000000..d8d6eaefd --- /dev/null +++ b/db/zm_update-1.31.43.sql @@ -0,0 +1,24 @@ +-- +-- This updates a 1.31.42 database to 1.31.43 +-- +-- Add WebSite enum to Monitor.Type +-- Add Refresh column to Monitors table +-- + +ALTER TABLE `zm`.`Monitors` +CHANGE COLUMN `Type` `Type` ENUM('Local', 'Remote', 'File', 'Ffmpeg', 'Libvlc', 'cURL', 'WebSite') NOT NULL DEFAULT 'Local' ; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'Refresh' + ) > 0, +"SELECT 'Column Refresh exists in Monitors'", +"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 9ef5b2606..c52ccbad9 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -26,7 +26,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.31.42 +Version: 1.31.43 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/distros/ubuntu1204/control b/distros/ubuntu1204/control index 8c9bf2e61..febcb9435 100644 --- a/distros/ubuntu1204/control +++ b/distros/ubuntu1204/control @@ -54,7 +54,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,liburi-encode-perl ,libwww-perl ,libdata-uuid-perl - ,libnumber-bytes-human + ,libnumber-bytes-human-perl ,libfile-slurp-perl ,mysql-client | virtual-mysql-client ,perl-modules diff --git a/docs/faq.rst b/docs/faq.rst index d6694b153..7735ff38f 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -71,6 +71,7 @@ The 1.2 at the start is basically adding 20% on top of the calculation to accoun The math breakdown for 4 cameras running at 1280x960 capture, 50 frame buffer, 24 bit color space: :: + 1280*960 = 1,228,800 (bytes) 1,228,800 * (3 bytes for 24 bit) = 3,686,400 (bytes) 3,686,400 * 50 = 184,320,000 (bytes) diff --git a/docs/installationguide/dedicateddrive.rst b/docs/installationguide/dedicateddrive.rst index 40db1ee93..f0dfadc28 100644 --- a/docs/installationguide/dedicateddrive.rst +++ b/docs/installationguide/dedicateddrive.rst @@ -14,7 +14,7 @@ If you are using an older version of ZoneMinder, please follow the legacy steps **Step 2:** Mount your dedicated drive, partition, or network share to the local filesystem in any folder of your choosing. We recommend you use systemd to manage the mount points. -Instructions on how to accomplish this can be found `here `_ and `here `_. +Instructions on how to accomplish this can be found `here `__ and `here `__. Note that bind mounting ZoneMinder's images folder is optional. Newer version of ZoneMinder write very little, if anything, to the images folder. Verify the dedicated drive, partition, or network share is successfully mounted before proceeding to the next step. diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst index 327089380..12d9ed769 100644 --- a/docs/installationguide/redhat.rst +++ b/docs/installationguide/redhat.rst @@ -45,7 +45,7 @@ The following notes are based on real problems which have occurred by those who How to Install ZoneMinder ------------------------- -ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site `_ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site `_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline: +ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site `__ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site `_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline: :: @@ -65,9 +65,9 @@ The feedback we get from those who use these development packages is extremely h How to Change from Zmrepo to RPM Fusion --------------------------------------- -As mentioned above, the place to get the latest ZoneMinder release is now `RPM Fusion `_. If you are currently using ZoneMinder release packages from Zmrepo, then the following steps will change you over to RPM Fusion: +As mentioned above, the place to get the latest ZoneMinder release is now `RPM Fusion `__. If you are currently using ZoneMinder release packages from Zmrepo, then the following steps will change you over to RPM Fusion: -- Navigate to the `RPM Fusion site `_ and enable RPM Fusion on your system +- Navigate to the `RPM Fusion site `__ and enable RPM Fusion on your system - Now issue the following from the command line: :: @@ -123,7 +123,7 @@ Your build environment is now set up. Build from SRPM *************** -To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site `_. If instead you wish to rebuild the latest source rpm from our master branch then browse the `Zmrepo site `_. +To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site `__. If instead you wish to rebuild the latest source rpm from our master branch then browse the `Zmrepo site `_. For this example, I'll use one of the source rpms from zmrepo: diff --git a/docs/userguide/definemonitor.rst b/docs/userguide/definemonitor.rst index 66e8928d3..8f85feb98 100644 --- a/docs/userguide/definemonitor.rst +++ b/docs/userguide/definemonitor.rst @@ -147,6 +147,23 @@ Keep aspect ratio Orientation As per local devices. +WebSite +^^^^^^^ + +This Source Type allows one to configure an arbitrary website as a non-reocrdable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue. + +Website URL + Enter the full http or https url to the desired website. + +Width (pixels) + Chose a desired width in pixels that gives an acceptable appearance. This may take some expirimentation. + +Height (pixels) + Chose a desired height in pixels that gives an acceptable appearance. This may take some expirimentation. + +Web Site Refresh + If the website in question has static content, optionally enter a time period in seconds for ZoneMinder to refresh the content. + Timestamp Tab ------------- diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index addd6239b..6eb41e57e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -2946,6 +2946,23 @@ our @options = ( type => $types{boolean}, category => 'web', }, + { + name => 'ZM_WEB_XFRAME_WARN', + default => 'yes', + description => 'Warn when website X-Frame-Options is set to sameorigin', + help => q` + When creating a Web Site monitor, if the target web site has + X-Frame-Options set to sameorigin in the header, the site will + not display in ZoneMinder. This is a design feature in most modern + browsers. When this condiction has occured, ZoneMinder will write a + warning to the log file. To get around this, one can install a browser + plugin or extension to ignore X-Frame headers, and then the page will + display properly. Once the plugin or extenstion has ben installed, + the end user may choose to turn this warning off. + `, + type => $types{boolean}, + category => 'web', + }, { name => 'ZM_WEB_H_REFRESH_MAIN', default => '60', diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm index 19a5f8662..8be04ee99 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm @@ -77,39 +77,39 @@ sub zmDbConnect { } my $options = shift; - if ( ( ! defined( $dbh ) ) or ! $dbh->ping() ) { + if ( ( !defined($dbh) ) or ! $dbh->ping() ) { my ( $host, $portOrSocket ) = ( $ZoneMinder::Config::Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); my $socket; if ( defined($portOrSocket) ) { if ( $portOrSocket =~ /^\// ) { - $socket = ";mysql_socket=".$portOrSocket; + $socket = ';mysql_socket='.$portOrSocket; } else { - $socket = ";host=".$host.";port=".$portOrSocket; + $socket = ';host='.$host.';port='.$portOrSocket; } } else { - $socket = ";host=".$Config{ZM_DB_HOST}; + $socket = ';host='.$Config{ZM_DB_HOST}; } - my $sslOptions = ""; + my $sslOptions = ''; if ( $Config{ZM_DB_SSL_CA_CERT} ) { $sslOptions = ';'.join(';', - "mysql_ssl=1", - "mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT}, - "mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY}, - "mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT} + 'mysql_ssl=1', + 'mysql_ssl_ca_file='.$Config{ZM_DB_SSL_CA_CERT}, + 'mysql_ssl_client_key='.$Config{ZM_DB_SSL_CLIENT_KEY}, + 'mysql_ssl_client_cert='.$Config{ZM_DB_SSL_CLIENT_CERT} ); } - $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME} + $dbh = DBI->connect( 'DBI:mysql:database='.$Config{ZM_DB_NAME} .$socket . $sslOptions . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' ) , $Config{ZM_DB_USER} , $Config{ZM_DB_PASS} ); - $dbh->trace( 0 ); + $dbh->trace(0) if $dbh; } - return( $dbh ); -} + return $dbh; +} # end sub zmDbConnect sub zmDbDisconnect { if ( defined( $dbh ) ) { diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index e1f554483..7a7b8d146 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -529,6 +529,8 @@ sub MoveTo { my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint if ( ! $$NewStorage{Id} ) { return "New storage does not have an id. Moving will not happen."; + } elsif ( $$NewStorage{Id} == $$self{StorageId} ) { + return "Event is already located at " . $NewPath; } elsif ( !$NewPath ) { return "New path ($NewPath) is empty."; } elsif ( ! -e $NewPath ) { diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm index 065a2339b..1b158b2ac 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -160,9 +160,12 @@ sub Sql { if ( $term->{attr} =~ /^Monitor/ ) { my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/; $self->{Sql} .= 'M.'.$temp_attr_name; - } elsif ( $term->{attr} =~ /^Server/ ) { - $self->{Sql} .= 'S.'.$term->{attr}; - + } elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) { + $self->{Sql} .= 'M.ServerId'; + } elsif ( $term->{attr} eq 'StorageServerId' ) { + $self->{Sql} .= 'S.ServerId'; + } elsif ( $term->{attr} eq 'FilterServerId' ) { + $self->{Sql} .= $Config{ZM_SERVER_ID}; # StartTime options } elsif ( $term->{attr} eq 'DateTime' ) { $self->{Sql} .= 'E.StartTime'; @@ -208,7 +211,7 @@ sub Sql { foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) { if ( $term->{attr} =~ /^MonitorName/ ) { $value = "'$temp_value'"; - } elsif ( $term->{attr} eq 'ServerId' ) { + } elsif ( $term->{attr} =~ /ServerId/) { Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})"); if ( $temp_value eq 'ZM_SERVER_ID' ) { $value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'"; @@ -241,7 +244,7 @@ sub Sql { } $value = "'$value'"; } - } elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) { + } elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) { if ( $temp_value eq 'NULL' ) { $value = $temp_value; } else { diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm index ef5432693..929a32b2e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm @@ -158,6 +158,7 @@ sub new { ( $this->{fileName} = $0 ) =~ s|^.*/||; $this->{logPath} = $Config{ZM_PATH_LOGS}; $this->{logFile} = $this->{logPath}.'/'.$this->{id}.'.log'; + ($this->{logFile}) = $this->{logFile} =~ /^([\w\.\/]+)$/; $this->{trace} = 0; @@ -207,6 +208,7 @@ sub initialise( @ ) { if ( my $logFile = $this->getTargettedEnv('LOG_FILE') ) { $tempLogFile = $logFile; } + ($tempLogFile) = $tempLogFile =~ /^([\w\.\/]+)$/; my $tempLevel = INFO; my $tempTermLevel = $this->{termLevel}; @@ -581,28 +583,34 @@ sub logPrint { syslog($priorities{$level}, $code.' [%s]', $string); } print($LOGFILE $message) if $level <= $this->{fileLevel}; + print(STDERR $message) if $level <= $this->{termLevel}; + if ( $level <= $this->{databaseLevel} ) { - my $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NULL )'; - $this->{sth} = $this->{dbh}->prepare_cached($sql); - if ( !$this->{sth} ) { - $this->{databaseLevel} = NOLOG; - Error("Can't prepare log entry '$sql': ".$this->{dbh}->errstr()); - } else { - my $res = $this->{sth}->execute($seconds+($microseconds/1000000.0) - , $this->{id} - , $$ - , $level - , $code - , $string - , $this->{fileName} - ); - if ( !$res ) { + if ( ( $this->{dbh} and $this->{dbh}->ping() ) or ( $this->{dbh} = zmDbConnect() ) ) { + + my $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NULL )'; + $this->{sth} = $this->{dbh}->prepare_cached($sql); + if ( !$this->{sth} ) { $this->{databaseLevel} = NOLOG; - Error("Can't execute log entry '$sql': ".$this->{sth}->errstr()); + Error("Can't prepare log entry '$sql': ".$this->{dbh}->errstr()); + } else { + my $res = $this->{sth}->execute($seconds+($microseconds/1000000.0) + , $this->{id} + , $$ + , $level + , $code + , $string + , $this->{fileName} + ); + if ( !$res ) { + $this->{databaseLevel} = NOLOG; + Error("Can't execute log entry '$sql': ".$this->{dbh}->errstr()); + } } + } else { + print(STDERR "Can't log to database: "); } } # end if doing db logging - print(STDERR $message) if $level <= $this->{termLevel}; } # end if level < effectivelevel } diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index 00c796a53..fceb3b440 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -242,6 +242,7 @@ use constant KILL_DELAY => 60; # seconds to wait between sending TERM and sendin our %cmd_hash; our %pid_hash; our %terminating_processes; +our $zm_terminate = 0; sub run { my $fd = 0; @@ -276,9 +277,9 @@ sub run { listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!"); $SIG{CHLD} = \&reaper; - $SIG{INT} = \&shutdownAll; - $SIG{TERM} = \&shutdownAll; - $SIG{ABRT} = \&shutdownAll; + $SIG{INT} = \&shutdown_sig_handler; + $SIG{TERM} = \&shutdown_sig_handler; + $SIG{ABRT} = \&shutdown_sig_handler; $SIG{HUP} = \&logrot; my $rin = ''; @@ -295,12 +296,17 @@ sub run { dPrint(ZoneMinder::Logger::INFO, 'Loading Server record have ' . $$Server{Name}); } - while( 1 ) { + while( !$zm_terminate ) { if ( $Config{ZM_SERVER_ID} ) { if ( ! ( $secs_count % 60 ) ) { - $dbh = zmDbConnect() if ! $dbh->ping(); + Debug("Connecting"); + while ( (!$zm_terminate) and !($dbh and $dbh->ping()) ) { + Warning("Not connected to db ($dbh). Reconnecting"); + $dbh = zmDbConnect(); + } my @cpuload = CpuLoad(); + Debug("UPdating Server record @cpuload"); if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,CpuLoad=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef, 'Running', $cpuload[0], &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) { Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr()); @@ -308,7 +314,9 @@ sub run { } $secs_count += 1; } +Debug("Before select"); my $nfound = select(my $rout = $rin, undef, undef, $timeout); +Debug("Aftere select $nfound"); if ( $nfound > 0 ) { if ( vec($rout, fileno(SERVER), 1) ) { my $paddr = accept(CLIENT, SERVER); @@ -330,7 +338,8 @@ sub run { # Do nothing, this is all we're here for dPrint(ZoneMinder::Logger::WARNING, "Already running, ignoring command '$command'\n"); } elsif ( $command eq 'shutdown' ) { - shutdownAll(); + # Breka out of while loop + last; } elsif ( $command eq 'check' ) { check($daemon, @args); } elsif ( $command eq 'status' ) { @@ -346,14 +355,13 @@ sub run { } close(CLIENT); } else { - Fatal('Bogus descriptor'); + Error('Bogus descriptor'); } } elsif ( $nfound < 0 ) { if ( $! == EINTR ) { # Dead child, will be reaped #print( "Probable dead child\n" ); # See if it needs to start up again - restartPending(); } elsif ( $! == EPIPE ) { Error("Can't select: $!"); } else { @@ -361,12 +369,15 @@ sub run { } } else { #print( "Select timed out\n" ); - restartPending(); } +Debug("restartPending"); + restartPending(); +Debug("check_for_processes_to_kill"); check_for_processes_to_kill(); } # end while + dPrint(ZoneMinder::Logger::INFO, 'Server exiting at ' .strftime( '%y/%m/%d %H:%M:%S', localtime() ) ."\n" @@ -377,9 +388,7 @@ sub run { Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr()); } } - unlink(main::SOCK_FILE) or Error('Unable to unlink ' . main::SOCK_FILE .". Error message was: $!") if ( -e main::SOCK_FILE ); - unlink(ZM_PID) or Error('Unable to unlink ' . ZM_PID .". Error message was: $!") if ( -e ZM_PID ); - exit(); + shutdownAll(); } sub cPrint { @@ -426,10 +435,12 @@ sub start { } my $sigset = POSIX::SigSet->new; - my $blockset = POSIX::SigSet->new( SIGCHLD ); + my $blockset = POSIX::SigSet->new(SIGCHLD); + Debug("Blocking SIGCHLD"); sigprocmask(SIG_BLOCK, $blockset, $sigset) or Fatal("Can't block SIGCHLD: $!"); + Debug("forking"); if ( my $cpid = fork() ) { - logReinit(); + #logReinit(); $process->{pid} = $cpid; $process->{started} = time(); @@ -442,6 +453,7 @@ sub start { $cmd_hash{$process->{command}} = $pid_hash{$cpid} = $process; sigprocmask(SIG_SETMASK, $sigset) or Fatal("Can't restore SIGCHLD: $!"); + Debug("unblocko child"); } elsif ( defined($cpid) ) { # Force reconnection to the db. $dbh = zmDbConnect(1); @@ -527,16 +539,18 @@ sub check_for_processes_to_kill { my $sigset = POSIX::SigSet->new; my $blockset = POSIX::SigSet->new(SIGCHLD); sigprocmask(SIG_BLOCK, $blockset, $sigset) or die "dying at block...\n"; - foreach my $command ( %terminating_processes ) { + foreach my $command ( keys %terminating_processes ) { my $process = $cmd_hash{$command}; -Debug("Have process $command at pid $$process{pid} $$process{term_sent_at}"); - if ( $$process{term_sent_at} - time > KILL_DELAY ) { + my $now = time; + Debug("Have process $command at pid $$process{pid} $$process{term_sent_at} - $now = " . ( $$process{term_sent_at} - $now ) ); + if ( $$process{term_sent_at} and ( $$process{term_sent_at} - $now > KILL_DELAY ) ) { dPrint(ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at " .strftime('%y/%m/%d %H:%M:%S', localtime()) .' after ' . KILL_DELAY . ' seconds.' ." Sending KILL to pid $$process{pid}\n" ); kill('KILL', $$process{pid}); + delete $terminating_processes{$command}; } } sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n"; @@ -595,8 +609,14 @@ sub logrot { } } +sub shutdown_sig_handler { + $zm_terminate = 1; +} + sub reaper { my $saved_status = $!; + + # Wait for a child to terminate while ( (my $cpid = waitpid(-1, WNOHANG)) > 0 ) { my $status = $?; @@ -620,8 +640,8 @@ sub reaper { my $out_str = "'$process->{command}' "; if ( $exit_signal ) { + # 15 == TERM, 14 == ALARM if ( $exit_signal == 15 || $exit_signal == 14 ) { -# TERM or ALRM $out_str .= 'exited'; } else { $out_str .= 'crashed'; @@ -659,22 +679,26 @@ sub reaper { $process->{delay} = $Config{ZM_MAX_RESTART_DELAY}; } } + Debug("Delay for $$process{command} is now $$process{delay}"); } else { delete $cmd_hash{$$process{command}}; } - } + } # end while waitpid $SIG{CHLD} = \&reaper; $! = $saved_status; +Debug("Leaving reaper"); } sub restartPending { -# Restart any pending processes - foreach my $process ( values( %cmd_hash ) ) { +# Restart any pending processes, we list them first because cmd_hash may change in foreach + my @processes = values %cmd_hash; + foreach my $process ( @processes ) { if ( $process->{pending} && $process->{pending} <= time() ) { dPrint(ZoneMinder::Logger::INFO, "Starting pending process, $process->{command}\n"); start($process->{daemon}, @{$process->{args}}); } } + dPrint(ZoneMinder::Logger::INFO, "done restartPending"); } sub shutdownAll { @@ -683,9 +707,14 @@ sub shutdownAll { next if ! $pid_hash{$pid}; send_stop(1, $pid_hash{$pid}); } - while ( %terminating_processes ) { + my $count= KILL_DELAY; + while ( (keys %terminating_processes) and $count) { check_for_processes_to_kill(); - sleep(1) if %terminating_processes; + if ( %terminating_processes ) { + Debug("Still " . %terminating_processes . ' to die. count is: ' . $count . ' sleeping'); + sleep(1); + $count --; + } } dPrint(ZoneMinder::Logger::INFO, "Server shutdown at " .strftime('%y/%m/%d %H:%M:%S', localtime()) diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 3bde3e859..fa59d91e0 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -94,7 +94,7 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|) : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}) ; -logInit(); +logInit($filter_id?(id=>'zmfilter_'.$filter_id):()); sub HupHandler { Info("Received HUP, reloading"); &ZoneMinder::Logger::logHupHandler(); @@ -154,38 +154,38 @@ my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL}; my $event_id = 0; if ( ! EVENT_PATH ) { - Error( "No event path defined. Config was $Config{ZM_DIR_EVENTS}\n" ); + Error("No event path defined. Config was $Config{ZM_DIR_EVENTS}\n"); die; } # In future, should not be neccessary wrt StorageAreas chdir( EVENT_PATH ); -# SHould not be neccessary... but nice to get a local var. What if it fails? +# Should not be neccessary... but nice to get a local var. What if it fails? my $dbh = zmDbConnect(); if ( $filter_name ) { - Info( "Scanning for events using filter '$filter_name'\n" ); + Info("Scanning for events using filter '$filter_name'\n"); } elsif ( $filter_id ) { - Info( "Scanning for events using filter id '$filter_id'\n" ); + Info("Scanning for events using filter id '$filter_id'\n"); } else { - Info( "Scanning for events using all filters\n" ); + Info("Scanning for events using all filters\n"); } if ( ! ( $filter_name or $filter_id ) ) { - Debug("Sleeping due to start delay: " . START_DELAY . ' seconds...' ); - sleep( START_DELAY ); + Debug("Sleeping due to start delay: " . START_DELAY . ' seconds...'); + sleep(START_DELAY); } my @filters; my $last_action = 0; -while( ! $zm_terminate ) { +while( !$zm_terminate ) { my $now = time; if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) { - Debug( "Reloading filters\n" ); + Debug("Reloading filters\n"); $last_action = $now; - @filters = getFilters( { Name=>$filter_name, Id=>$filter_id } ); + @filters = getFilters({ Name=>$filter_name, Id=>$filter_id }); } foreach my $filter ( @filters ) { @@ -195,16 +195,16 @@ while( ! $zm_terminate ) { my ( $id ) = $$filter{Id} =~ /(\d+)/; Debug("Running concurrent filter process $proc --filter_id $$filter{Id} => $id for $$filter{Name}"); - system( qq`$proc --filter "$$filter{Name}" &` ); + system(qq`$proc --filter "$$filter{Name}" &`); } else { - checkFilter( $filter ); + checkFilter($filter); } } last if $filter_name or $filter_id or $zm_terminate; - Debug( "Sleeping for $delay seconds\n" ); - sleep( $delay ); + Debug("Sleeping for $delay seconds\n"); + sleep($delay); } sub getFilters { @@ -232,18 +232,18 @@ sub getFilters { or UpdateDiskSpace = 1 or AutoMove = 1 ) ORDER BY Name'; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Unable to prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( @sql_values ) - or Fatal( "Unable to execute '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached($sql) + or Fatal("Unable to prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute(@sql_values) + or Fatal("Unable to execute '$sql': ".$dbh->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 $filter = new ZoneMinder::Filter($$db_filter{Id}, $db_filter); + Debug("Found filter '$db_filter->{Name}'"); # The undef here is to make sure the Sql gets regenerated because the Filter object may be cached my $filter_sql = $filter->Sql(undef); if ( ! $filter_sql ) { - Error( "Error parsing Sql. skipping filter '$db_filter->{Name}'\n" ); + Error("Error parsing Sql. skipping filter '$db_filter->{Name}'"); next FILTER; } push @filters, $filter; @@ -252,7 +252,7 @@ FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) { if ( ! @filters ) { Warning("No filter found for $sql with values(@sql_values)"); } else { - Debug( "Got " . @filters . " filters" ); + Debug("Got " . @filters . " filters"); } return @filters; @@ -283,56 +283,56 @@ sub checkFilter { foreach my $event ( @Events ) { last if $zm_terminate; - my $Event = new ZoneMinder::Event( $$event{Id}, $event ); + my $Event = new ZoneMinder::Event($$event{Id}, $event); - Debug( "Checking event $event->{Id}" ); + Debug("Checking event $event->{Id}"); my $delete_ok = !undef; $dbh->ping(); if ( $filter->{AutoArchive} ) { - Info( "Archiving event $event->{Id}" ); + Info("Archiving event $event->{Id}"); # 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( "Unable toprepare '$sql': ".$dbh->errstr() ); + or Fatal("Unable to prepare '$sql': ".$dbh->errstr()); my $res = $sth->execute( $event->{Id} ) - or Error( "Unable toexecute '$sql': ".$sth->errstr() ); + or Error("Unable to execute '$sql': ".$dbh->errstr()); } if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) { if ( !$event->{Videoed} ) { - $delete_ok = undef if ( !generateVideo( $filter, $event ) ); + $delete_ok = undef if !generateVideo($filter, $event); } } if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) { if ( !$event->{Emailed} ) { - $delete_ok = undef if ( !sendEmail( $filter, $Event ) ); + $delete_ok = undef if !sendEmail($filter, $Event); } } if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) { if ( !$event->{Messaged} ) { - $delete_ok = undef if ( !sendMessage( $filter, $event ) ); + $delete_ok = undef if !sendMessage($filter, $event); } } if ( $Config{ZM_OPT_UPLOAD} && $filter->{AutoUpload} ) { if ( !$event->{Uploaded} ) { - $delete_ok = undef if ( !uploadArchFile( $filter, $event ) ); + $delete_ok = undef if !uploadArchFile($filter, $event); } } if ( $filter->{AutoExecute} ) { if ( !$event->{Executed} ) { - $delete_ok = undef if ( !executeCommand( $filter, $event ) ); + $delete_ok = undef if !executeCommand($filter, $event); } } if ( $filter->{AutoDelete} ) { if ( $delete_ok ) { $Event->delete(); } else { - Error( "Unable toto delete event $event->{Id} as previous operations failed\n" ); + Error("Unable toto delete event $event->{Id} as previous operations failed"); } } # end if AutoDelete if ( $filter->{AutoMove} ) { - my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} ); - $_ = $Event->MoveTo( $NewStorage ); + my $NewStorage = new ZoneMinder::Storage($filter->{AutoMoveTo}); + $_ = $Event->MoveTo($NewStorage); Error($_) if $_; } @@ -364,7 +364,7 @@ sub generateVideo { my $scale = $event->{DefaultScale}/100; my $format; - my @ffmpeg_formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} ); + my @ffmpeg_formats = split(/\s+/, $Config{ZM_FFMPEG_FORMATS}); my $default_video_format; my $default_phone_format; foreach my $ffmpeg_format( @ffmpeg_formats ) { @@ -408,10 +408,10 @@ sub generateVideo { return 0; } else { my $sql = 'UPDATE Events SET Videoed = 1 WHERE Id = ?'; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Unable to prepare '$sql': ".$dbh->errstr()); - my $res = $sth->execute( $event->{Id} ) - or Fatal("Unable toexecute '$sql': ".$sth->errstr()); + my $sth = $dbh->prepare_cached($sql) + or Fatal("Unable to prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($event->{Id}) + or Fatal("Unable toexecute '$sql': ".$dbh->errstr()); if ( wantarray() ) { return( $format, $output ); } @@ -463,12 +463,12 @@ sub uploadArchFile { my $event = shift; if ( ! $Config{ZM_UPLOAD_HOST} ) { - Error( 'Cannot upload archive as no upload host defined' ); + Error('Cannot upload archive as no upload host defined'); return( 0 ); } my $archFile = $event->{MonitorName}.'-'.$event->{Id}; - my $archImagePath = getEventPath( $event ) + my $archImagePath = getEventPath($event) .'/' .( ( $Config{ZM_UPLOAD_ARCH_ANALYSE} ) @@ -485,14 +485,14 @@ sub uploadArchFile { $archFile .= '.zip'; $archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile; my $zip = Archive::Zip->new(); - Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" ); + 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 ); + Debug("Adding $imageFile\n"); + my $member = $zip->addFile($imageFile); if ( !$member ) { - Error( "Unable toto add image file $imageFile to zip archive $archLocPath" ); + Error("Unable toto add image file $imageFile to zip archive $archLocPath"); $archError = 1; last; } @@ -505,10 +505,10 @@ sub uploadArchFile { $status = $zip->writeToFileNamed( $archLocPath ); if ( $archError = ($status != &AZ_OK) ) { - Error( "Zip error: $status\n " ); + Error("Zip error: $status"); } } else { - Error( "Error adding images to zip archive $archLocPath, not writing" ); + Error("Error adding images to zip archive $archLocPath, not writing"); } } elsif ( $Config{ZM_UPLOAD_ARCH_FORMAT} eq 'tar' ) { if ( $Config{ZM_UPLOAD_ARCH_COMPRESS} ) { @@ -517,7 +517,7 @@ sub uploadArchFile { $archFile .= '.tar'; } $archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile; - Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" ); + Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n"); if ( $archError = !Archive::Tar->create_archive( $archLocPath, @@ -525,7 +525,7 @@ sub uploadArchFile { @archImageFiles ) ) { - Error( 'Tar error: '.Archive::Tar->error()."\n " ); + Error('Tar error: '.Archive::Tar->error()); } } @@ -533,7 +533,7 @@ sub uploadArchFile { return( 0 ); } else { if ( $Config{ZM_UPLOAD_PROTOCOL} eq 'ftp' ) { - Info( 'Uploading to '.$Config{ZM_UPLOAD_HOST}." using FTP\n" ); + Info('Uploading to '.$Config{ZM_UPLOAD_HOST}." using FTP"); my $ftp = Net::FTP->new( $Config{ZM_UPLOAD_HOST}, Timeout=>$Config{ZM_UPLOAD_TIMEOUT}, @@ -541,58 +541,56 @@ sub uploadArchFile { Debug=>$Config{ZM_UPLOAD_DEBUG} ); if ( !$ftp ) { - Error( "Unable tocreate FTP connection: $@" ); - return( 0 ); + Error("Unable tocreate FTP connection: $@"); + return 0; } - $ftp->login( $Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS} ) - or Error( "FTP - Unable tologin" ); + $ftp->login($Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS}) + or Error("FTP - Unable tologin"); $ftp->binary() - or Error( "FTP - Unable togo binary" ); - $ftp->cwd( $Config{ZM_UPLOAD_REM_DIR} ) - or Error( "FTP - Unable tocwd" ) + or Error("FTP - Unable togo binary"); + $ftp->cwd($Config{ZM_UPLOAD_REM_DIR}) + or Error("FTP - Unable tocwd") if ( $Config{ZM_UPLOAD_REM_DIR} ); $ftp->put( $archLocPath ) - or Error( "FTP - Unable toupload '$archLocPath'" ); + or Error("FTP - Unable toupload '$archLocPath'"); $ftp->quit() - or Error( "FTP - Unable toquit" ); + or Error("FTP - Unable toquit"); } 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}; + $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} + ($Config{ZM_UPLOAD_PASS} ? (password=>$Config{ZM_UPLOAD_PASS}) : ()), + ($Config{ZM_UPLOAD_PORT} ? (port=>$Config{ZM_UPLOAD_PORT}) : ()), + ($Config{ZM_UPLOAD_TIMEOUT} ? (timeout=>$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 ); + my $sftp = Net::SFTP::Foreign->new($Config{ZM_UPLOAD_HOST}, %sftpOptions); if ( $sftp->error ) { - Error( "Unable tocreate SFTP connection: ".$sftp->error ); - return( 0 ); + Error("Unable tocreate SFTP connection: ".$sftp->error); + return 0; } - $sftp->setcwd( $Config{ZM_UPLOAD_REM_DIR} ) - or Error( "SFTP - Unable tosetcwd: ".$sftp->error ) + $sftp->setcwd($Config{ZM_UPLOAD_REM_DIR}) + or Error("SFTP - Unable to setcwd: ".$sftp->error) if $Config{ZM_UPLOAD_REM_DIR}; - $sftp->put( $archLocPath, $archFile ) - or Error( "SFTP - Unable toupload '$archLocPath': ".$sftp->error ); + $sftp->put($archLocPath, $archFile) + or Error("SFTP - Unable to upload '$archLocPath': ".$sftp->error); } - unlink( $archLocPath ); + unlink($archLocPath); my $sql = 'UPDATE Events SET Uploaded = 1 WHERE Id = ?'; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) - or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached($sql) + or Fatal("Unable toprepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($event->{Id}) + or Fatal("Unable toexecute '$sql': ".$dbh->errstr()); } - return( 1 ); -} + return 1; +} # end sub uploadArchFile sub substituteTags { my $text = shift; @@ -613,14 +611,13 @@ sub substituteTags { my $max_alarm_frame; my $max_alarm_score = 0; if ( $need_images ) { - my $sql = "SELECT * FROM Frames + my $sql = q`SELECT * FROM Frames WHERE EventId = ? AND Type = 'Alarm' - ORDER BY FrameId" - ; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $Event->{Id} ) - or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); + ORDER BY FrameId`; + my $sth = $dbh->prepare_cached($sql) + or Fatal("Unable toprepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($Event->{Id}) + or Fatal( "Unable toexecute '$sql': ".$dbh->errstr()); my $rows = 0; while( my $frame = $sth->fetchrow_hashref() ) { if ( !$first_alarm_frame ) { @@ -646,8 +643,8 @@ sub substituteTags { $text =~ s/%MEM%/$Monitor->{MonthEvents}/g; $text =~ s/%MEA%/$Monitor->{ArchivedEvents}/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/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/g; + $text =~ s/%MPI%/$url?view=watch&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; @@ -697,13 +694,13 @@ sub substituteTags { 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} ) + || ($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 }; } else { - Warning("No image for EIMA"); + Warning('No image for EIMA'); } } } @@ -737,7 +734,7 @@ sub sendEmail { my $Event = shift; if ( ! $Config{ZM_FROM_EMAIL} ) { - Error("No 'from' email address defined, not sending email"); + Error('No from email address defined, not sending email'); return 0; } if ( ! $Config{ZM_EMAIL_ADDRESS} ) { @@ -745,7 +742,7 @@ sub sendEmail { return 0; } - Info("Creating notification email\n"); + Info('Creating notification email'); my $subject = substituteTags($Config{ZM_EMAIL_SUBJECT}, $filter, $Event); return 0 if !$subject; @@ -753,7 +750,7 @@ sub sendEmail { my $body = substituteTags($Config{ZM_EMAIL_BODY}, $filter, $Event, \@attachments); return 0 if !$body; - Info("Sending notification email '$subject'\n"); + Info("Sending notification email '$subject'"); eval { if ( $Config{ZM_NEW_MAIL_MODULES} ) { @@ -771,7 +768,7 @@ sub sendEmail { ); ### Add the attachments foreach my $attachment ( @attachments ) { - Info( "Attaching '$attachment->{path}\n" ); + Info( "Attaching '$attachment->{path}'" ); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, @@ -783,20 +780,20 @@ sub sendEmail { 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" ); + Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message"); } $ssmtp_location = qx('which ssmtp'); } if ( !$ssmtp_location ) { - Debug( "Unable tofind ssmtp, trying MIME::Lite->send" ); - MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 ); + Debug('Unable tofind 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} ); + $mail->send('sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS}); } } else { - MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 ); + MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60); $mail->send(); } } else { @@ -809,29 +806,29 @@ sub sendEmail { ); foreach my $attachment ( @attachments ) { - Info( "Attaching '$attachment->{path}\n" ); + Info("Attaching '$attachment->{path}'"); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, Encoding => 'base64' ); } - $mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL} ); + $mail->smtpsend(Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL}); } }; if ( $@ ) { - Error( "Unable tosend email: $@" ); - return( 0 ); + Error("Unable tosend email: $@"); + return 0; } else { - Info( "Notification email sent\n" ); + Info('Notification email sent'); } - my $sql = 'update Events set Emailed = 1 where Id = ?'; + my $sql = 'UPDATE Events SET Emailed = 1 WHERE Id = ?'; my $sth = $dbh->prepare_cached($sql) - or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); + or Fatal("Unable toprepare '$sql': ".$dbh->errstr()); my $res = $sth->execute($Event->{Id}) - or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); + or Fatal("Unable toexecute '$sql': ".$dbh->errstr()); - return( 1 ); + return 1; } sub sendMessage { @@ -839,28 +836,28 @@ sub sendMessage { my $event = shift; if ( ! $Config{ZM_FROM_EMAIL} ) { - Error( "No 'from' email address defined, not sending message" ); - return( 0 ); + 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 ); + Error('No message address defined, not sending message'); + return 0; } - Info( "Creating notification message\n" ); + Info('Creating notification message'); - my $subject = substituteTags( $Config{ZM_MESSAGE_SUBJECT}, $filter, $event ); - return( 0 ) if ( !$subject ); + 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 ); + my $body = substituteTags($Config{ZM_MESSAGE_BODY}, $filter, $event, \@attachments); + return 0 if !$body; - Info( "Sending notification message '$subject'\n" ); + Info("Sending notification message '$subject'"); eval { if ( $Config{ZM_NEW_MAIL_MODULES} ) { ### Create the multipart container - my $mail = MIME::Lite->new ( + my $mail = MIME::Lite->new( From => $Config{ZM_FROM_EMAIL}, To => $Config{ZM_MESSAGE_ADDRESS}, Subject => $subject, @@ -873,7 +870,7 @@ sub sendMessage { ); ### Add the attachments foreach my $attachment ( @attachments ) { - Info( "Attaching '$attachment->{path}\n" ); + Info("Attaching '$attachment->{path}"); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, @@ -885,20 +882,20 @@ sub sendMessage { 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" ); + Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message"); } $ssmtp_location = qx('which ssmtp'); } if ( !$ssmtp_location ) { - Debug( 'Unable tofind ssmtp, trying MIME::Lite->send' ); - MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 ); + Debug('Unable tofind 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} ); + $mail->send('sendmail', $ssmtp_location, $Config{ZM_MESSAGE_ADDRESS}); } } else { - MIME::Lite->send( 'smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60 ); + MIME::Lite->send(smtp=>$Config{ZM_EMAIL_HOST}, Timeout=>60); $mail->send(); } } else { @@ -911,59 +908,60 @@ sub sendMessage { ); foreach my $attachment ( @attachments ) { - Info( "Attaching '$attachment->{path}\n" ); + Info("Attaching '$attachment->{path}'"); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, Encoding => 'base64' ); } - $mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, + $mail->smtpsend( + Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL} ); } }; if ( $@ ) { - Error( "Unable tosend email: $@" ); - return( 0 ); + Error("Unable tosend email: $@"); + return 0; } else { - Info( "Notification message sent\n" ); + Info('Notification message sent'); } - my $sql = 'update Events set Messaged = 1 where Id = ?'; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) - or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); + my $sql = 'UPDATE Events SET Messaged = 1 WHERE Id = ?'; + my $sth = $dbh->prepare_cached($sql) + or Fatal("Unable toprepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($event->{Id}) + or Fatal("Unable toexecute '$sql': ".$dbh->errstr()); - return( 1 ); + return 1; } sub executeCommand { my $filter = shift; my $event = shift; - my $event_path = getEventPath( $event ); + my $event_path = getEventPath($event); my $command = $filter->{AutoExecuteCmd}; $command .= " $event_path"; - $command = substituteTags( $command, $filter, $event ); + $command = substituteTags($command, $filter, $event); - Info( "Executing '$command'\n" ); + Info("Executing '$command'"); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { - chomp( $output ); - Debug( "Output: $output\n" ); + chomp($output); + Debug("Output: $output"); } if ( $status ) { - Error( "Command '$command' exited with status: $status\n" ); - return( 0 ); + Error("Command '$command' exited with status: $status"); + return 0; } else { - my $sql = 'update Events set Executed = 1 where Id = ?'; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); + my $sql = 'UPDATE Events SET Executed = 1 WHERE Id = ?'; + my $sth = $dbh->prepare_cached($sql) + or Fatal("Unable to prepare '$sql': ".$dbh->errstr()); my $res = $sth->execute( $event->{Id} ) - or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); + or Fatal("Unable to execute '$sql': ".$dbh->errstr()); } return( 1 ); } diff --git a/scripts/zmpkg.pl.in b/scripts/zmpkg.pl.in index 6b6839bff..62771c7aa 100644 --- a/scripts/zmpkg.pl.in +++ b/scripts/zmpkg.pl.in @@ -211,7 +211,7 @@ if ( $command =~ /^(?:start|restart)$/ ) { my $res = $sth->execute( @values ) or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { - if ( $monitor->{Function} ne 'None' ) { + if ( $monitor->{Function} ne 'None' && $monitor->{Type} ne 'WebSite' ) { if ( $monitor->{Type} eq 'Local' ) { runCommand( "zmdc.pl start zmc -d $monitor->{Device}" ); } else { diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index 3b607306c..61104f55f 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -84,6 +84,7 @@ while( 1 ) { while( my $monitor = $sth->fetchrow_hashref() ) { my $now = time(); next if $monitor->{Function} eq 'None'; + next if $monitor->{Type} eq 'WebSite'; my $restart = 0; if ( zmMemVerify( $monitor ) ) { # Check we have got an image recently diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 2008132ef..3f6b0a807 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -69,7 +69,9 @@ Event::Event( untimedEvent = true; start_time = now; } else if ( start_time.tv_sec > now.tv_sec ) { - Error("StartTime in the future"); + Error("StartTime in the future %d.%d > $d.%d", + start_time.tv_sec, start_time.tv_usec, now.tv_sec, now.tv_usec + ); start_time = now; } @@ -257,7 +259,7 @@ Event::~Event() { "UPDATE Events SET Name='%s %" PRIu64 "', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' WHERE Id = %" PRIu64, monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id ); db_mutex.lock(); - while ( mysql_query(&dbconn, sql) ) { + while ( mysql_query(&dbconn, sql) && !zm_terminate ) { Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn)); db_mutex.unlock(); sleep(1); @@ -422,16 +424,16 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) { #else static char escapedNotes[ZM_SQL_MED_BUFSIZ]; - mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); + mysql_real_escape_string(&dbconn, escapedNotes, notes.c_str(), notes.length()); - snprintf( sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %" PRIu64, escapedNotes, id ); + snprintf(sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %" PRIu64, escapedNotes, id); db_mutex.lock(); - if ( mysql_query( &dbconn, sql ) ) { - Error( "Can't insert event: %s", mysql_error( &dbconn ) ); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't insert event: %s", mysql_error(&dbconn)); } db_mutex.unlock(); #endif - } + } // end if update } void Event::AddFrames( int n_frames, Image **images, struct timeval **timestamps ) { @@ -485,7 +487,7 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %" PRIu64 ", %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); frameCount++; - } + } // end foreach frame if ( frameCount ) { Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames ); @@ -543,7 +545,10 @@ Debug(3, "Writing video"); Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] ); static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf(sql, sizeof(sql), "INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score); + snprintf(sql, sizeof(sql), + "INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score )" + " VALUES ( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", + id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score); db_mutex.lock(); if ( mysql_query(&dbconn, sql) ) { Error("Can't insert frame: %s", mysql_error(&dbconn)); @@ -556,7 +561,7 @@ Debug(3, "Writing video"); // We are writing a Bulk frame if ( frame_type == BULK ) { - snprintf( sql, sizeof(sql), + snprintf(sql, sizeof(sql), "UPDATE Events SET Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %" PRIu64, ( delta_time.positive?"":"-" ), delta_time.sec, delta_time.fsec, @@ -568,7 +573,7 @@ Debug(3, "Writing video"); id ); db_mutex.lock(); - while ( mysql_query(&dbconn, sql) ) { + while ( mysql_query(&dbconn, sql) && !zm_terminate ) { Error("Can't update event: %s", mysql_error(&dbconn)); db_mutex.unlock(); sleep(1); diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 989d2b14d..2578d91d4 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "zm.h" #include "zm_db.h" diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 4d78c45ab..ac841f27b 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -16,6 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include "zm_ffmpeg.h" #include "zm_image.h" diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 2216d723b..4661ce385 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -18,6 +18,7 @@ // #include "zm.h" +#include "zm_signal.h" #if HAVE_LIBAVFORMAT @@ -180,7 +181,7 @@ int FfmpegCamera::Capture( Image &image ) { // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. int frameComplete = false; - while ( !frameComplete ) { + while ( !frameComplete && !zm_terminate) { int avResult = av_read_frame(mFormatContext, &packet); char errbuf[AV_ERROR_MAX_STRING_SIZE]; if ( avResult < 0 ) { @@ -295,12 +296,12 @@ int FfmpegCamera::Capture( Image &image ) { bytes += packet.size; zm_av_packet_unref( &packet ); } // end while ! frameComplete - return 1; + return frameComplete ? 1 : 0; } // FfmpegCamera::Capture int FfmpegCamera::PostCapture() { // Nothing to do here - return( 0 ); + return 0; } int FfmpegCamera::OpenFfmpeg() { diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index 7c063f0f9..3e30d2182 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -543,7 +543,7 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) ); snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line ); - if (mysql_query(&dbconn, sql)) { + if ( mysql_query(&dbconn, sql) ) { Level tempDatabaseLevel = mDatabaseLevel; databaseLevel(NOLOG); Error("Can't insert log entry: sql(%s) error(%s)", sql, mysql_error(&dbconn)); diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index b249d47ba..4b268c016 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "zm.h" #include "zm_db.h" @@ -1245,9 +1246,9 @@ bool Monitor::Analyse() { Info("%s: %d - Analysing at %.2f fps", name, image_count, new_fps); if ( fps != new_fps ) { fps = new_fps; + db_mutex.lock(); static char sql[ZM_SQL_SML_BUFSIZ]; snprintf(sql, sizeof(sql), "INSERT INTO Monitor_Status (MonitorId,AnalysisFPS) VALUES (%d, %.2lf) ON DUPLICATE KEY UPDATE AnalysisFPS = %.2lf", id, fps, fps); - db_mutex.lock(); if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); } diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 3209ceab9..7d5d2de59 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -20,7 +20,7 @@ #define __STDC_FORMAT_MACROS 1 -#include +#include #include #include diff --git a/src/zms.cpp b/src/zms.cpp index 63cbfff5f..e8d20f72f 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "zm.h" #include "zm_db.h" diff --git a/src/zmu.cpp b/src/zmu.cpp index e508b16fb..dd38cb0c0 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -87,6 +87,7 @@ Options for use with monitors: */ #include +#include #include "zm.h" #include "zm_db.h" diff --git a/utils/generate_apache_config.pl b/utils/generate_apache_config.pl index 9d9e94e10..664b4c819 100755 --- a/utils/generate_apache_config.pl +++ b/utils/generate_apache_config.pl @@ -55,6 +55,22 @@ DocumentRoot /usr/share/zoneminder/www qq` ErrorLog $$opts{error_log} +Alias /zm/cache "/var/cache/zoneminder/cache" +Alias /cache "/var/cache/zoneminder/cache" + + Options -Indexes +FollowSymLinks + AllowOverride None + + # Apache 2.4 + Require all granted + + + # Apache 2.2 + Order deny,allow + Allow from all + + + ScriptAlias /zm/cgi-bin/ /usr/lib/zoneminder/cgi-bin/ ScriptAlias /cgi-bin/ /usr/lib/zoneminder/cgi-bin/ @@ -67,7 +83,6 @@ ScriptAlias /cgi-bin/ /usr/lib/zoneminder/cgi-bin/ Alias /zm /usr/share/zoneminder/www - php_flag register_globals off Options +Indexes +FollowSymLinks @@ -81,6 +96,32 @@ Alias /zm /usr/share/zoneminder/www Allow from all + +# For better visibility, the following directives have been migrated from the +# default .htaccess files included with the CakePHP project. +# Parameters not set here are inherited from the parent directive above. + + RewriteEngine on + RewriteRule ^$ app/webroot/ [L] + RewriteRule (.*) app/webroot/$1 [L] + RewriteBase /zm/api + + + + RewriteEngine on + RewriteRule ^$ webroot/ [L] + RewriteRule (.*) webroot/$1 [L] + RewriteBase /zm/api + + + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + RewriteBase /zm/api + + `; if ( $$opts{protocol} eq 'https' ) { $template .= qq` diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 91e671f2e..3794e4645 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -105,10 +105,11 @@ commonprep () { fi # fix 32bit rpm builds - patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch - if [ $? -eq 0 ]; then - patch -p1 < utils/packpack/setarch.patch - fi + # FIXME: breaks arm rpm builds + #patch --dry-run --silent -f -p1 < utils/packpack/setarch.patch + #if [ $? -eq 0 ]; then + # patch -p1 < utils/packpack/setarch.patch + #fi # The rpm specfile requires we download each submodule as a tarball then manually move it into place # Might as well do this for Debian as well, rather than git submodule init diff --git a/version b/version index dd8165476..856307b22 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.42 +1.31.44 diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php index ff6410e15..3f1770f42 100644 --- a/web/api/app/Controller/HostController.php +++ b/web/api/app/Controller/HostController.php @@ -6,14 +6,14 @@ class HostController extends AppController { public $components = array('RequestHandler'); public function daemonCheck($daemon=false, $args=false) { - $string = Configure::read('ZM_PATH_BIN')."/zmdc.pl check"; + $string = Configure::read('ZM_PATH_BIN').'/zmdc.pl check'; if ( $daemon ) { $string .= " $daemon"; if ( $args ) $string .= " $args"; } - $result = exec( $string ); - $result = preg_match( '/running/', $result ); + $result = exec($string); + $result = preg_match('/running/', $result); $this->set(array( 'result' => $result, @@ -31,10 +31,18 @@ class HostController extends AppController { } function getAuthHash() { - $this->set(array( - 'auth_hash'=> generateAuthHash( ZM_AUTH_HASH_IPS ), - '_serialize' => array('auth_hash') - ) ); + if ( $zmOptAuth == '1' ) { + require_once '../../../includes/auth.php'; + $this->set(array( + 'auth_hash' => generateAuthHash(ZM_AUTH_HASH_IPS), + '_serialize' => array('auth_hash') + ) ); + } else { + $this->set(array( + 'auth_hash' => '', + '_serialize' => array('auth_hash') + ) ); + } } // If $mid is set, only return disk usage for that monitor diff --git a/web/api/app/Plugin/Crud b/web/api/app/Plugin/Crud index 1351dde6b..0bd63fb46 160000 --- a/web/api/app/Plugin/Crud +++ b/web/api/app/Plugin/Crud @@ -1 +1 @@ -Subproject commit 1351dde6b4c75b215099ae8bcf5a21d6c6e10298 +Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef diff --git a/web/api/lib/Cake/Console/Templates/skel/Console/cake.bat b/web/api/lib/Cake/Console/Templates/skel/Console/cake.bat index 722febf10..31bde01b1 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Console/cake.bat +++ b/web/api/lib/Cake/Console/Templates/skel/Console/cake.bat @@ -1,30 +1,30 @@ -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:: -:: Bake is a shell script for running CakePHP bake script -:: -:: CakePHP(tm) : Rapid Development Framework (https://cakephp.org) -:: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) -:: -:: Licensed under The MIT License -:: Redistributions of files must retain the above copyright notice. -:: -:: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) -:: @link https://cakephp.org CakePHP(tm) Project -:: @package app.Console -:: @since CakePHP(tm) v 2.0 -:: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - -:: In order for this script to work as intended, the cake\console\ folder must be in your PATH - -@echo. -@echo off - -SET app=%0 -SET lib=%~dp0 - -php -q "%lib%cake.php" -working "%CD% " %* - -echo. - -exit /B %ERRORLEVEL% +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: +:: Bake is a shell script for running CakePHP bake script +:: +:: CakePHP(tm) : Rapid Development Framework (https://cakephp.org) +:: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) +:: +:: Licensed under The MIT License +:: Redistributions of files must retain the above copyright notice. +:: +:: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) +:: @link https://cakephp.org CakePHP(tm) Project +:: @package app.Console +:: @since CakePHP(tm) v 2.0 +:: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +:: In order for this script to work as intended, the cake\console\ folder must be in your PATH + +@echo. +@echo off + +SET app=%0 +SET lib=%~dp0 + +php -q "%lib%cake.php" -working "%CD% " %* + +echo. + +exit /B %ERRORLEVEL% diff --git a/web/api/lib/Cake/Console/cake.bat b/web/api/lib/Cake/Console/cake.bat index 7aa9ad78f..8792173ef 100644 --- a/web/api/lib/Cake/Console/cake.bat +++ b/web/api/lib/Cake/Console/cake.bat @@ -1,28 +1,28 @@ -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:: -:: Bake is a shell script for running CakePHP bake script -:: -:: CakePHP(tm) : Rapid Development Framework (https://cakephp.org) -:: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) -:: -:: Licensed under The MIT License -:: Redistributions of files must retain the above copyright notice. -:: -:: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) -:: @link https://cakephp.org CakePHP(tm) Project -:: @package Cake.Console -:: @since CakePHP(tm) v 1.2.0.5012 -:: @license https://opensource.org/licenses/mit-license.php MIT License -:: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - -@echo off - -SET app=%0 -SET lib=%~dp0 - -php -q "%lib%cake.php" -working "%CD% " %* - -echo. - -exit /B %ERRORLEVEL% +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: +:: Bake is a shell script for running CakePHP bake script +:: +:: CakePHP(tm) : Rapid Development Framework (https://cakephp.org) +:: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) +:: +:: Licensed under The MIT License +:: Redistributions of files must retain the above copyright notice. +:: +:: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) +:: @link https://cakephp.org CakePHP(tm) Project +:: @package Cake.Console +:: @since CakePHP(tm) v 1.2.0.5012 +:: @license https://opensource.org/licenses/mit-license.php MIT License +:: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +@echo off + +SET app=%0 +SET lib=%~dp0 + +php -q "%lib%cake.php" -working "%CD% " %* + +echo. + +exit /B %ERRORLEVEL% diff --git a/web/includes/Event.php b/web/includes/Event.php index 175163890..45662ef3b 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -451,6 +451,11 @@ class Event { $values[] = $this->{'Id'}; dbQuery( $sql, $values ); } + public function link_to($text=null) { + if ( !$text ) + $text = $this->{'Id'}; + return ''.$text.''; + } } # end class diff --git a/web/includes/Storage.php b/web/includes/Storage.php index 352a2249d..57f518a65 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -13,7 +13,7 @@ class Storage { if ( ! $row ) { Error("Unable to load Storage record for Id=" . $IdOrRow ); } - } elseif ( is_array( $IdOrRow ) ) { + } else if ( is_array($IdOrRow) ) { $row = $IdOrRow; } } diff --git a/web/includes/actions.php b/web/includes/actions.php index b112bebb8..8549d3978 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -299,10 +299,12 @@ if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) { continue; } $Monitor = new Monitor( $mid ); - $Monitor->zmaControl('stop'); - $Monitor->zmcControl('stop'); + if ( $Monitor->Type() != 'WebSite' ) { + $Monitor->zmaControl('stop'); + $Monitor->zmcControl('stop'); + } $Monitor->save( $_REQUEST['newMonitor'] ); - if ($Monitor->Function() != 'None' ) { + if ($Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) { $Monitor->zmcControl('start'); if ( $Monitor->Enabled() ) { $Monitor->zmaControl('start'); @@ -330,7 +332,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) { $monitor['Function'] = $newFunction; $monitor['Enabled'] = $newEnabled; - if ( daemonCheck() ) { + if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { $restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled); zmaControl( $monitor, 'stop' ); zmcControl( $monitor, $restart?'restart':'' ); @@ -371,7 +373,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) { } else { dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) ); } - if ( daemonCheck() ) { + if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) { zmaControl( $monitor, 'stop' ); zmcControl( $monitor, 'restart' ); @@ -399,7 +401,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) { } } if($changes>0) { - if ( daemonCheck() ) { + if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { zmaControl( $mid, 'restart' ); } $refreshParent = true; @@ -424,7 +426,7 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) { $deletedZid = 1; } if ( $deletedZid ) { - if ( daemonCheck() ) { + if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { if ( $zone['Type'] == 'Privacy' ) { zmaControl( $mid, 'stop' ); zmcControl( $mid, 'restart' ); @@ -492,8 +494,10 @@ if ( canEdit( 'Monitors' ) ) { if ( $mid ) { # If we change anything that changes the shared mem size, zma can complain. So let's stop first. - zmaControl( $monitor, 'stop' ); - zmcControl( $monitor, 'stop' ); + if ( $monitor['Type'] != 'WebSite' ) { + zmaControl( $monitor, 'stop' ); + zmcControl( $monitor, 'stop' ); + } dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) ); // Groups will be added below if ( isset($changes['Name']) or isset($changes['StorageId']) ) { @@ -606,8 +610,10 @@ if ( canEdit( 'Monitors' ) ) { $new_monitor = new Monitor($mid); //fixDevices(); - $new_monitor->zmcControl('start'); - $new_monitor->zmaControl('start'); + if ( $monitor['Type'] != 'WebSite' ) { + $new_monitor->zmcControl('start'); + $new_monitor->zmaControl('start'); + } if ( $new_monitor->Controllable() ) { require_once( 'control_functions.php' ); diff --git a/web/includes/auth.php b/web/includes/auth.php index 875785359..297dcaec9 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -18,7 +18,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -function userLogin( $username, $password='', $passwordHashed=false ) { +function userLogin($username, $password='', $passwordHashed=false) { global $user, $cookies; $sql = 'SELECT * FROM Users WHERE Enabled=1'; @@ -29,10 +29,10 @@ function userLogin( $username, $password='', $passwordHashed=false ) { } else { $sql .= ' AND Username=? AND Password=password(?)'; } - $sql_values = array( $username, $password ); + $sql_values = array($username, $password); } else { $sql .= ' AND Username=?'; - $sql_values = array( $username ); + $sql_values = array($username); } session_start(); $_SESSION['username'] = $username; @@ -41,8 +41,8 @@ function userLogin( $username, $password='', $passwordHashed=false ) { $_SESSION['password'] = $password; } $_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking - if ( $dbUser = dbFetchOne( $sql, NULL, $sql_values ) ) { - Info( "Login successful for user \"$username\"" ); + if ( $dbUser = dbFetchOne($sql, NULL, $sql_values) ) { + Info("Login successful for user \"$username\""); $_SESSION['user'] = $user = $dbUser; unset($_SESSION['loginFailed']); if ( ZM_AUTH_TYPE == 'builtin' ) { @@ -50,30 +50,30 @@ function userLogin( $username, $password='', $passwordHashed=false ) { } session_regenerate_id(); } else { - Warning( "Login denied for user \"$username\"" ); + Warning("Login denied for user \"$username\""); $_SESSION['loginFailed'] = true; - unset( $user ); + unset($user); } session_write_close(); -} +} # end function userLogin function userLogout() { global $user; - Info( 'User "'.$user['Username'].'" logged out' ); + Info('User "'.$user['Username'].'" logged out'); session_start(); - unset( $_SESSION['user'] ); - unset( $user ); + unset($_SESSION['user']); + unset($user); session_destroy(); } -function getAuthUser( $auth ) { +function getAuthUser($auth) { if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' && !empty($auth) ) { $remoteAddr = ''; if ( ZM_AUTH_HASH_IPS ) { $remoteAddr = $_SERVER['REMOTE_ADDR']; if ( !$remoteAddr ) { - Error( "Can't determine remote address for authentication, using empty string" ); + Error("Can't determine remote address for authentication, using empty string"); $remoteAddr = ''; } } @@ -103,7 +103,7 @@ function getAuthUser( $auth ) { return false; } // end getAuthUser($auth) -function generateAuthHash( $useRemoteAddr ) { +function generateAuthHash($useRemoteAddr) { if ( ZM_OPT_USE_AUTH and ZM_AUTH_RELAY == 'hashed' and isset($_SESSION['username']) and $_SESSION['passwordHash'] ) { # regenerate a hash at half the liftetime of a hash, an hour is 3600 so half is 1800 $time = time(); @@ -119,7 +119,7 @@ function generateAuthHash( $useRemoteAddr ) { $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$local_time[2].$local_time[3].$local_time[4].$local_time[5]; } #Logger::Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] ); - $auth = md5( $authKey ); + $auth = md5($authKey); session_start(); $_SESSION['AuthHash'] = $auth; $_SESSION['AuthHashGeneratedAt'] = $time; @@ -135,22 +135,22 @@ function generateAuthHash( $useRemoteAddr ) { return $auth; } -function visibleMonitor( $mid ) { +function visibleMonitor($mid) { global $user; - return( empty($user['MonitorIds']) || in_array( $mid, explode( ',', $user['MonitorIds'] ) ) ); + return ( empty($user['MonitorIds']) || in_array($mid, explode(',', $user['MonitorIds'])) ); } -function canView( $area, $mid=false ) { +function canView($area, $mid=false) { global $user; - return( ($user[$area] == 'View' || $user[$area] == 'Edit') && ( !$mid || visibleMonitor( $mid ) ) ); + return ( ($user[$area] == 'View' || $user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) ) ); } -function canEdit( $area, $mid=false ) { +function canEdit($area, $mid=false) { global $user; - return( $user[$area] == 'Edit' && ( !$mid || visibleMonitor( $mid ) ) ); + return ( $user[$area] == 'Edit' && ( !$mid || visibleMonitor($mid) )); } ?> diff --git a/web/includes/database.php b/web/includes/database.php index 82fc3c765..2c3a818c1 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -133,7 +133,7 @@ function dbQuery( $sql, $params=NULL ) { } } else { if ( defined('ZM_DB_DEBUG') ) { - Logger::Debug("SQL: $sql values:" . $params?implode(',',$params):'' ); + Logger::Debug("SQL: $sql values:" . ($params?implode(',',$params):'') ); } $result = $dbConn->query($sql); } diff --git a/web/includes/functions.php b/web/includes/functions.php index 2ae484784..f85a04ed6 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -280,6 +280,27 @@ function getImageStill( $id, $src, $width, $height, $title='' ) { return ''.$title.''; } +function getWebSiteUrl( $id, $src, $width, $height, $title='' ) { + # Prevent unsightly warnings when php cannot verify the ssl certificate + stream_context_set_default( [ + 'ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ], + ]); + # The End User can turn off the following warning under Options -> Web + if ( ZM_WEB_XFRAME_WARN ) { + $header = get_headers($src, 1); + # If the target website has set X-Frame-Options, check it for "sameorigin" and warn the end user + if (array_key_exists('X-Frame-Options', $header)) { + $header = $header['X-Frame-Options']; + if ( stripos($header, 'sameorigin') === 0 ) + Warning("Web site $src has X-Frame-Options set to sameorigin. An X-Frame-Options browser plugin is required to display this site."); + } + } + return ''; +} + function outputControlStill( $src, $width, $height, $monitor, $scale, $target ) { ?>
@@ -495,11 +516,11 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { switch( $types[$key] ) { case 'set' : { - if ( is_array( $newValues[$key] ) ) { - if ( join(',',$newValues[$key]) != $values[$key] ) { + if ( is_array($newValues[$key]) ) { + if ( (!isset($values[$key])) or ( join(',',$newValues[$key]) != $values[$key] ) ) { $changes[$key] = "`$key` = ".dbEscape(join(',',$newValues[$key])); } - } elseif ( $values[$key] ) { + } else if ( (!isset($values[$key])) or $values[$key] ) { $changes[$key] = "`$key` = ''"; } break; @@ -548,7 +569,7 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) { } case 'raw' : { - if ( $values[$key] != $value ) { + if ( (!isset($values[$key])) or ($values[$key] != $value) ) { $changes[$key] = $key . ' = '.dbEscape($value); } break; @@ -1004,8 +1025,15 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { $filter['sql'] .= 'M.'.preg_replace('/^Monitor/', '', $terms[$i]['attr']); break; case 'ServerId': + case 'MonitorServerId': $filter['sql'] .= 'M.ServerId'; break; + case 'StorageServerId': + $filter['sql'] .= 'S.ServerId'; + break; + case 'FilterServerId': + $filter['sql'] .= ZM_SERVER_ID; + break; # Unspecified start or end, so assume start, this is to support legacy filters case 'DateTime': $filter['sql'] .= 'E.StartTime'; @@ -1100,6 +1128,9 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { case 'Notes': $value = dbEscape($value); break; + case 'MonitorServerId': + case 'FilterServerId': + case 'StorageServerId': case 'ServerId': if ( $value == 'ZM_SERVER_ID' ) { $value = ZM_SERVER_ID; @@ -2128,8 +2159,14 @@ function getStreamHTML( $monitor, $options = array() ) { $options['buffer'] = $monitor->StreamReplayBuffer(); //Warning("width: " . $options['width'] . ' height: ' . $options['height']. ' scale: ' . $options['scale'] ); + if ( $monitor->Type() == "WebSite" ) { + return getWebSiteUrl( 'liveStream'.$monitor->Id(), $monitor->Path(), + ( isset($options['width']) ? $options['width'] : NULL ), + ( isset($options['height']) ? $options['height'] : NULL ), + $monitor->Name() + ); //FIXME, the width and height of the image need to be scaled. - if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) { + } else if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) { $streamSrc = $monitor->getStreamSrc( array( 'mode'=>'mpeg', 'scale'=>(isset($options['scale'])?$options['scale']:100), diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index 6e97eaab8..9042fe839 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -130,7 +130,9 @@ $SLANG = array( 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'Monitor Name', 'AttrStorageArea' => 'Storage Area', - 'AttrServer' => 'Server', + 'AttrFilterServer' => 'Server Filter is Running On', + 'AttrMonitorServer' => 'Server Monitor is Running On', + 'AttrStorageServer' => 'Server Hosting Storage', 'AttrStateId' => 'Run State', 'AttrName' => 'Name', 'AttrNotes' => 'Notes', @@ -174,8 +176,10 @@ $SLANG = array( 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', 'BadStreamReplayBuffer' => 'Stream replay buffer must be an integer of zero or more', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Bandwidth', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -768,6 +772,7 @@ $SLANG = array( 'Watch' => 'Watch', 'WebColour' => 'Web Colour', 'Web' => 'Web', + 'WebSiteUrl' => 'Website URL', 'Week' => 'Week', 'WhiteBalance' => 'White Balance', 'White' => 'White', diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index b814e9a13..e1808410a 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -244,7 +244,7 @@ function getNavBarHTML($reload = null) { if ( logToDatabase() > Logger::NOLOG ) { if ( ! ZM_RUN_AUDIT ) { # zmaudit can clean the logs, but if we aren't running it, then we should clecan them regularly - dbQuery('DELETE FROM Logs WHERE TimeKey < unix_timestamp( NOW() - interval '.ZM_LOG_DATABASE_LIMIT.')'); + dbQuery('DELETE FROM Logs WHERE TimeKey < unix_timestamp( NOW() - interval '.ZM_LOG_DATABASE_LIMIT.') LIMIT 100'); } echo makePopupLink( '?view=log', 'zmLog', 'log', ''.translate('Log').'' ); } diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index 1d8cf3c9d..330044e7f 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -111,6 +111,9 @@ $status_counts = array(); for ( $i = 0; $i < count($displayMonitors); $i++ ) { $monitor = &$displayMonitors[$i]; if ( ! $monitor['Status'] ) { + if ( $monitor['Type'] == 'WebSite' ) + $monitor['Status'] = 'Running'; + else $monitor['Status'] = 'NotRunning'; } if ( !isset($status_counts[$monitor['Status']]) ) @@ -238,7 +241,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { ?> translate('AttrDiskSpace'), 'SystemLoad' => translate('AttrSystemLoad'), 'StorageId' => translate('AttrStorageArea'), - 'ServerId' => translate('AttrServer'), + 'ServerId' => translate('AttrMonitorServer'), + 'FilterServerId' => translate('AttrFilterServer'), + 'MonitorServerId' => translate('AttrMonitorServer'), + 'StorageServerId' => translate('AttrStorageServer'), 'StateId' => translate('AttrStateId'), ); @@ -268,7 +271,7 @@ for ( $i=0; $i < count($terms); $i++ ) { diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js index 4df73997e..f752dbff7 100644 --- a/web/skins/classic/views/js/filter.js +++ b/web/skins/classic/views/js/filter.js @@ -142,7 +142,9 @@ function parseRows (rows) { inputTds.eq(6).find(':input[value="-"]').prop('disabled', false); } - if (inputTds.eq(2).children().val() == "Archived") { //Archived types + var attr = inputTds.eq(2).children().val(); + + if ( attr == "Archived") { //Archived types inputTds.eq(3).html('equal to'); let archiveSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for (let i = 0; i < archiveTypes.length; i++) { @@ -151,7 +153,7 @@ function parseRows (rows) { let archiveVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(archiveSelect).children().val(archiveVal).chosen({width: "101%"}); - } else if (inputTds.eq(2).children().val().indexOf('Weekday') >= 0) { //Weekday selection + } else if ( attr.indexOf('Weekday') >= 0 ) { //Weekday selection let weekdaySelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for (let i = 0; i < weekdays.length; i++) { weekdaySelect.append(''); @@ -159,7 +161,7 @@ function parseRows (rows) { let weekdayVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(weekdaySelect).children().val(weekdayVal).chosen({width: "101%"}); - } else if (inputTds.eq(2).children().val() == 'StateId') { //Run state + } else if ( attr == 'StateId' ) { //Run state let stateSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for (let key in states) { stateSelect.append(''); @@ -167,8 +169,7 @@ function parseRows (rows) { let stateVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(stateSelect).children().val(stateVal).chosen({width: "101%"}); - - } else if (inputTds.eq(2).children().val() == 'ServerId') { //Select Server + } else if ( attr == 'ServerId' || attr == 'MonitorServerId' || attr == 'StorageServerId' || attr == 'FilterServerId' ) { //Select Server let serverSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for (let key in servers) { serverSelect.append(''); @@ -176,21 +177,15 @@ function parseRows (rows) { let serverVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(serverSelect).children().val(serverVal).chosen({width: "101%"}); - } else if (inputTds.eq(2).children().val() == 'StorageId') { //Choose by storagearea + } else if ( attr == 'StorageId' ) { //Choose by storagearea let storageSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for ( key in storageareas ) { -console.log(key + ' ' + storageareas[key]); storageSelect.append(''); -} -/* - for (let i=0; i < storageareas.length; i++) { - storageSelect.append(''); } -*/ let storageVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(storageSelect).children().val(storageVal).chosen({width: "101%"}); - } else if (inputTds.eq(2).children().val() == 'MonitorName') { //Monitor names + } else if ( attr == 'MonitorName' ) { //Monitor names let monitorSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for (let key in monitors) { monitorSelect.append(''); @@ -208,15 +203,15 @@ console.log(key + ' ' + storageareas[key]); let textVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(textInput).children().val(textVal); } - if (inputTds.eq(2).children().val().endsWith('DateTime')) { //Start/End DateTime + if ( attr.endsWith('DateTime') ) { //Start/End DateTime inputTds.eq(4).children().datetimepicker({timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false}); - } else if (inputTds.eq(2).children().val().endsWith('Date')) { //Start/End Date + } else if ( attr.endsWith('Date') ) { //Start/End Date inputTds.eq(4).children().datepicker({dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false}); - } else if (inputTds.eq(2).children().val().endsWith('Time')) { //Start/End Time + } else if ( attr.endsWith('Time')) { //Start/End Time inputTds.eq(4).children().timepicker({timeFormat: "HH:mm:ss", constrainInput: false}); } - let attr = inputTds.find("[name$='attr\\]']") // Set attr list id and name + attr = inputTds.find("[name$='attr\\]']") // Set attr list id and name let term = attr.attr('name').split(/[[\]]{1,2}/); term.length--; term.shift(); diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index 8871de840..82127dd8e 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -54,14 +54,6 @@ function validateForm( form ) { else if ( monitorNames[form.elements['newMonitor[Name]'].value] ) errors[errors.length] = ""; - if ( form.elements['newMonitor[AnalysisFPSLimit]'].value && !(parseFloat(form.elements['newMonitor[AnalysisFPSLimit]'].value) > 0 ) ) - errors[errors.length] = ""; - if ( form.elements['newMonitor[MaxFPS]'].value && !(parseFloat(form.elements['newMonitor[MaxFPS]'].value) > 0 ) ) - errors[errors.length] = ""; - if ( form.elements['newMonitor[AlarmMaxFPS]'].value && !(parseFloat(form.elements['newMonitor[AlarmMaxFPS]'].value) > 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[RefBlendPerc]'].value || (parseInt(form.elements['newMonitor[RefBlendPerc]'].value) > 100 ) || (parseInt(form.elements['newMonitor[RefBlendPerc]'].value) < 0 ) ) - errors[errors.length] = ""; if ( form.elements['newMonitor[Type]'].value == 'Local' ) { if ( !form.elements['newMonitor[Palette]'].value || !form.elements['newMonitor[Palette]'].value.match( /^\d+$/ ) ) errors[errors.length] = ""; @@ -81,44 +73,63 @@ function validateForm( form ) { } else if ( form.elements['newMonitor[Type]'].value == 'File' ) { if ( !form.elements['newMonitor[Path]'].value ) errors[errors.length] = ""; + } else if ( form.elements['newMonitor[Type]'].value == 'WebSite' ) { + if ( form.elements['newMonitor[Function]'].value != 'Monitor' && form.elements['newMonitor[Function]'].value != 'None') + errors[errors.length] = ""; + if ( form.elements['newMonitor[Path]'].value.search(/^https?:\/\//i) ) + errors[errors.length] = ""; + } + + if ( form.elements['newMonitor[Type]'].value != 'WebSite' ) { + + if ( form.elements['newMonitor[AnalysisFPSLimit]'].value && !(parseFloat(form.elements['newMonitor[AnalysisFPSLimit]'].value) > 0 ) ) + errors[errors.length] = ""; + if ( form.elements['newMonitor[MaxFPS]'].value && !(parseFloat(form.elements['newMonitor[MaxFPS]'].value) > 0 ) ) + errors[errors.length] = ""; + if ( form.elements['newMonitor[AlarmMaxFPS]'].value && !(parseFloat(form.elements['newMonitor[AlarmMaxFPS]'].value) > 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[RefBlendPerc]'].value || (parseInt(form.elements['newMonitor[RefBlendPerc]'].value) > 100 ) || (parseInt(form.elements['newMonitor[RefBlendPerc]'].value) < 0 ) ) + errors[errors.length] = ""; + + if ( !form.elements['newMonitor[Colours]'].value || (parseInt(form.elements['newMonitor[Colours]'].value) != 1 && parseInt(form.elements['newMonitor[Colours]'].value) != 3 && parseInt(form.elements['newMonitor[Colours]'].value) != 4 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[Width]'].value || !(parseInt(form.elements['newMonitor[Width]'].value) > 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[Height]'].value || !(parseInt(form.elements['newMonitor[Height]'].value) > 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[LabelX]'].value || !(parseInt(form.elements['newMonitor[LabelX]'].value) >= 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[LabelY]'].value || !(parseInt(form.elements['newMonitor[LabelY]'].value) >= 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[ImageBufferCount]'].value || !(parseInt(form.elements['newMonitor[ImageBufferCount]'].value) >= 10 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[WarmupCount]'].value || !(parseInt(form.elements['newMonitor[WarmupCount]'].value) >= 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[PreEventCount]'].value || !(parseInt(form.elements['newMonitor[PreEventCount]'].value) >= 0 ) || (parseInt(form.elements['newMonitor[PreEventCount]'].value) > parseInt(form.elements['newMonitor[ImageBufferCount]'].value)) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[PostEventCount]'].value || !(parseInt(form.elements['newMonitor[PostEventCount]'].value) >= 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[StreamReplayBuffer]'].value || !(parseInt(form.elements['newMonitor[StreamReplayBuffer]'].value) >= 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[AlarmFrameCount]'].value || !(parseInt(form.elements['newMonitor[AlarmFrameCount]'].value) > 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[SectionLength]'].value || !(parseInt(form.elements['newMonitor[SectionLength]'].value) >= 30 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[AnalysisUpdateDelay]'].value || !(parseInt(form.elements['newMonitor[AnalysisUpdateDelay]'].value) >= 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[FPSReportInterval]'].value || !(parseInt(form.elements['newMonitor[FPSReportInterval]'].value) >= 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[FrameSkip]'].value || !(parseInt(form.elements['newMonitor[FrameSkip]'].value) >= 0 ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[MotionFrameSkip]'].value || !(parseInt(form.elements['newMonitor[MotionFrameSkip]'].value) >= 0 ) ) + errors[errors.length] = ""; + if ( form.elements['newMonitor[Type]'].value == 'Local' ) + if ( !form.elements['newMonitor[SignalCheckColour]'].value || !form.elements['newMonitor[SignalCheckColour]'].value.match( /^[#0-9a-zA-Z]+$/ ) ) + errors[errors.length] = ""; + if ( !form.elements['newMonitor[WebColour]'].value || !form.elements['newMonitor[WebColour]'].value.match( /^[#0-9a-zA-Z]+$/ ) ) + errors[errors.length] = ""; + } - if ( !form.elements['newMonitor[Colours]'].value || (parseInt(form.elements['newMonitor[Colours]'].value) != 1 && parseInt(form.elements['newMonitor[Colours]'].value) != 3 && parseInt(form.elements['newMonitor[Colours]'].value) != 4 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[Width]'].value || !(parseInt(form.elements['newMonitor[Width]'].value) > 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[Height]'].value || !(parseInt(form.elements['newMonitor[Height]'].value) > 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[LabelX]'].value || !(parseInt(form.elements['newMonitor[LabelX]'].value) >= 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[LabelY]'].value || !(parseInt(form.elements['newMonitor[LabelY]'].value) >= 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[ImageBufferCount]'].value || !(parseInt(form.elements['newMonitor[ImageBufferCount]'].value) >= 10 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[WarmupCount]'].value || !(parseInt(form.elements['newMonitor[WarmupCount]'].value) >= 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[PreEventCount]'].value || !(parseInt(form.elements['newMonitor[PreEventCount]'].value) >= 0 ) || (parseInt(form.elements['newMonitor[PreEventCount]'].value) > parseInt(form.elements['newMonitor[ImageBufferCount]'].value)) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[PostEventCount]'].value || !(parseInt(form.elements['newMonitor[PostEventCount]'].value) >= 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[StreamReplayBuffer]'].value || !(parseInt(form.elements['newMonitor[StreamReplayBuffer]'].value) >= 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[AlarmFrameCount]'].value || !(parseInt(form.elements['newMonitor[AlarmFrameCount]'].value) > 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[SectionLength]'].value || !(parseInt(form.elements['newMonitor[SectionLength]'].value) >= 30 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[AnalysisUpdateDelay]'].value || !(parseInt(form.elements['newMonitor[AnalysisUpdateDelay]'].value) >= 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[FPSReportInterval]'].value || !(parseInt(form.elements['newMonitor[FPSReportInterval]'].value) >= 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[FrameSkip]'].value || !(parseInt(form.elements['newMonitor[FrameSkip]'].value) >= 0 ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[MotionFrameSkip]'].value || !(parseInt(form.elements['newMonitor[MotionFrameSkip]'].value) >= 0 ) ) - errors[errors.length] = ""; - if ( form.elements['newMonitor[Type]'].value == 'Local' ) - if ( !form.elements['newMonitor[SignalCheckColour]'].value || !form.elements['newMonitor[SignalCheckColour]'].value.match( /^[#0-9a-zA-Z]+$/ ) ) - errors[errors.length] = ""; - if ( !form.elements['newMonitor[WebColour]'].value || !form.elements['newMonitor[WebColour]'].value.match( /^[#0-9a-zA-Z]+$/ ) ) - errors[errors.length] = ""; if ( errors.length ) { alert( errors.join( "\n" ) ); diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index 152fdcd9b..b9dc84f00 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -12,6 +12,15 @@ function Monitor( monitorData ) { if ( auth_hash ) this.streamCmdParms += '&auth='+auth_hash; this.streamCmdTimer = null; + this.type = monitorData.type; + this.refresh = monitorData.refresh; + this.start = function( delay ) { + if ( this.streamCmdQuery ) + this.streamCmdTimer = this.streamCmdQuery.delay( delay, this ); + else + console.log("No streamCmdQuery"); + }; + this.setStateClass = function( element, stateClass ) { if ( !element.hasClass( stateClass ) ) { @@ -68,7 +77,7 @@ function Monitor( monitorData ) { else stateClass = "idle"; - if ( !COMPACT_MONTAGE ) { + if ( (!COMPACT_MONTAGE) && (this.type != 'WebSite') ) { $('fpsValue'+this.id).set( 'text', this.status.fps ); $('stateValue'+this.id).set( 'text', stateStrings[this.alarmState] ); this.setStateClass( $('monitorState'+this.id), stateClass ); @@ -137,22 +146,26 @@ function Monitor( monitorData ) { this.streamCmdReq.cancel(); } //console.log("Starting CmdQuery for " + this.connKey ); - this.streamCmdReq.send( this.streamCmdParms+"&command="+CMD_QUERY ); + if ( this.type != 'WebSite' ) { + this.streamCmdReq.send( this.streamCmdParms+"&command="+CMD_QUERY ); + } }; - this.streamCmdReq = new Request.JSON( { - url: this.server_url, - method: 'get', - timeout: 1000+AJAX_TIMEOUT, - onSuccess: this.getStreamCmdResponse.bind( this ), - onTimeout: this.streamCmdQuery.bind( this, true ), - onError: this.onError.bind(this), - onFailure: this.onFailure.bind(this), - link: 'cancel' - } ); + if ( this.type != 'WebSite' ) { + this.streamCmdReq = new Request.JSON( { + url: this.server_url, + method: 'get', + timeout: 1000+AJAX_TIMEOUT, + onSuccess: this.getStreamCmdResponse.bind( this ), + onTimeout: this.streamCmdQuery.bind( this, true ), + onError: this.onError.bind(this), + onFailure: this.onFailure.bind(this), + link: 'cancel' + } ); + console.log("queueing for " + this.id + " " + this.connKey ); + requestQueue.addRequest( "cmdReq"+this.id, this.streamCmdReq ); + } - console.log("queueing for " + this.id + " " + this.connKey ); - requestQueue.addRequest( "cmdReq"+this.id, this.streamCmdReq ); } function selectLayout( element ) { @@ -378,15 +391,26 @@ function cancel_layout(button) { selectLayout('#zmMontageLayout'); } +function reloadWebSite(ndx) { + document.getElementById('imageFeed'+ndx).innerHTML = document.getElementById('imageFeed'+ndx).innerHTML; +} + var monitors = new Array(); function initPage() { -console.log("initPage"); for ( var i = 0; i < monitorData.length; i++ ) { monitors[i] = new Monitor(monitorData[i]); + var delay = Math.round( (Math.random()+0.5)*statusRefreshTimeout ); + var interval = monitors[i].refresh; + monitors[i].start( delay ); + if ( monitors[i].type == 'WebSite' && interval > 0 ) { + setInterval(reloadWebSite, interval*1000, i); + } } selectLayout('#zmMontageLayout'); for ( var i = 0; i < monitorData.length; i++ ) { + if ( monitors[i].type == 'WebSite' ) + continue; var delay = Math.round( (Math.random()+0.75)*statusRefreshTimeout ); console.log("Delay for monitor " + monitorData[i].id + " is " + delay ); monitors[i].streamCmdQuery.delay( delay, monitors[i] ); diff --git a/web/skins/classic/views/js/montage.js.php b/web/skins/classic/views/js/montage.js.php index 7f749d47a..1b67d764b 100644 --- a/web/skins/classic/views/js/montage.js.php +++ b/web/skins/classic/views/js/montage.js.php @@ -36,7 +36,9 @@ monitorData[monitorData.length] = { 'width': Width() ?>, 'height':Height() ?>, 'server_url': 'Server()->Url().$_SERVER['PHP_SELF'] ?>', - 'onclick': function(){createPopup( '?view=watch&mid=Id() ?>', 'zmWatchId() ?>', 'watch', Width(), $monitor->PopupScale() ); ?>, Height(), $monitor->PopupScale() ); ?> );} + 'onclick': function(){createPopup( '?view=watch&mid=Id() ?>', 'zmWatchId() ?>', 'watch', Width(), $monitor->PopupScale() ); ?>, Height(), $monitor->PopupScale() ); ?> );}, + 'type': 'Type() ?>', + 'refresh': 'Refresh() ?>' }; 0 ) { + var myReload = setInterval(reloadWebSite, monitorRefresh*1000); } } diff --git a/web/skins/classic/views/js/watch.js.php b/web/skins/classic/views/js/watch.js.php index 79b48de94..c446703cf 100644 --- a/web/skins/classic/views/js/watch.js.php +++ b/web/skins/classic/views/js/watch.js.php @@ -49,6 +49,8 @@ var monitorId = Id() ?>; var monitorWidth = Width() ?>; var monitorHeight = Height() ?>; var monitorUrl = 'Server()->Url() . ( ZM_MIN_STREAMING_PORT ? ':'. (ZM_MIN_STREAMING_PORT+$monitor->Id()) : '' ) ) ?>'; +var monitorType = 'Type() ) ?>'; +var monitorRefresh = 'Refresh() ) ?>'; var scale = ''; diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 522b45c31..0afb9069c 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -26,23 +26,6 @@ if ( !canView( 'Monitors' ) ) { return; } -$tabs = array(); -$tabs['general'] = translate('General'); -$tabs['source'] = translate('Source'); -$tabs['storage'] = translate('Storage'); -$tabs['timestamp'] = translate('Timestamp'); -$tabs['buffers'] = translate('Buffers'); -if ( ZM_OPT_CONTROL && canView( 'Control' ) ) - $tabs['control'] = translate('Control'); -if ( ZM_OPT_X10 ) - $tabs['x10'] = translate('X10'); -$tabs['misc'] = translate('Misc'); - -if ( isset($_REQUEST['tab']) ) - $tab = validHtmlStr($_REQUEST['tab']); -else - $tab = 'general'; - $Server = null; if ( defined( 'ZM_SERVER_ID' ) ) { $Server = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array( ZM_SERVER_ID ) ); @@ -142,6 +125,7 @@ if ( ! $monitor ) { 'V4LCapturesPerFrame' => 1, 'ServerId' => 'auto', 'StorageId' => '1', + 'Refresh' => '', ) ); } # end if $_REQUEST['dupID'] } # end if $_REQUEST['mid'] @@ -212,7 +196,8 @@ $sourceTypes = array( 'Ffmpeg' => translate('Ffmpeg'), 'Libvlc' => translate('Libvlc'), 'cURL' => 'cURL (HTTP(S) only)', - 'NVSocket' => translate('NVSocket') + 'WebSite'=> 'Web Site', + 'NVSocket' => translate('NVSocket') ); if ( !ZM_HAS_V4L ) unset($sourceTypes['Local']); @@ -507,6 +492,25 @@ if ( canEdit( 'Monitors' ) ) {
    Type() != 'WebSite' ) { + $tabs['storage'] = translate('Storage'); + $tabs['timestamp'] = translate('Timestamp'); + $tabs['buffers'] = translate('Buffers'); + if ( ZM_OPT_CONTROL && canView( 'Control' ) ) + $tabs['control'] = translate('Control'); + if ( ZM_OPT_X10 ) + $tabs['x10'] = translate('X10'); + $tabs['misc'] = translate('Misc'); +} + +if ( isset($_REQUEST['tab']) ) + $tab = validHtmlStr($_REQUEST['tab']); +else + $tab = 'general'; + foreach ( $tabs as $name=>$value ) { if ( $tab == $name ) { ?> @@ -583,7 +587,7 @@ if ( $tab != 'source' || ($monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Lib Type()!= 'Remote' && $monitor->Type()!= 'File' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' && $monitor->Type()!= 'cURL') ) { +if ( $tab != 'source' || ($monitor->Type()!= 'Remote' && $monitor->Type()!= 'File' && $monitor->Type()!= 'Ffmpeg' && $monitor->Type()!= 'Libvlc' && $monitor->Type()!= 'cURL' && $monitor->Type() != 'WebSite') ) { ?> @@ -713,6 +717,9 @@ switch ( $tab ) { ?> Enabled() ) { ?> checked="checked"/> +Type != 'WebSite' ) { +?> @@ -802,6 +809,7 @@ echo htmlOptions(Group::get_dropdown_options( ), $monitor->GroupIds() ); ?> +Type() == 'WebSite' ) { +?> + + () + () + Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { ?> @@ -876,7 +891,7 @@ include('_monitor_source_nvsocket.php');  (Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>) Type() != 'NVSocket' ) { +if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { ?> Colours() ); ?> @@ -890,7 +905,7 @@ if ( $monitor->Type() == 'Local' ) { ?> Type() != 'WebSite' ) { ?> Type() == "WebSite" ) { + echo getWebSiteUrl( 'liveStream'.$monitor->Id(), $monitor->Path(), reScale( $monitor->Width(), $scale ), reScale( $monitor->Height(), $scale ), $monitor->Name() ); + } else { + echo getStreamHTML( $monitor, $monitor_options ); + } if ( $showZones ) { $height = null; $width = null; @@ -255,7 +259,7 @@ foreach ( $monitors as $monitor ) {
Type() != 'WebSite') ) { ?>
 -  fps
"> +"> @@ -265,21 +265,22 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI + - +'lower(Name)') ) as $Storage ) { ?> - - - - - + Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Id()), $canEdit ) ?> + Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Name()), $canEdit ) ?> + Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Path()), $canEdit ) ?> + Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Type()), $canEdit ) ?> + Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Scheme()), $canEdit ) ?> Name()), $canEdit ) ?> - disabled="disabled"/> + echo makePopupLink( '?view=storage&id='.$Storage->Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Name()), $canEdit ) ?> + disk_used_space()) . ' of ' . human_filesize($Storage->disk_total_space()) ?> + disabled="disabled"/> diff --git a/web/skins/classic/views/report_event_audit.php b/web/skins/classic/views/report_event_audit.php index 4da7b032c..7ec79d3bb 100644 --- a/web/skins/classic/views/report_event_audit.php +++ b/web/skins/classic/views/report_event_audit.php @@ -102,6 +102,7 @@ while( $event = $result->fetch(PDO::FETCH_ASSOC) ) { if ( count($EventsByMonitor[$event['MonitorId']]['Events']) ) { $last_event = end($EventsByMonitor[$event['MonitorId']]['Events']); +#Logger::Debug(print_r($last_event,true)); $gap = $last_event->EndTimeSecs() - $event['StartTimeSecs']; if ( $gap < $EventsByMonitor[$event['MonitorId']]['MinGap'] ) @@ -140,7 +141,10 @@ while( $event = $result->fetch(PDO::FETCH_ASSOC) ) { videocam  + + + @@ -152,8 +156,25 @@ while( $event = $result->fetch(PDO::FETCH_ASSOC) ) { for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { $monitor = $displayMonitors[$monitor_i]; $Monitor = new Monitor($monitor); - $montagereview_link = "?view=montagereview&live=0&MonitorId=". $monitor['Id'] . '&minTime='.$minTime.'&maxTime='.$maxTime; + + if ( isset($EventsByMonitor[$Monitor->Id()]) ) { + $EventCounts = $EventsByMonitor[$Monitor->Id()]; + $MinGap = $EventCounts['MinGap']; + $MaxGap = $EventCounts['MaxGap']; + $FileMissing = $EventCounts['FileMissing']; + $ZeroSize = $EventCounts['ZeroSize']; + $FirstEvent = $EventCounts['Events'][0]; + $LastEvent = end($EventCounts['Events']); + } else { + $MinGap = 0; + $MaxGap = 0; + $FileMissing = 0; + $ZeroSize = 0; + $FirstEvent = 0; + $LastEvent = 0; + } + ?> @@ -168,11 +189,14 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { }, $Monitor->GroupIds() ) ); ?> + Server()->Name()?> Id()])?count($EventsByMonitor[$Monitor->Id()]['Events']):0 ?> - Id()])?$EventsByMonitor[$Monitor->Id()]['MinGap']:0 ?> - Id()])?$EventsByMonitor[$Monitor->Id()]['MaxGap']:0 ?> - Id()])?$EventsByMonitor[$Monitor->Id()]['FileMissing']:0 ?> - Id()])?$EventsByMonitor[$Monitor->Id()]['ZeroSize']:0 ?> + link_to($FirstEvent->Id().' at ' . $FirstEvent->StartTime()) : 'none'?> + link_to($LastEvent->Id().' at ' . $LastEvent->StartTime()) : 'none'?> + + + + Controllable() && canView( 'Control' ) ); +$showPtzControls = ( ZM_OPT_CONTROL && $monitor->Controllable() && canView('Control') && $monitor->Type() != 'WebSite' ); if ( isset( $_REQUEST['scale'] ) ) { $scale = validInt($_REQUEST['scale']); @@ -80,6 +80,7 @@ if ( canView( 'Control' ) && $monitor->Type() == 'Local' ) {
$scale) ); ?>
+Type() != 'WebSite' ) { ?>
@@ -119,7 +120,7 @@ if ( $streamMode == 'jpeg' ) { ?>
+Type() != 'WebSite' ?> Type() != 'WebSite' ) { ?>