diff --git a/distros/redhat/readme/README.Fedora b/distros/redhat/readme/README.Fedora index d0ccf8c4e..3366c31a6 100644 --- a/distros/redhat/readme/README.Fedora +++ b/distros/redhat/readme/README.Fedora @@ -3,6 +3,11 @@ What's New 1. See the ZoneMinder release notes for a list of new features: https://github.com/ZoneMinder/zoneminder/releases + +2. The contents of the ZoneMinder Apache config file have changed. In + addition, this ZoneMinder package now requires you to manually symlink the + ZoneMinder Apache config file. See new install step 6 and upgrade step 3 + below for details. New installs ============ diff --git a/distros/redhat/readme/README.Redhat7 b/distros/redhat/readme/README.Redhat7 index b70e7768d..d8d1b4bde 100644 --- a/distros/redhat/readme/README.Redhat7 +++ b/distros/redhat/readme/README.Redhat7 @@ -4,6 +4,11 @@ What's New 1. See the ZoneMinder release notes for a list of new features: https://github.com/ZoneMinder/zoneminder/releases +2. The contents of the ZoneMinder Apache config file have changed. In + addition, this ZoneMinder package now requires you to manually symlink the + ZoneMinder Apache config file. See new install step 6 and upgrade step 3 + below for details. + New installs ============ diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index c52ccbad9..efe4a88e2 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -26,7 +26,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.31.43 +Version: 1.31.44 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm index a455bf71c..213b83dac 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm @@ -245,7 +245,8 @@ sub initialise( @ ) { $tempSyslogLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL_SYSLOG')); if ( $Config{ZM_LOG_DEBUG} ) { - foreach my $target ( split( /\|/, $Config{ZM_LOG_DEBUG_TARGET} ) ) { + # Splitting on an empty string doesn't return an empty string, it returns an empty array + foreach my $target ( $Config{ZM_LOG_DEBUG_TARGET} ? split(/\|/, $Config{ZM_LOG_DEBUG_TARGET}) : '' ) { if ( $target eq $this->{id} || $target eq '_'.$this->{id} || $target eq $this->{idRoot} @@ -278,6 +279,9 @@ sub initialise( @ ) { $this->{initialised} = !undef; + # this function can get called on a previously initialized log Object, so clean any sth's + $this->{sth} = undef; + Debug( 'LogOpts: level='.$codes{$this->{level}} .'/'.$codes{$this->{effectiveLevel}} .', screen='.$codes{$this->{termLevel}} @@ -319,6 +323,8 @@ sub reinitialise { my $screenLevel = $this->termLevel(); $this->termLevel(NOLOG); $this->termLevel($screenLevel) if $screenLevel > NOLOG; + + $this->{sth} = undef; } # Prevents undefined logging levels @@ -392,6 +398,12 @@ sub level { # ICON: I am remarking this out because I don't see the point of having an effective level, if we are just going to set it to level. #$this->{effectiveLevel} = $this->{level} if ( $this->{level} > $this->{effectiveLevel} ); + # ICON: The point is that LOG_DEBUG can be set either in db or in env var and will get passed in here. + # So this will turn on debug, even if not output has Debug level turned on. I think it should be the other way around + + # ICON: Let's try this line instead. effectiveLevel is 1 DEBUG from above, but LOG_DEBUG is off, then $this->level will be 0, and + # so effectiveLevel will become 0 + $this->{effectiveLevel} = $this->{level} if ( $this->{level} < $this->{effectiveLevel} ); } return $this->{level}; } @@ -474,7 +486,7 @@ sub openSyslog { sub closeSyslog { my $this = shift; -#closelog(); + closelog(); } sub logFile { @@ -517,54 +529,59 @@ sub logPrint { if ( $level <= $this->{effectiveLevel} ) { $string =~ s/[\r\n]+$//g; - - my $code = $codes{$level}; + if ( $level <= $this->{syslogLevel} ) { + syslog($priorities{$level}, $codes{$level}.' [%s]', $string); + } my ($seconds, $microseconds) = gettimeofday(); - my $message = sprintf( - '%s.%06d %s[%d].%s [%s]' - , strftime('%x %H:%M:%S', localtime($seconds)) - , $microseconds - , $this->{id} - , $$ - , $code - , $string - ); - if ( $this->{trace} ) { - $message = Carp::shortmess($message); - } else { - $message = $message."\n"; + if ( $level <= $this->{fileLevel} or $level <= $this->{termLevel} ) { + my $message = sprintf( + '%s.%06d %s[%d].%s [%s]' + , strftime('%x %H:%M:%S', localtime($seconds)) + , $microseconds + , $this->{id} + , $$ + , $codes{$level} + , $string + ); + if ( $this->{trace} ) { + $message = Carp::shortmess($message); + } else { + $message = $message."\n"; + } + print($LOGFILE $message) if $level <= $this->{fileLevel}; + print(STDERR $message) if $level <= $this->{termLevel}; } - if ( $level <= $this->{syslogLevel} ) { - syslog($priorities{$level}, $code.' [%s]', $string); - } - print($LOGFILE $message) if $level <= $this->{fileLevel}; - print(STDERR $message) if $level <= $this->{termLevel}; if ( $level <= $this->{databaseLevel} ) { - if ( ( $this->{dbh} and $this->{dbh}->ping() ) or ( $this->{dbh} = ZoneMinder::Database::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} ) { + if ( ! ( $this->{dbh} and $this->{dbh}->ping() ) ) { + $this->{sth} = undef; + if ( ! ( $this->{dbh} = ZoneMinder::Database::zmDbConnect() ) ) { + print(STDERR "Can't log to database: "); $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 ) { - $this->{databaseLevel} = NOLOG; - Error("Can't execute log entry '$sql': ".$this->{dbh}->errstr()); - } + return; } - } else { - print(STDERR "Can't log to database: "); + } + + 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}; + if ( !$this->{sth} ) { + $this->{databaseLevel} = NOLOG; + Error("Can't prepare log entry '$sql': ".$this->{dbh}->errstr()); + return; + } + + my $res = $this->{sth}->execute($seconds+($microseconds/1000000.0) + , $this->{id} + , $$ + , $level + , $codes{$level} + , $string + , $this->{fileName} + ); + if ( !$res ) { + $this->{databaseLevel} = NOLOG; + Error("Can't execute log entry '$sql': ".$this->{dbh}->errstr()); } } # end if doing db logging } # end if level < effectivelevel diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index 3ddf07e15..e2a69aa57 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -119,13 +119,13 @@ if ( $command eq 'version' ) { } my $needs_daemon = $command !~ /(?:startup|shutdown|status|check|logrot|version)/; my $daemon = shift @ARGV; -if ( $needs_daemon && ! $daemon ) { +if ( $needs_daemon && !$daemon ) { print(STDERR "No daemon given\n"); pod2usage(-exitstatus => -1); } my @args; -my $daemon_patt = '('.join( '|', @daemons ).')'; +my $daemon_patt = '('.join('|', @daemons).')'; if ( $needs_daemon ) { if ( $daemon =~ /^${daemon_patt}$/ ) { $daemon = $1; @@ -139,7 +139,7 @@ foreach my $arg ( @ARGV ) { # Detaint arguments, if they look ok #if ( $arg =~ /^(-{0,2}[\w]+)/ ) if ( $arg =~ /^(-{0,2}[\w\/?&=.-]+)$/ ) { - push( @args, $1 ); + push @args, $1; } else { print(STDERR "Bogus argument '$arg' found"); exit(-1); @@ -245,6 +245,16 @@ our %terminating_processes; our $zm_terminate = 0; sub run { + + # Call this first otherwise stdout/stderror redirects to the pidfile = bad + if ( open(my $PID, '>', ZM_PID) ) { + print($PID $$); + close($PID); + } else { + # Log not initialized at this point so use die instead + die "Can't open pid file at ".ZM_PID."\n"; + } + my $fd = 0; while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) { POSIX::close($fd++); @@ -252,6 +262,8 @@ sub run { setpgrp(); + # dbh got closed with the rest of the fd's above, so need to reconnect. + my $dbh = zmDbConnect(1); logInit(); dPrint(ZoneMinder::Logger::INFO, 'Server starting at ' @@ -259,18 +271,10 @@ sub run { ."\n" ); - if ( open(my $PID, '>', ZM_PID) ) { - print($PID $$); - close($PID); - } else { - Error("Can't open pid file at " . ZM_PID); - } - # Tell any existing processes to die, wait 1 second between TERM and KILL killAll(1); dPrint(ZoneMinder::Logger::INFO, 'Socket should be open at ' .main::SOCK_FILE); - my $dbh = zmDbConnect(1); socket(SERVER, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!"); unlink(main::SOCK_FILE) or Error('Unable to unlink ' . main::SOCK_FILE .". Error message was: $!") if -e main::SOCK_FILE; bind(SERVER, $saddr) or Fatal("Can't bind to " . main::SOCK_FILE . ": $!"); @@ -306,7 +310,7 @@ sub run { $dbh = zmDbConnect(); } my @cpuload = CpuLoad(); - Debug("UPdating Server record @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()); @@ -315,7 +319,6 @@ sub run { $secs_count += 1; } 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); @@ -370,9 +373,7 @@ Debug("Aftere select $nfound"); #print( "Select timed out\n" ); } -Debug("restartPending"); restartPending(); -Debug("check_for_processes_to_kill"); check_for_processes_to_kill(); } # end while @@ -382,7 +383,7 @@ Debug("check_for_processes_to_kill"); ."\n" ); if ( $Config{ZM_SERVER_ID} ) { - $dbh = zmDbConnect() if ! $dbh->ping(); + $dbh = zmDbConnect() if ! ($dbh and $dbh->ping()); if ( ! defined $dbh->do(q{UPDATE Servers SET Status='NotRunning' WHERE Id=?}, undef, $Config{ZM_SERVER_ID}) ) { Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr()); } @@ -399,6 +400,7 @@ sub cPrint { # I think the purpose of this is to echo the logs to the client process so it can then display them. sub dPrint { my $logLevel = shift; + # One thought here, if no client exists to read these... does it block? if ( fileno(CLIENT) ) { print CLIENT @_ } @@ -435,12 +437,10 @@ sub start { my $sigset = POSIX::SigSet->new; 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() ) { # This logReinit is required. Not sure why. - logReinit(); + #logReinit(); $process->{pid} = $cpid; $process->{started} = time(); @@ -453,7 +453,6 @@ sub start { $cmd_hash{$process->{command}} = $pid_hash{$cpid} = $process; sigprocmask(SIG_SETMASK, $sigset) or Fatal("Can't restore SIGCHLD: $!"); - Debug("unblocking child"); } elsif ( defined($cpid) ) { # Force reconnection to the db. $dbh = zmDbConnect(1); @@ -516,7 +515,7 @@ sub send_stop { .strftime('%y/%m/%d %H:%M:%S', localtime()) ."\n" ); - sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n"; + sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n"; return(); } @@ -708,7 +707,6 @@ sub reaper { } # end while waitpid $SIG{CHLD} = \&reaper; $! = $saved_status; - Debug("Leaving reaper"); } sub restartPending { @@ -720,7 +718,6 @@ sub restartPending { start($process->{daemon}, @{$process->{args}}); } } - Debug("done restartPending"); } sub shutdownAll { @@ -808,7 +805,7 @@ sub status { foreach my $process ( values %cmd_hash ) { if ( $process->{pending} ) { dPrint(ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at " - .strftime( '%y/%m/%d %H:%M:%S', localtime($process->{pending})) + .strftime('%y/%m/%d %H:%M:%S', localtime($process->{pending})) ."\n" ); } diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index fa59d91e0..095d2764e 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -70,12 +70,14 @@ use Getopt::Long; use autouse 'Pod::Usage'=>qw(pod2usage); use autouse 'Data::Dumper'=>qw(Dumper); +my $daemon = 0; my $filter_name = ''; my $filter_id; my $version = 0; my $zm_terminate = 0; GetOptions( + 'daemon' =>\$daemon, 'filter=s' =>\$filter_name, 'filter_id=s' =>\$filter_id, 'version' =>\$version @@ -201,7 +203,7 @@ while( !$zm_terminate ) { } } - last if $filter_name or $filter_id or $zm_terminate; + last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate; Debug("Sleeping for $delay seconds\n"); sleep($delay); diff --git a/scripts/zmpkg.pl.in b/scripts/zmpkg.pl.in index 62771c7aa..d11e04d5a 100644 --- a/scripts/zmpkg.pl.in +++ b/scripts/zmpkg.pl.in @@ -41,9 +41,10 @@ use autouse 'Pod::Usage'=>qw(pod2usage); $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; -my $store_state=""; # PP - will remember state name passed +my $store_state=''; # PP - will remember state name passed logInit(); +Info("Aftere LogInit"); my $command = $ARGV[0]||''; if ( $command eq 'version' ) { @@ -53,28 +54,27 @@ if ( $command eq 'version' ) { my $state; -my $dbh; - +my $dbh = zmDbConnect(); +Info("Command: $command"); if ( !$command || $command !~ /^(?:start|stop|restart|status|logrot|version)$/ ) { if ( $command ) { - $dbh = zmDbConnect(); # Check to see if it's a valid run state my $sql = 'SELECT * FROM States WHERE Name=?'; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $command ) - or Fatal( "Can't execute: ".$sth->errstr() ); + my $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($command) + or Fatal("Can't execute: ".$sth->errstr()); if ( $state = $sth->fetchrow_hashref() ) { - $state->{Name} = $command; + #$state->{Name} = $command; $state->{Definitions} = []; - foreach( split( /,/, $state->{Definition} ) ) { - my ( $id, $function, $enabled ) = split( /:/, $_ ); + foreach( split(',', $state->{Definition}) ) { + my ( $id, $function, $enabled ) = split(':', $_); push( @{$state->{Definitions}}, { Id=>$id, Function=>$function, Enabled=>$enabled } ); } - $store_state=$command; # PP - Remember the name that was passed to search in DB - $command = 'state'; + $store_state = $command; # PP - Remember the name that was passed to search in DB + $command = 'state'; } else { $command = undef; } @@ -82,64 +82,66 @@ if ( !$command || $command !~ /^(?:start|stop|restart|status|logrot|version)$/ ) if ( !$command ) { pod2usage(-exitstatus => -1); } -} -$dbh = zmDbConnect() if ! $dbh; +} # end if not one of the usual commands + # PP - Sane state check +Debug("StartisActiveSSantiyCheck"); isActiveSanityCheck(); +Debug("Done isActiveSSantiyCheck"); # Move to the right place -chdir( $Config{ZM_PATH_WEB} ) - or Fatal( "Can't chdir to '".$Config{ZM_PATH_WEB}."': $!" ); +chdir($Config{ZM_PATH_WEB}) + or Fatal("Can't chdir to '$Config{ZM_PATH_WEB}': $!"); - my $dbg_id = ''; +my $dbg_id = ''; - Info( "Command: $command\n" ); +Info("Command: $command"); - my $retval = 0; +my $retval = 0; - if ( $command eq 'state' ) { - Info( "Updating DB: $state->{Name}\n" ); - my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=? ORDER BY Id ASC' : 'SELECT * FROM Monitors ORDER BY Id ASC'; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID}: () ) - or Fatal( "Can't execute: ".$sth->errstr() ); - while( my $monitor = $sth->fetchrow_hashref() ) { - foreach my $definition ( @{$state->{Definitions}} ) { - if ( $monitor->{Id} =~ /^$definition->{Id}$/ ) { - $monitor->{NewFunction} = $definition->{Function}; - $monitor->{NewEnabled} = $definition->{Enabled}; - } - } -#next if ( !$monitor->{NewFunction} ); - $monitor->{NewFunction} = 'None' - if ( !$monitor->{NewFunction} ); - $monitor->{NewEnabled} = 0 - if ( !$monitor->{NewEnabled} ); - if ( $monitor->{Function} ne $monitor->{NewFunction} - || $monitor->{Enabled} ne $monitor->{NewEnabled} - ) { - my $sql = 'UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?'; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $monitor->{NewFunction}, $monitor->{NewEnabled}, $monitor->{Id} ) - or Fatal( "Can't execute: ".$sth->errstr() ); +if ( $command eq 'state' ) { + Info("Updating DB: $state->{Name}"); + my $sql = 'SELECT * FROM Monitors' . ($Config{ZM_SERVER_ID} ? ' WHERE ServerId=?' : '' ) .' ORDER BY Id ASC'; + my $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID}: ()) + or Fatal("Can't execute: ".$sth->errstr()); + while( my $monitor = $sth->fetchrow_hashref() ) { + foreach my $definition ( @{$state->{Definitions}} ) { + if ( $monitor->{Id} =~ /^$definition->{Id}$/ ) { + $monitor->{NewFunction} = $definition->{Function}; + $monitor->{NewEnabled} = $definition->{Enabled}; } } - $sth->finish(); + #next if ( !$monitor->{NewFunction} ); + $monitor->{NewFunction} = 'None' + if ( !$monitor->{NewFunction} ); + $monitor->{NewEnabled} = 0 + if ( !$monitor->{NewEnabled} ); + if ( $monitor->{Function} ne $monitor->{NewFunction} + || $monitor->{Enabled} ne $monitor->{NewEnabled} + ) { + my $sql = 'UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?'; + my $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($monitor->{NewFunction}, $monitor->{NewEnabled}, $monitor->{Id}) + or Fatal("Can't execute: ".$sth->errstr()); + } # end if change of function or enablement + } # end foreach monitor + $sth->finish(); -# PP - Now mark a specific state as active - resetStates(); - Info ("Marking $store_state as Enabled"); - $sql = "UPDATE States SET IsActive = '1' WHERE Name = ?"; - $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - $res = $sth->execute( $store_state ) - or Fatal( "Can't execute: ".$sth->errstr() ); + # PP - Now mark a specific state as active + resetStates(); + Info("Marking $store_state as Enabled"); + $sql = 'UPDATE States SET IsActive = 1 WHERE Name = ?'; + $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); + $res = $sth->execute($store_state) + or Fatal("Can't execute: ".$sth->errstr()); -# PP - zero out other states isActive - $command = 'restart'; - } + # PP - zero out other states isActive + $command = 'restart'; +} # end if command = state # Check if we are running systemd and if we have been called by the system if ( $command =~ /^(start|stop|restart)$/ ) { @@ -154,6 +156,7 @@ if ( $command =~ /^(start|stop|restart)$/ ) { if ( $command =~ /^(?:stop|restart)$/ ) { my $status = runCommand('zmdc.pl check'); + Debug("zmdc.pl check = $status"); if ( $status eq 'running' ) { runCommand('zmdc.pl shutdown'); @@ -163,20 +166,19 @@ if ( $command =~ /^(?:stop|restart)$/ ) { } } -#runCommand( "zmupdate.pl -f" ); - if ( $command =~ /^(?:start|restart)$/ ) { my $status = runCommand('zmdc.pl check'); + Debug("zmdc.pl check = $status"); if ( $status eq 'stopped' ) { if ( $Config{ZM_DYN_DB_VERSION} and ( $Config{ZM_DYN_DB_VERSION} ne ZM_VERSION ) ) { - Fatal( 'Version mismatch, system is version '.ZM_VERSION + Fatal('Version mismatch, system is version '.ZM_VERSION .', database is '.$Config{ZM_DYN_DB_VERSION} .', please run zmupdate.pl to update.' ); - exit( -1 ); + exit(-1); } # Recreate the temporary directory if it's been wiped @@ -196,8 +198,8 @@ if ( $command =~ /^(?:start|restart)$/ ) { my @values; if ( $Config{ZM_SERVER_ID} ) { require ZoneMinder::Server; - Info("Multi-server configuration detected. Starting up services for server $Config{ZM_SERVER_ID}\n"); - $Server = new ZoneMinder::Server( $Config{ZM_SERVER_ID} ); + Info("Multi-server configuration detected. Starting up services for server $Config{ZM_SERVER_ID}"); + $Server = new ZoneMinder::Server($Config{ZM_SERVER_ID}); $sql = 'SELECT * FROM Monitors WHERE ServerId=?'; @values = ( $Config{ZM_SERVER_ID} ); } else { @@ -206,46 +208,49 @@ if ( $command =~ /^(?:start|restart)$/ ) { } { - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( @values ) - or Fatal( "Can't execute: ".$sth->errstr() ); - while( my $monitor = $sth->fetchrow_hashref() ) { - if ( $monitor->{Function} ne 'None' && $monitor->{Type} ne 'WebSite' ) { - if ( $monitor->{Type} eq 'Local' ) { - runCommand( "zmdc.pl start zmc -d $monitor->{Device}" ); - } else { - runCommand( "zmdc.pl start zmc -m $monitor->{Id}" ); - } - if ( $monitor->{Function} ne 'Monitor' ) { - runCommand( "zmdc.pl start zma -m $monitor->{Id}" ); - } - if ( $Config{ZM_OPT_CONTROL} ) { - if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' ) { - if ( $monitor->{Controllable} && $monitor->{TrackMotion} ) { - runCommand( "zmdc.pl start zmtrack.pl -m $monitor->{Id}" ); - } + my $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute(@values) + or Fatal("Can't execute: ".$sth->errstr()); + while( my $monitor = $sth->fetchrow_hashref() ) { + if ( $monitor->{Function} ne 'None' && $monitor->{Type} ne 'WebSite' ) { + if ( $monitor->{Type} eq 'Local' ) { + runCommand("zmdc.pl start zmc -d $monitor->{Device}"); + } else { + runCommand("zmdc.pl start zmc -m $monitor->{Id}"); } - } - } - } - $sth->finish(); + if ( $monitor->{Function} ne 'Monitor' ) { + runCommand("zmdc.pl start zma -m $monitor->{Id}"); + } + if ( $Config{ZM_OPT_CONTROL} ) { + if ( $monitor->{Controllable} && $monitor->{TrackMotion} ) { + if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' ) { + runCommand( "zmdc.pl start zmtrack.pl -m $monitor->{Id}" ); + } else { + Warning(' Monitor is set to track motion, but does not have motion detection enabled.'); + } # end if Has motion enabled + } # end if track motion + } # end if ZM_OPT_CONTROL + } # end if function is not none or Website + } # end foreach monitor + $sth->finish(); } + { - my $sql = 'SELECT Id FROM Filters WHERE Background=1'; - my $sth = $dbh->prepare_cached($sql) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() - or Fatal( "Can't execute: ".$sth->errstr() ); - if ( $sth->rows ) { - while( my $filter = $sth->fetchrow_hashref() ) { -# This is now started unconditionally - runCommand("zmdc.pl start zmfilter.pl --filter_id=$$filter{Id}"); + my $sql = 'SELECT Id FROM Filters WHERE Background=1'; + my $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute() + or Fatal("Can't execute: ".$sth->errstr()); + if ( $sth->rows ) { + while( my $filter = $sth->fetchrow_hashref() ) { + # This is now started unconditionally + runCommand("zmdc.pl start zmfilter.pl --filter_id=$$filter{Id} --daemon"); + } + } else { + runCommand('zmdc.pl start zmfilter.pl'); } - } else { - runCommand('zmdc.pl start zmfilter.pl'); - } - $sth->finish(); + $sth->finish(); } if ( $Config{ZM_RUN_AUDIT} ) { @@ -283,32 +288,32 @@ if ( $command =~ /^(?:start|restart)$/ ) { } else { $retval = 1; } -} +} # end if command is start or restart if ( $command eq 'status' ) { my $status = runCommand('zmdc.pl check'); - print( STDOUT $status."\n" ); + print(STDOUT $status."\n"); } elsif ( $command eq 'logrot' ) { runCommand('zmdc.pl logrot'); } -exit( $retval ); +exit($retval); # PP - Make sure isActive is on and only one sub isActiveSanityCheck { - Info ('Sanity checking States table...'); + Info('Sanity checking States table...'); $dbh = zmDbConnect() if ! $dbh; # PP - First, make sure default exists and there is only one - my $sql = "SELECT Name FROM States WHERE Name='default'"; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $sql = q`SELECT Name FROM States WHERE Name='default'`; + my $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); my $res = $sth->execute() - or Fatal( "Can't execute: ".$sth->errstr() ); + or Fatal("Can't execute: ".$sth->errstr()); - if ($sth->rows != 1) { + if ( $sth->rows != 1 ) { # PP - no row, or too many rows. Either case is an error Info( 'Fixing States table - either no default state or duplicate default states' ); $sql = "DELETE FROM States WHERE Name='default'"; @@ -316,69 +321,53 @@ sub isActiveSanityCheck { or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); - $sql = "INSERT INTO States (Name,Definition,IsActive) VALUES ('default','','1');"; - $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $sql = q`"INSERT INTO States (Name,Definition,IsActive) VALUES ('default','','1');`; + $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); $res = $sth->execute() - or Fatal( "Can't execute: ".$sth->errstr() ); + or Fatal("Can't execute: ".$sth->errstr()); } - # PP - Now make sure no two states have IsActive=1 - $sql = "SELECT Name FROM States WHERE IsActive = '1'"; - $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $sql = 'SELECT Name FROM States WHERE IsActive = 1'; + $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); $res = $sth->execute() - or Fatal( "Can't execute: ".$sth->errstr() ); + or Fatal("Can't execute: ".$sth->errstr()); if ( $sth->rows != 1 ) { - Info( 'Fixing States table so only one run state is active' ); + Info('Fixing States table so only one run state is active'); resetStates(); - $sql = "UPDATE States SET IsActive='1' WHERE Name='default'"; - $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $sql = q`UPDATE States SET IsActive=1 WHERE Name='default'`; + $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); $res = $sth->execute() - or Fatal( "Can't execute: ".$sth->errstr() ); + or Fatal("Can't execute: ".$sth->errstr()); } -} - +} # end sub isActiveSanityCheck # PP - zeroes out isActive for all states sub resetStates { $dbh = zmDbConnect() if ! $dbh; - my $sql = "UPDATE States SET IsActive='0'"; - my $sth = $dbh->prepare_cached( $sql ) - or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $sql = 'UPDATE States SET IsActive=0'; + my $sth = $dbh->prepare_cached($sql) + or Fatal("Can't prepare '$sql': ".$dbh->errstr()); my $res = $sth->execute() - or Fatal( "Can't execute: ".$sth->errstr() ); - + or Fatal("Can't execute: ".$sth->errstr()); } sub systemdRunning { - my $result = 0; - my $output = qx(ps -o comm="" -p 1); - chomp( $output ); - - if ( $output =~ /systemd/ ) { - $result = 1; - } - - return $result; + return scalar ( $output =~ /systemd/ ); } sub calledBysystem { - my $result = 0; my $ppid = getppid(); my $output = qx(ps -o comm="" -p $ppid); - chomp( $output ); + #chomp( $output ); - if ($output =~ /^(?:systemd|init)$/) { - $result = 1; - } - - return $result; + return ($output =~ /^(?:systemd|init)$/); } sub verifyFolder { @@ -386,24 +375,22 @@ sub verifyFolder { # Recreate the temporary directory if it's been wiped if ( !-e $folder ) { - Debug( "Recreating directory '$folder'" ); - mkdir( $folder, 0774 ) + Debug("Recreating directory '$folder'"); + mkdir($folder, 0774) or Fatal( "Can't create missing temporary directory '$folder': $!" ); - my ( $runName ) = getpwuid( $> ); + my ( $runName ) = getpwuid($>); if ( $runName ne $Config{ZM_WEB_USER} ) { # Not running as web user, so should be root in which case # chown the directory - my ( $webName, $webPass, $webUid, $webGid ) = getpwnam( $Config{ZM_WEB_USER} ) - or Fatal( "Can't get user details for web user '" - .$Config{ZM_WEB_USER}."': $!" + my ( $webName, $webPass, $webUid, $webGid ) = getpwnam($Config{ZM_WEB_USER}) + or Fatal("Can't get details for web user '$Config{ZM_WEB_USER}': $!"); + chown($webUid, $webGid, $folder) + or Fatal("Can't change ownership of '$folder' to '" + .$Config{ZM_WEB_USER}.':'.$Config{ZM_WEB_GROUP}."': $!" ); - chown( $webUid, $webGid, "$folder" ) - or Fatal( "Can't change ownership of directory '$folder' to '" - .$Config{ZM_WEB_USER}.":".$Config{ZM_WEB_GROUP}."': $!" - ); - } - } -} + } # end if runName ne ZM_WEB_USER + } # end if folder doesn't exist +} # end sub verifyFolder 1; __END__ diff --git a/src/zm_db.cpp b/src/zm_db.cpp index d4a927f3b..806ef8e5c 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -24,7 +24,7 @@ #include "zm_db.h" MYSQL dbconn; -Mutex db_mutex; +RecursiveMutex db_mutex; bool zmDbConnected = false; @@ -91,15 +91,15 @@ void zmDbClose() { } MYSQL_RES * zmDbFetch(const char * query) { - if ( ! zmDbConnected ) { + if ( !zmDbConnected ) { Error("Not connected."); return NULL; } db_mutex.lock(); if ( mysql_query(&dbconn, query) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); db_mutex.unlock(); + Error("Can't run query: %s", mysql_error(&dbconn)); return NULL; } Debug(4, "Success running query: %s", query); diff --git a/src/zm_db.h b/src/zm_db.h index de47a6108..c707ad2b7 100644 --- a/src/zm_db.h +++ b/src/zm_db.h @@ -41,7 +41,7 @@ class zmDbRow { }; extern MYSQL dbconn; -extern Mutex db_mutex; +extern RecursiveMutex db_mutex; bool zmDbConnect(); void zmDbClose(); diff --git a/src/zm_thread.cpp b/src/zm_thread.cpp index 8bb854e26..03d048bea 100644 --- a/src/zm_thread.cpp +++ b/src/zm_thread.cpp @@ -95,6 +95,15 @@ bool Mutex::locked() { return( state == EBUSY ); } +RecursiveMutex::RecursiveMutex() { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + + if ( pthread_mutex_init(&mMutex, &attr) < 0 ) + Error("Unable to create pthread mutex: %s", strerror(errno)); +} + Condition::Condition( Mutex &mutex ) : mMutex( mutex ) { if ( pthread_cond_init( &mCondition, NULL ) < 0 ) throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) ); diff --git a/src/zm_thread.h b/src/zm_thread.h index e1facc6a5..0c41a93a5 100644 --- a/src/zm_thread.h +++ b/src/zm_thread.h @@ -59,27 +59,34 @@ public: }; class Mutex { -friend class Condition; + friend class Condition; -private: - pthread_mutex_t mMutex; + private: + pthread_mutex_t mMutex; -public: - Mutex(); - ~Mutex(); + public: + Mutex(); + ~Mutex(); -private: - pthread_mutex_t *getMutex() { - return( &mMutex ); - } + private: + pthread_mutex_t *getMutex() { + return &mMutex; + } -public: - int trylock(); - void lock(); - void lock( int secs ); - void lock( double secs ); - void unlock(); - bool locked(); + public: + int trylock(); + void lock(); + void lock( int secs ); + void lock( double secs ); + void unlock(); + bool locked(); +}; + +class RecursiveMutex : public Mutex { + private: + pthread_mutex_t mMutex; + public: + RecursiveMutex(); }; class ScopedMutex { diff --git a/src/zms.cpp b/src/zms.cpp index e8d20f72f..c785485da 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -74,45 +74,44 @@ int main( int argc, const char *argv[] ) { unsigned int playback_buffer = 0; bool nph = false; - const char *basename = strrchr( argv[0], '/' ); - if (basename) //if we found a / lets skip past it + const char *basename = strrchr(argv[0], '/'); + if ( basename ) //if we found a / lets skip past it basename++; else //argv[0] will not always contain the full path, but rather just the script name basename = argv[0]; const char *nph_prefix = "nph-"; - if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) ) { + if ( basename && !strncmp(basename, nph_prefix, strlen(nph_prefix)) ) { nph = true; } zmLoadConfig(); - - const char *query = getenv( "QUERY_STRING" ); + const char *query = getenv("QUERY_STRING"); if ( query ) { - Debug( 1, "Query: %s", query ); + Debug(1, "Query: %s", query); char temp_query[1024]; - strncpy( temp_query, query, sizeof(temp_query) ); + strncpy(temp_query, query, sizeof(temp_query)); char *q_ptr = temp_query; char *parms[16]; // Shouldn't be more than this int parm_no = 0; - while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) ) { + while( (parm_no < 16) && (parms[parm_no] = strtok(q_ptr, "&")) ) { parm_no++; q_ptr = NULL; } for ( int p = 0; p < parm_no; p++ ) { - char *name = strtok( parms[p], "=" ); - char *value = strtok( NULL, "=" ); + char *name = strtok(parms[p], "="); + char *value = strtok(NULL, "="); if ( !value ) value = (char *)""; - if ( !strcmp( name, "source" ) ) { - source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR; - } else if ( !strcmp( name, "mode" ) ) { - mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG; - mode = !strcmp( value, "raw" )?ZMS_RAW:mode; - mode = !strcmp( value, "zip" )?ZMS_ZIP:mode; - mode = !strcmp( value, "single" )?ZMS_SINGLE:mode; + if ( !strcmp(name, "source") ) { + source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR; + } else if ( !strcmp(name, "mode") ) { + mode = !strcmp(value, "jpeg")?ZMS_JPEG:ZMS_MPEG; + mode = !strcmp(value, "raw")?ZMS_RAW:mode; + mode = !strcmp(value, "zip")?ZMS_ZIP:mode; + mode = !strcmp(value, "single")?ZMS_SINGLE:mode; } else if ( !strcmp( name, "format" ) ) { strncpy( format, value, sizeof(format) ); } else if ( !strcmp( name, "monitor" ) ) { @@ -182,32 +181,32 @@ int main( int argc, const char *argv[] ) { if ( config.opt_use_auth ) { User *user = 0; - if ( strcmp( config.auth_relay, "none" ) == 0 ) { + if ( strcmp(config.auth_relay, "none") == 0 ) { if ( username.length() ) { - user = zmLoadUser( username.c_str() ); + user = zmLoadUser(username.c_str()); } } else { //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) { if ( *auth ) { - user = zmLoadAuthUser( auth, config.auth_hash_ips ); + user = zmLoadAuthUser(auth, config.auth_hash_ips); } } //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) { if ( username.length() && password.length() ) { - user = zmLoadUser( username.c_str(), password.c_str() ); + user = zmLoadUser(username.c_str(), password.c_str()); } } } if ( !user ) { - Error( "Unable to authenticate user" ); + Error("Unable to authenticate user"); logTerm(); zmDbClose(); - return( -1 ); + return -1; } - ValidateAccess( user, monitor_id ); - } + ValidateAccess(user, monitor_id); + } // end if config.opt_use_auth hwcaps_detect(); zmSetDefaultTermHandler(); diff --git a/web/ajax/stream.php b/web/ajax/stream.php index 1c2fb3d62..44aaec8e6 100644 --- a/web/ajax/stream.php +++ b/web/ajax/stream.php @@ -22,13 +22,9 @@ if ( sem_acquire($semaphore,1) !== false ) { if ( file_exists( $localSocketFile ) ) { Warning("sock file $localSocketFile already exists?! Is someone else talking to zms?"); // They could be. We can maybe have concurrent requests from a browser. - } else { - Logger::Debug("socket file does not exist, we should be good to connect."); } if ( ! socket_bind( $socket, $localSocketFile ) ) { - ajaxError( "socket_bind( $localSocketFile ) failed: ".socket_strerror(socket_last_error()) ); - } else { - Logger::Debug("Bound to $localSocketFile"); + ajaxError("socket_bind( $localSocketFile ) failed: ".socket_strerror(socket_last_error()) ); } switch ( $_REQUEST['command'] ) { @@ -81,7 +77,6 @@ if ( sem_acquire($semaphore,1) !== false ) { $eSockets = NULL; $timeout = MSG_TIMEOUT - ( time() - $start_time ); - Logger::Debug("TImeout is: $timeout/1000 seconds. " ); $numSockets = socket_select( $rSockets, $wSockets, $eSockets, intval($timeout/1000), ($timeout%1000)*1000 ); diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 660bee5ce..5801c1e9e 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -332,25 +332,18 @@ class MonitorsController extends AppController { } public function daemonControl($id, $command, $monitor=null, $daemon=null) { - $args = ''; $daemons = array(); - if (!$monitor) { + if ( !$monitor ) { // Need to see if it is local or remote $monitor = $this->Monitor->find('first', array( - 'fields' => array('Type', 'Function'), + 'fields' => array('Type', 'Function', 'Device'), 'conditions' => array('Id' => $id) )); $monitor = $monitor['Monitor']; } - if ($monitor['Type'] == 'Local') { - $args = '-d ' . $monitor['Device']; - } else { - $args = '-m ' . $id; - } - - if ($monitor['Function'] == 'Monitor') { + if ( $monitor['Function'] == 'Monitor' ) { array_push($daemons, 'zmc'); } else { array_push($daemons, 'zmc', 'zma'); @@ -359,6 +352,13 @@ class MonitorsController extends AppController { $zm_path_bin = Configure::read('ZM_PATH_BIN'); foreach ($daemons as $daemon) { + $args = ''; + if ( $daemon == 'zmc' and $monitor['Type'] == 'Local') { + $args = '-d ' . $monitor['Device']; + } else { + $args = '-m ' . $id; + } + $shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args"); $status = exec( $shellcmd ); } diff --git a/web/includes/Filter.php b/web/includes/Filter.php index de51b9876..721302edb 100644 --- a/web/includes/Filter.php +++ b/web/includes/Filter.php @@ -20,7 +20,7 @@ public $defaults = array( 'limit' => 100, 'Query' => array(), 'sort_field' => ZM_WEB_EVENT_SORT_FIELD, - 'sort_asc' => ZM_WEB_EVENT_SORT_ORDER == 'asc' ? 'asc' : 'desc', + 'sort_asc' => ZM_WEB_EVENT_SORT_ORDER, ); public function __construct( $IdOrRow=NULL ) { diff --git a/web/includes/actions.php b/web/includes/actions.php index 8549d3978..54402da7c 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -476,15 +476,12 @@ if ( canEdit( 'Monitors' ) ) { ); if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) { - Logger::Debug("Auto selecting server"); $_REQUEST['newMonitor']['ServerId'] = dbFetchOne('SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id'); Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] ); if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) { $_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID; Logger::Debug("Auto selecting server to " . ZM_SERVER_ID); } - } else { - Logger::Debug("NOT Auto selecting server" . $_REQUEST['newMonitor']['ServerId']); } $columns = getTableColumns('Monitors'); @@ -571,22 +568,23 @@ if ( canEdit( 'Monitors' ) ) { $restart = true; } # end if count(changes) - if ( - ( !isset($_POST['newMonitor']['GroupIds']) ) - or - ( count($_POST['newMonitor']['GroupIds']) != count($Monitor->GroupIds()) ) - or - array_diff($_POST['newMonitor']['GroupIds'], $Monitor->GroupIds()) - ) { - if ( $Monitor->Id() ) - dbQuery('DELETE FROM Groups_Monitors WHERE MonitorId=?', array($mid)); - if ( isset($_POST['newMonitor']['GroupIds']) ) { - foreach ( $_POST['newMonitor']['GroupIds'] as $group_id ) { - dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid)); - } + if ( + ( !isset($_POST['newMonitor']['GroupIds']) ) + or + ( count($_POST['newMonitor']['GroupIds']) != count($Monitor->GroupIds()) ) + or + array_diff($_POST['newMonitor']['GroupIds'], $Monitor->GroupIds()) + ) { + if ( $Monitor->Id() ) + dbQuery('DELETE FROM Groups_Monitors WHERE MonitorId=?', array($mid)); + + if ( isset($_POST['newMonitor']['GroupIds']) ) { + foreach ( $_POST['newMonitor']['GroupIds'] as $group_id ) { + dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid)); } - } // end if there has been a change of groups + } + } // end if there has been a change of groups if ( ZM_OPT_X10 ) { $x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] ); diff --git a/web/index.php b/web/index.php index d93b46e23..4cc7cf75c 100644 --- a/web/index.php +++ b/web/index.php @@ -200,7 +200,7 @@ isset($view) || $view = NULL; isset($request) || $request = NULL; isset($action) || $action = NULL; -if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $view != 'video' && $request != 'control' && $view != 'frames' && $view != 'archive' ) { +if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $request != 'control' && $view != 'frames' && $view != 'archive' ) { require_once( 'includes/csrf/csrf-magic.php' ); #Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\""); csrf_check(); diff --git a/web/skins/classic/css/classic/views/watch.css b/web/skins/classic/css/classic/views/watch.css index 4ebb30dda..247be4f05 100644 --- a/web/skins/classic/css/classic/views/watch.css +++ b/web/skins/classic/css/classic/views/watch.css @@ -38,13 +38,6 @@ text-align: center; } -#dvrControls input { - height: 20px; - width: 28px; - padding-bottom: 3px; - margin: 0 3px; -} - #dvrControls input[disabled] { color: #aaaaaa; } diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index e1808410a..3d7777ef1 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -373,18 +373,13 @@ function xhtmlFooter() { global $view; global $skin; global $running; -if ( canEdit('System') ) { - include("skins/$skin/views/state.php"); + if ( canEdit('System') ) { + include("skins/$skin/views/state.php"); + } ?> - - - + + diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 7f960cc90..5da53d0d2 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -192,7 +192,13 @@ if ( currentView != 'none' ) { }); function getNavBar() { - $j.getJSON(thisUrl + '?view=request&request=status&entity=navBar', setNavBar); + $j.getJSON(thisUrl + '?view=request&request=status&entity=navBar') + .done(setNavBar) + .fail(function( jqxhr, textStatus, error ) { + var err = textStatus + ", " + error; + console.log( "Request Failed: " + err ); + window.location.href = thisUrl; + }); } function setNavBar(data) { diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index 119a223eb..864bb956f 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -223,7 +223,7 @@ ob_start(); - + - + diff --git a/web/skins/classic/views/options.php b/web/skins/classic/views/options.php index 0a70ed96a..31c1f57cb 100644 --- a/web/skins/classic/views/options.php +++ b/web/skins/classic/views/options.php @@ -279,7 +279,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Scheme()), $canEdit ) ?> Id(), 'zmStorage', 'storage', validHtmlStr($Storage->Name()), $canEdit ) ?> - disk_used_space()) . ' of ' . human_filesize($Storage->disk_total_space()) ?> + disk_used_space()) . ' of ' . human_filesize($Storage->disk_total_space()) ?> disabled="disabled"/> diff --git a/web/skins/classic/views/video.php b/web/skins/classic/views/video.php index 0c7c4e153..2a6a41424 100644 --- a/web/skins/classic/views/video.php +++ b/web/skins/classic/views/video.php @@ -18,8 +18,8 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canView( 'Events' ) ) { - $view = "error"; +if ( !canView('Events') ) { + $view = 'error'; return; } @@ -28,31 +28,31 @@ require_once('includes/Event.php'); $eid = validInt($_REQUEST['eid']); $sql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultRate,M.DefaultScale FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE E.Id = ?'; -$sql_values = array( $eid ); +$sql_values = array($eid); if ( $user['MonitorIds'] ) { - $monitor_ids = explode( ',', $user['MonitorIds'] ); - $sql .= ' AND MonitorId IN (' .implode( ',', array_fill(0,count($monitor_ids),'?') ) . ')'; - $sql_values = array_merge( $sql_values, $monitor_ids ); + $monitor_ids = explode(',', $user['MonitorIds']); + $sql .= ' AND MonitorId IN ('.implode(',', array_fill(0,count($monitor_ids),'?')).')'; + $sql_values = array_merge($sql_values, $monitor_ids); } -$event = dbFetchOne( $sql, NULL, $sql_values ); +$event = dbFetchOne($sql, NULL, $sql_values); -if ( isset( $_REQUEST['rate'] ) ) +if ( isset($_REQUEST['rate']) ) $rate = validInt($_REQUEST['rate']); else - $rate = reScale( RATE_BASE, $event['DefaultRate'], ZM_WEB_DEFAULT_RATE ); -if ( isset( $_REQUEST['scale'] ) ) + $rate = reScale(RATE_BASE, $event['DefaultRate'], ZM_WEB_DEFAULT_RATE); +if ( isset($_REQUEST['scale']) ) $scale = validInt($_REQUEST['scale']); else - $scale = reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); + $scale = reScale(SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE); -$Event = new Event( $event['Id'] ); +$Event = new Event($event['Id']); $eventPath = $Event->Path(); $videoFormats = array(); -$ffmpegFormats = preg_split( '/\s+/', ZM_FFMPEG_FORMATS ); +$ffmpegFormats = preg_split('/\s+/', ZM_FFMPEG_FORMATS); foreach ( $ffmpegFormats as $ffmpegFormat ) { - if ( preg_match( '/^([^*]+)(\*\*?)$/', $ffmpegFormat, $matches ) ) { + if ( preg_match('/^([^*]+)(\*\*?)$/', $ffmpegFormat, $matches) ) { $videoFormats[$matches[1]] = $matches[1]; if ( !isset($videoFormat) && $matches[2] == '*' ) { $videoFormat = $matches[1]; @@ -63,42 +63,44 @@ foreach ( $ffmpegFormats as $ffmpegFormat ) { } $videoFiles = array(); -if ( $dir = opendir( $eventPath ) ) { - while ( ($file = readdir( $dir )) !== false ) { +if ( $dir = opendir($eventPath) ) { + while ( ($file = readdir($dir)) !== false ) { $file = $eventPath.'/'.$file; - if ( is_file( $file ) ) { - if ( preg_match( '/\.(?:'.join( '|', $videoFormats ).')$/', $file ) ) { + if ( is_file($file) ) { + if ( preg_match('/\.(?:'.join('|', $videoFormats).')$/', $file) ) { $videoFiles[] = $file; } } } - closedir( $dir ); + closedir($dir); } if ( isset($_REQUEST['deleteIndex']) ) { $deleteIndex = validInt($_REQUEST['deleteIndex']); - unlink( $videoFiles[$deleteIndex] ); - unset( $videoFiles[$deleteIndex] ); + unlink($videoFiles[$deleteIndex]); + unset($videoFiles[$deleteIndex]); } if ( isset($_REQUEST['downloadIndex']) ) { + // can't be output buffering, as this file might be large + ob_end_clean(); $downloadIndex = validInt($_REQUEST['downloadIndex']); - header( 'Pragma: public' ); - header( 'Expires: 0' ); - header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' ); - header( 'Cache-Control: private', false ); // required by certain browsers - header( 'Content-Description: File Transfer' ); - header( 'Content-disposition: attachment; filename="'.basename($videoFiles[$downloadIndex]).'"' ); // basename is required because the video index contains the path and firefox doesn't strip the path but simply replaces the slashes with an underscore. - header( 'Content-Transfer-Encoding: binary' ); - header( 'Content-Type: application/force-download' ); - header( 'Content-Length: '.filesize($videoFiles[$downloadIndex]) ); - readfile( $videoFiles[$downloadIndex] ); + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Cache-Control: private', false); // required by certain browsers + header('Content-Description: File Transfer'); + header('Content-disposition: attachment; filename="'.basename($videoFiles[$downloadIndex]).'"'); // basename is required because the video index contains the path and firefox doesn't strip the path but simply replaces the slashes with an underscore. + header('Content-Transfer-Encoding: binary'); + header('Content-Type: application/force-download'); + header('Content-Length: '.filesize($videoFiles[$downloadIndex])); + readfile($videoFiles[$downloadIndex]); exit; } $focusWindow = true; -xhtmlHeaders(__FILE__, translate('Video') ); +xhtmlHeaders(__FILE__, translate('Video')); ?>
@@ -112,30 +114,30 @@ xhtmlHeaders(__FILE__, translate('Video') ); -

-
+

+
- +
- + - + - + @@ -143,16 +145,22 @@ if ( isset($_REQUEST['showIndex']) ) {
- disabled="disabled"/> + disabled="disabled"/>
-

+

+ + +

- + @@ -164,7 +172,7 @@ if ( isset($_REQUEST['showIndex']) ) { - +
@@ -178,29 +186,29 @@ if ( isset($_REQUEST['showIndex']) ) { 0 ) { - preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches ); - if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) ) { + if ( filesize($file) > 0 ) { + preg_match('/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches); + if ( preg_match('/^r(.+)$/', $matches[2], $temp_matches) ) { $rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); $rateText = isset($rates[$rate])?$rates[$rate]:($rate."x"); - } elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) ) { - $rateText = $temp_matches[1]."fps"; + } elseif ( preg_match('/^F(.+)$/', $matches[2], $temp_matches) ) { + $rateText = $temp_matches[1].'fps'; } - if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) ) { - $scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); - $scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x"); - } elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) ) { + if ( preg_match('/^s(.+)$/', $matches[3], $temp_matches) ) { + $scale = (int)(100 * preg_replace('/_/', '.', $temp_matches[1]) ); + $scaleText = isset($scales[$scale])?$scales[$scale]:($scale.'x'); + } elseif ( preg_match('/^S(.+)$/', $matches[3], $temp_matches) ) { $scaleText = $temp_matches[1]; } - $width = $scale?reScale( $event['Width'], $scale ):$event['Width']; - $height = $scale?reScale( $event['Height'], $scale ):$event['Height']; + $width = $scale?reScale($event['Width'], $scale):$event['Width']; + $height = $scale?reScale($event['Height'], $scale):$event['Height']; ?> - + - + Delta(1); $Frame->FrameId('snapshot'); } - $path = $Event->Path().'/snapshot.jpg'; + $Monitor = $Event->Monitor(); + if ( $Monitor->SaveJPEGs() & 1 ) { + # If we store Frames as jpgs, then we don't store a snapshot + $path = $Event->Path().'/'.sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d',$Frame->FrameId()).'-'.$show.'.jpg'; + } else { + $path = $Event->Path().'/snapshot.jpg'; + } } else { $Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid'], 'FrameId'=>$_REQUEST['fid']));
 /  /  /  /