Merge branch 'master' into cycle_width

This commit is contained in:
Isaac Connor 2018-07-11 15:49:44 -04:00
commit 410b1ad810
52 changed files with 977 additions and 15343 deletions

View File

@ -802,6 +802,24 @@ if(WITH_SYSTEMD)
endif(NOT POLKIT_FOUND)
endif(WITH_SYSTEMD)
# Find the path to an arp compatible executable
if(ZM_PATH_ARP STREQUAL "")
find_program(ARP_EXECUTABLE arp)
if(ARP_EXECUTABLE)
set(ZM_PATH_ARP "${ARP_EXECUTABLE}")
mark_as_advanced(ARP_EXECUTABLE)
else(ARP_EXECUTABLE)
find_program(ARP_EXECUTABLE ip)
if(ARP_EXECUTABLE)
set(ZM_PATH_ARP "${ARP_EXECUTABLE} neigh")
mark_as_advanced(ARP_EXECUTABLE)
endif(ARP_EXECUTABLE)
endif(ARP_EXECUTABLE)
if(ARP_EXECUTABLE-NOTFOUND)
message(WARNING "Unable to find a compatible arp binary. Monitor probe will not function." )
endif(ARP_EXECUTABLE-NOTFOUND)
endif(ZM_PATH_ARP STREQUAL "")
# Some variables that zm expects
set(ZM_PID "${ZM_RUNDIR}/zm.pid")
set(ZM_CONFIG "${ZM_CONFIG_DIR}/zm.conf")

View File

@ -13,6 +13,7 @@ Build-Depends: debhelper (>= 9), cmake
, libv4l-dev (>= 0.8.3)
, libbz2-dev
, ffmpeg | libav-tools
, net-tools
, libnetpbm10-dev
, libvlccore-dev, libvlc-dev
, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev

View File

@ -8,16 +8,6 @@ SET(zmgid_final www)
SET(webroot /srv/www/htdocs)
SET(zm_webdir ${webroot}/zoneminder)
# Download jscalendar & move files into position
file(DOWNLOAD http://downloads.sourceforge.net/jscalendar/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip STATUS download_jsc)
if(download_jsc EQUAL 0)
message(STATUS "Jscalander successfully downloaded. Installing...")
execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ERROR_VARIABLE unzip_jsc)
message(STATUS "Status of jscalender script was: ${unzip_jsc}")
else(download_jsc EQUAL 0)
message(STATUS "Unable to download optional jscalander. Skipping...")
endif(download_jsc EQUAL 0)
# Create several empty folders
file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp)
@ -45,7 +35,3 @@ install(FILES zoneminder.tmpfiles DESTINATION /etc/tmpfiles.d RENAME zoneminder.
install(FILES redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
# Install jscalendar
if(unzip_jsc STREQUAL "")
install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar)
endif(unzip_jsc STREQUAL "")

View File

@ -1,7 +0,0 @@
#!/bin/bash
unzip -o jscalendar-1.0.zip
mkdir -v jscalendar-doc
cd jscalendar-1.0
mv -v *html *php doc/* README ../jscalendar-doc
rmdir -v doc

View File

@ -16,7 +16,6 @@ Version: 1.27.0
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
# jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/
# Mootools is under the MIT license: http://mootools.net/
License: GPLv2+ and LGPLv2+ and MIT
URL: http://www.zoneminder.com/
@ -141,7 +140,7 @@ fi
%files
%defattr(-,root,root,-)
%doc AUTHORS COPYING README.md distros/opensuse/README.OpenSuse distros/opensuse/jscalendar-doc
%doc AUTHORS COPYING README.md distros/opensuse/README.OpenSuse
%docdir /opt/zoneminder/share/man
%config %attr(640,root,%{zmgid_final}) /etc/zm.conf
%config(noreplace) %attr(644,root,root) /etc/apache2/conf.d/zoneminder.conf

View File

@ -27,18 +27,6 @@ else(ZM_WEB_USER STREQUAL "nginx")
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
endif(ZM_WEB_USER STREQUAL "nginx")
# Unpack jscalendar & move files into position
message(STATUS "Unpacking and Installing jscalendar...")
execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/misc/jscalendar.sh
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
ERROR_VARIABLE unzip_jsc
)
if("${unzip_jsc}" STREQUAL "")
message(STATUS "jscalendar successfully installed.")
else("${unzip_jsc}" STREQUAL "")
message(FATAL_ERROR "\nAn error occurred while jscalendar was being processed:\n${unzip_jsc}")
endif("${unzip_jsc}" STREQUAL "")
# Create several empty folders
file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp)
@ -58,7 +46,6 @@ install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DE
# Install auxiliary files
install(FILES misc/redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar)
# Install zoneminder service files
install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)

View File

@ -1,7 +0,0 @@
#!/bin/bash
unzip -o misc/jscalendar-1.0.zip
mkdir -v jscalendar-doc
cd jscalendar-1.0
mv -v *html *php doc/* README ../jscalendar-doc
rmdir -v doc

View File

@ -30,7 +30,6 @@ Version: 1.31.44
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
# jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/
# Mootools is inder the MIT license: http://mootools.net/
# CakePHP is under the MIT license: https://github.com/cakephp/cakephp
# Crud is under the MIT license: https://github.com/FriendsOfCake/crud
@ -53,6 +52,7 @@ BuildRequires: pcre-devel
BuildRequires: libjpeg-turbo-devel
BuildRequires: findutils
BuildRequires: coreutils
BuildRequires: net-tools
BuildRequires: perl
BuildRequires: perl-generators
BuildRequires: perl(Archive::Tar)
@ -252,7 +252,7 @@ EOF
%files
%license COPYING
%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https distros/redhat/jscalendar-doc
%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https
# We want these two folders to have "normal" read permission
# compared to the folder contents

View File

@ -11,6 +11,8 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa
,libavformat-dev (>= 6:10~)
,libavutil-dev (>= 6:10~)
,libswscale-dev (>= 6:10~)
,ffmpeg | libav-tools
,net-tools
,libbz2-dev
,libgcrypt-dev | libgcrypt11-dev
,libcurl4-gnutls-dev

View File

@ -347,14 +347,14 @@ sub delete {
my $event = $_[0];
if ( ! ( $event->{Id} and $event->{MonitorId} and $event->{StartTime} ) ) {
my ( $caller, undef, $line ) = caller;
Warning( "Can't Delete event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime} from $caller:$line\n" );
Warning("Can't Delete event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime} from $caller:$line\n");
return;
}
if ( ! -e $event->Storage()->Path() ) {
Warning("Not deleting event because storage path doesn't exist");
return;
}
Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}\n" );
Info("Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}\n");
$ZoneMinder::Database::dbh->ping();
$ZoneMinder::Database::dbh->begin_work();
@ -362,9 +362,9 @@ sub delete {
{
my $sql = 'DELETE FROM Frames WHERE EventId=?';
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql)
or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
my $res = $sth->execute($event->{Id})
or Error( "Can't execute '$sql': ".$sth->errstr() );
$sth->finish();
if ( $ZoneMinder::Database::dbh->errstr() ) {
@ -373,10 +373,10 @@ sub delete {
}
$sql = 'DELETE FROM Stats WHERE EventId=?';
$sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
$res = $sth->execute( $event->{Id} )
or Error( "Can't execute '$sql': ".$sth->errstr() );
$sth = $ZoneMinder::Database::dbh->prepare_cached($sql)
or Error("Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr());
$res = $sth->execute($event->{Id})
or Error("Can't execute '$sql': ".$sth->errstr());
$sth->finish();
if ( $ZoneMinder::Database::dbh->errstr() ) {
$ZoneMinder::Database::dbh->commit();
@ -387,10 +387,10 @@ sub delete {
# Do it individually to avoid locking up the table for new events
{
my $sql = 'DELETE FROM Events WHERE Id=?';
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Error( "Can't execute '$sql': ".$sth->errstr() );
my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql)
or Error("Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr());
my $res = $sth->execute($event->{Id})
or Error("Can't execute '$sql': ".$sth->errstr());
$sth->finish();
}
$ZoneMinder::Database::dbh->commit();

View File

@ -557,8 +557,8 @@ our $hasJSONAny = 0;
sub _testJSON {
return if ( $testedJSON );
my $result = eval {
require JSON::Any;
JSON::Any->import();
require JSON::MaybeXS;
JSON::MaybeXS->import();
};
$testedJSON = 1;
$hasJSONAny = 1 if ( $result );
@ -581,7 +581,7 @@ sub jsonEncode {
_testJSON();
if ( $hasJSONAny ) {
my $string = eval { JSON::Any->objToJson( $value ) };
my $string = eval { JSON::MaybeXS->encode_json( $value ) };
Fatal( "Unable to encode object to JSON: $@" ) unless( $string );
return( $string );
}
@ -616,7 +616,7 @@ sub jsonDecode {
_testJSON();
if ( $hasJSONAny ) {
my $object = eval { JSON::Any->jsonToObj( $value ) };
my $object = eval { JSON::MaybeXS->decode_json( $value ) };
Fatal( "Unable to decode JSON string '$value': $@" ) unless( $object );
return( $object );
}

View File

@ -487,7 +487,7 @@ sub openSyslog {
sub closeSyslog {
my $this = shift;
#closelog();
closelog();
}
sub logFile {
@ -530,29 +530,29 @@ 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() ) ) {
@ -576,7 +576,7 @@ sub logPrint {
, $this->{id}
, $$
, $level
, $code
, $codes{$level}
, $string
, $this->{fileName}
);

View File

@ -252,36 +252,37 @@ MAIN: while( $loop ) {
foreach my $day_dir ( @day_dirs ) {
Debug( "Checking day dir $day_dir" );
( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint
if ( ! chdir( $day_dir ) ) {
Error( "Can't chdir to '$$Storage{Path}/$day_dir': $!" );
if ( !chdir($day_dir) ) {
Error("Can't chdir to '$$Storage{Path}/$day_dir': $!");
next;
}
if ( ! opendir( DIR, '.' ) ) {
Error( "Can't open directory '$$Storage{Path}/$day_dir': $!" );
if ( ! opendir(DIR, '.') ) {
Error("Can't open directory '$$Storage{Path}/$day_dir': $!");
next;
}
my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR );
closedir( DIR );
Debug("Have " . @event_links . ' event links');
closedir(DIR);
my $count = 0;
foreach my $event_link ( @event_links ) {
if ( $event_link =~ /[^\d\.]/ ) {
Warning("Non-event link found $event_link in $day_dir, skipping");
next;
}
Debug( "Checking link $event_link" );
Debug("Checking link $event_link");
( my $event = $event_link ) =~ s/^.*\.//;
#Event path is hour/minute/sec
my $event_path = readlink( $event_link );
my $event_path = readlink($event_link);
if ( !($event_path and -e $event_path) ) {
aud_print( "Event link $day_dir/$event_link does not point to valid target" );
aud_print("Event link $day_dir/$event_link does not point to valid target");
if ( confirm() ) {
( $event_link ) = ( $event_link =~ /^(.*)$/ ); # De-taint
unlink( $event_link );
unlink($event_link);
$cleaned = 1;
}
} else {
Debug( "Checking link $event_link points to $event_path " );
Debug("Checking link $event_link points to $event_path ");
my $Event = $fs_events->{$event} = new ZoneMinder::Event();
$$Event{Id} = $event;
$$Event{Path} = join('/', $Storage->Path(), $day_dir,$event_path);
@ -292,6 +293,42 @@ MAIN: while( $loop ) {
$Event->DiskSpace( undef );
} # event path exists
} # end foreach event_link
# Now check for events that have lost their link
my @time_dirs = glob('[0-9][0-9]/[0-9][0-9]/[0-9][0-9]');
foreach my $event_dir ( @time_dirs ) {
Debug("Checking time dir $event_dir");
( $event_dir ) = ( $event_dir =~ /^(.*)$/ ); # De-taint
my $event_id = undef;
my @mp4_files = glob("$event_dir/[0-9]+\-video.mp4");
foreach my $mp4_file ( @mp4_files ) {
my ( $id ) = $mp4_file =~ /^([0-9]+)\-video\.mp4$/;
if ( $id ) {
$event_id = $id;
last;
}
}
if ( $event_id ) {
my $Event = $fs_events->{$event_id} = new ZoneMinder::Event();
$$Event{Id} = $event_id;
$$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_dir);
$$Event{RelativePath} = join('/', $day_dir, $event_dir);
$$Event{Scheme} = 'Deep';
$Event->MonitorId( $monitor_dir );
$Event->StorageId( $Storage->Id() );
$Event->DiskSpace( undef );
} else {
aud_print("Deleting event directories with no event id information at $day_dir/$event_dir");
if ( confirm() ) {
my $command = "rm -rf $event_dir";
executeShellCommand( $command );
$cleaned = 1;
}
} # end if able to find id
} # end foreach event_dir without link
chdir( $Storage->Path() );
} # end foreach day dir
}
@ -701,17 +738,18 @@ FROM Frames WHERE EventId=?';
if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^(.*)s$/ ) {
$Config{ZM_LOG_DATABASE_LIMIT} = $1;
}
my $deleteLogByTimeSql =
'DELETE low_priority FROM Logs
WHERE TimeKey < unix_timestamp(now() - interval '.$Config{ZM_LOG_DATABASE_LIMIT}.')';
my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql )
or Fatal( "Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr() );
$res = $deleteLogByTimeSth->execute()
or Fatal( "Can't execute: ".$deleteLogByTimeSth->errstr() );
if ( $deleteLogByTimeSth->rows() ){
aud_print( 'Deleted '.$deleteLogByTimeSth->rows()
." log table entries by time\n" );
}
my $deleted_rows;
do {
my $deleteLogByTimeSql =
'DELETE FROM Logs
WHERE TimeKey < unix_timestamp(now() - interval '.$Config{ZM_LOG_DATABASE_LIMIT}.') LIMIT 10';
my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql )
or Fatal( "Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr() );
$res = $deleteLogByTimeSth->execute()
or Fatal( "Can't execute: ".$deleteLogByTimeSth->errstr() );
$deleted_rows = $deleteLogByTimeSth->rows();
aud_print( "Deleted $deleted_rows log table entries by time\n" );
} while ( $deleted_rows );
}
} # end if ZM_LOG_DATABASE_LIMIT
$loop = $continuous;

View File

@ -370,7 +370,6 @@ sub run {
restartPending();
check_for_processes_to_kill() if %terminating_processes;
reaper() if %pids_to_reap;
} # end while
dPrint(ZoneMinder::Logger::INFO, 'Server exiting at '
@ -437,7 +436,7 @@ sub start {
$dbh = zmDbConnect(1);
# This logReinit is required. Not sure why.
logReinit();
#logReinit();
$process->{pid} = $cpid;
$process->{started} = time();
@ -514,7 +513,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();
}
@ -647,7 +646,6 @@ sub chld_sig_handler {
sub reaper {
foreach my $cpid ( keys %pids_to_reap ) {
my $process = $pid_hash{$cpid};
Debug("Reaping pricess $cpid");
delete $pid_hash{$cpid};
my $reap_info = $pids_to_reap{$cpid};
my ( $status, $stopped ) = @$reap_info{'status','stopped'};
@ -713,7 +711,6 @@ Debug("Reaping pricess $cpid");
} else {
delete $cmd_hash{$$process{command}};
}
Debug("Reaping pricess $cpid");
} # end foreach pid_to_reap
} # end sub reaper

View File

@ -70,16 +70,18 @@ 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(
'filter=s' =>\$filter_name,
'filter_id=s' =>\$filter_id,
'version' =>\$version
) or pod2usage(-exitstatus => -1);
daemon =>\$daemon,
'filter=s' =>\$filter_name,
'filter_id=s' =>\$filter_id,
version =>\$version
) or pod2usage(-exitstatus => -1);
if ( $version ) {
print ZoneMinder::Base::ZM_VERSION . "\n";
@ -96,15 +98,16 @@ use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)
logInit($filter_id?(id=>'zmfilter_'.$filter_id):());
sub HupHandler {
Info("Received HUP, reloading");
Info('Received HUP, reloading');
ZoneMinder::Object::init_cache();
&ZoneMinder::Logger::logHupHandler();
}
sub TermHandler {
Info("Received TERM, exiting");
Info('Received TERM, exiting');
$zm_terminate = 1;
}
sub Term {
exit( 0 );
exit(0);
}
$SIG{HUP} = \&HupHandler;
$SIG{TERM} = \&TermHandler;
@ -173,7 +176,7 @@ if ( $filter_name ) {
}
if ( ! ( $filter_name or $filter_id ) ) {
Debug("Sleeping due to start delay: " . START_DELAY . ' seconds...');
Debug('Sleeping due to start delay: ' . START_DELAY . ' seconds...');
sleep(START_DELAY);
}
@ -201,7 +204,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);
@ -265,8 +268,7 @@ sub checkFilter {
Info(
join(' ',
'Checking filter', $filter->{Name},
join( ', ',
join(', ',
($filter->{AutoDelete}?'delete':()),
($filter->{AutoArchive}?'archive':()),
($filter->{AutoVideo}?'video':()),
@ -383,7 +385,7 @@ sub generateVideo {
$format = $ffmpeg_formats[0];
}
my $command = join( '',
my $command = join('',
$Config{ZM_PATH_BIN},
'/zmvideo.pl -e ',
$event->{Id},
@ -395,7 +397,7 @@ sub generateVideo {
$format,
);
my $output = qx($command);
chomp( $output );
chomp($output);
my $status = $? >> 8;
if ( $status || logDebugging() ) {
Debug("Output: $output\n");

View File

@ -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__

View File

@ -685,14 +685,14 @@ int FfmpegCamera::Close() {
//Function to handle capture and store
int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event_file ) {
if ( ! mCanCapture ) {
if ( !mCanCapture ) {
return -1;
}
int ret;
static char errbuf[AV_ERROR_MAX_STRING_SIZE];
int frameComplete = false;
while ( ! frameComplete ) {
while ( !frameComplete ) {
av_init_packet(&packet);
ret = av_read_frame(mFormatContext, &packet);
@ -874,18 +874,18 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
}
#if HAVE_AVUTIL_HWCONTEXT_H
if ( hwaccel ) {
ret = avcodec_receive_frame( mVideoCodecContext, hwFrame );
ret = avcodec_receive_frame(mVideoCodecContext, hwFrame);
if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
zm_av_packet_unref( &packet );
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
Error("Unable to send packet at frame %d: %s, continuing", frameCount, errbuf);
zm_av_packet_unref(&packet);
continue;
}
ret = av_hwframe_transfer_data(mRawFrame, hwFrame, 0);
if (ret < 0) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
Error( "Unable to transfer frame at frame %d: %s, continuing", frameCount, errbuf );
zm_av_packet_unref( &packet );
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
Error("Unable to transfer frame at frame %d: %s, continuing", frameCount, errbuf);
zm_av_packet_unref(&packet);
continue;
}
} else {

View File

@ -1234,11 +1234,11 @@ bool Monitor::CheckSignal( const Image *image ) {
bool Monitor::Analyse() {
if ( shared_data->last_read_index == shared_data->last_write_index ) {
// I wonder how often this happens. Maybe if this happens we should sleep or something?
return( false );
return false;
}
struct timeval now;
gettimeofday( &now, NULL );
gettimeofday(&now, NULL);
if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) {
if ( now.tv_sec != last_fps_time ) {
@ -1547,7 +1547,7 @@ bool Monitor::Analyse() {
if ( score ) {
if ( state == IDLE || state == TAPE || state == PREALARM ) {
if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count) ) {
Info("%s: %03d - Gone into alarm state %u > %u",
Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u",
name, image_count, Event::PreAlarmCount(), alarm_frame_count);
shared_data->state = state = ALARM;
if ( signal_change || (function != MOCORD && state != ALERT) ) {
@ -1576,6 +1576,9 @@ bool Monitor::Analyse() {
else
pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count;
Debug(4,"Resulting pre_index(%d) from index(%d) + image_buffer_count(%d) - pre_event_count(%d) % %d",
pre_index, index, image_buffer_count, pre_event_count, image_buffer_count);
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) {

View File

@ -55,6 +55,10 @@ RemoteCamera::~RemoteCamera() {
freeaddrinfo(hp);
hp = NULL;
}
if ( mAuthenticator ) {
delete mAuthenticator;
mAuthenticator = NULL;
}
}
void RemoteCamera::Initialise() {

View File

@ -27,15 +27,14 @@
namespace zm {
Authenticator::Authenticator( const std::string &username, const std::string &password) :
fCnonce( "0a4f113b" ),
fCnonce("0a4f113b"),
fUsername(username),
fPassword(password)
{
#ifdef HAVE_GCRYPT_H
// Special initialisation for libgcrypt
if ( !gcry_check_version( GCRYPT_VERSION ) )
{
Fatal( "Unable to initialise libgcrypt" );
if ( !gcry_check_version(GCRYPT_VERSION) ) {
Fatal("Unable to initialise libgcrypt");
}
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
@ -64,36 +63,34 @@ void Authenticator::authHandleHeader(std::string headerData)
size_t digest_match_len = strlen(digest_match);
// Check if basic auth
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
{
if ( strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0 ) {
fAuthMethod = AUTH_BASIC;
Debug( 2, "Set authMethod to Basic");
Debug(2, "Set authMethod to Basic");
}
// Check if digest auth
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
{
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0) {
fAuthMethod = AUTH_DIGEST;
Debug( 2, "Set authMethod to Digest");
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
// subparts are key="value"
for ( size_t i = 0; i < subparts.size(); i++ )
{
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
std::string key = trimSpaces( kvPair[0] );
if (key == "realm") {
fRealm = trimSet( kvPair[1], "\"");
for ( size_t i = 0; i < subparts.size(); i++ ) {
StringVector kvPair = split(trimSpaces(subparts[i]), "=");
std::string key = trimSpaces(kvPair[0]);
if ( key == "realm" ) {
fRealm = trimSet(kvPair[1], "\"");
continue;
}
if (key == "nonce") {
fNonce = trimSet( kvPair[1], "\"");
if ( key == "nonce" ) {
fNonce = trimSet(kvPair[1], "\"");
continue;
}
if (key == "qop") {
fQop = trimSet( kvPair[1], "\"");
if ( key == "qop" ) {
fQop = trimSet(kvPair[1], "\"");
continue;
}
}
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
Debug(2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s",
username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str());
}
}
@ -103,12 +100,9 @@ std::string Authenticator::quote( const std::string &src ) {
std::string Authenticator::getAuthHeader(std::string method, std::string uri) {
std::string result = "Authorization: ";
if (fAuthMethod == AUTH_BASIC)
{
if (fAuthMethod == AUTH_BASIC) {
result += "Basic " + base64Encode( username() + ":" + password() );
}
else if (fAuthMethod == AUTH_DIGEST)
{
} else if (fAuthMethod == AUTH_DIGEST) {
result += std::string("Digest ") +
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
@ -153,8 +147,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
#endif
for ( unsigned int j = 0; j < md5len; j++ )
{
for ( unsigned int j = 0; j < md5len; j++ ) {
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
}
md5HexBuf[md5len*2]='\0';
@ -169,8 +162,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
#endif
for ( unsigned int j = 0; j < md5len; j++ )
{
for ( unsigned int j = 0; j < md5len; j++ ) {
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
}
md5HexBuf[md5len*2]='\0';
@ -191,22 +183,21 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
#endif
for ( unsigned int j = 0; j < md5len; j++ )
{
for ( unsigned int j = 0; j < md5len; j++ ) {
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
}
md5HexBuf[md5len*2]='\0';
return md5HexBuf;
#else // HAVE_DECL_MD5
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
return( 0 );
Error("You need to build with gnutls or openssl installed to use digest authentication");
return 0;
#endif // HAVE_DECL_MD5
}
void Authenticator::checkAuthResponse(std::string &response) {
std::string authLine;
StringVector lines = split( response, "\r\n" );
StringVector lines = split(response, "\r\n");
const char* authenticate_match = "WWW-Authenticate:";
size_t authenticate_match_len = strlen(authenticate_match);

View File

@ -366,5 +366,5 @@ int main(int argc, char *argv[]) {
logTerm();
zmDbClose();
return result;
return zm_terminate ? 0 : result;
}

View File

@ -78,35 +78,42 @@ class AppController extends Controller {
if ( $zmOptAuth == '1' ) {
require_once "../../../includes/auth.php";
$this->loadModel('User');
global $user;
$user = $this->Session->read('user');
if ( isset($_REQUEST['user']) and isset($_REQUEST['pass']) ) {
$user = $this->User->find('first', array ('conditions' => array (
'User.Username' => $_REQUEST['user'],
'User.Password' => $_REQUEST['pass'],
)) );
if ( ! $user ) {
$user = userLogin($_REQUEST['user'],$_REQUEST['pass']);
if ( !$user ) {
throw new UnauthorizedException(__('User not found'));
return;
} else {
$this->Session->Write( 'user.Username', $user['User']['Username'] );
$this->Session->Write( 'user.Enabled', $user['User']['Enabled'] );
}
}
if ( isset($_REQUEST['auth']) ) {
$user = getAuthUser($_REQUEST['auth']);
if ( ! $user ) {
throw new UnauthorizedException(__('User not found'));
return;
} else {
if ( ! $this->Session->Write('user.Username', $user['Username']) )
$this->log("Error writing session var user.Username");
if ( ! $this->Session->Write('user.Enabled', $user['Enabled']) )
$this->log("Error writing session var user.Enabled");
}
} # end if REQUEST['auth']
if ( 0 and $user ) {
# We have to redo the session variables because cakephp's Session code will overwrite the normal php session
# Actually I'm not sure that is true. Getting indeterminate behaviour
Logger::Debug("user.Username: " . $this->Session->read('user.Username'));
if ( ! $this->Session->Write('user', $user) )
$this->log("Error writing session var user");
Logger::Debug("user.Username: " . $this->Session->read('user.Username'));
if ( ! $this->Session->Write('user.Username', $user['Username']) )
$this->log("Error writing session var user.Username");
if ( ! $this->Session->Write('password', $user['Password']) )
$this->log("Error writing session var user.Username");
if ( ! $this->Session->Write('user.Enabled', $user['Enabled']) )
$this->log("Error writing session var user.Enabled");
if ( ! $this->Session->Write('remoteAddr', $_SERVER['REMOTE_ADDR']) )
$this->log("Error writing session var remoteAddr");
}
if ( ! $this->Session->read('user.Username') ) {
throw new UnauthorizedException(__('Not Authenticated'));
return;
@ -115,14 +122,12 @@ class AppController extends Controller {
return;
}
$options = array ('conditions' => array ('User.Username' => $this->Session->Read('user.Username')));
$userMonitors = $this->User->find('first', $options);
$this->Session->Write('allowedMonitors',$userMonitors['User']['MonitorIds']);
$this->Session->Write('streamPermission',$userMonitors['User']['Stream']);
$this->Session->Write('eventPermission',$userMonitors['User']['Events']);
$this->Session->Write('controlPermission',$userMonitors['User']['Control']);
$this->Session->Write('systemPermission',$userMonitors['User']['System']);
$this->Session->Write('monitorPermission',$userMonitors['User']['Monitors']);
$this->Session->Write('allowedMonitors',$user['MonitorIds']);
$this->Session->Write('streamPermission',$user['Stream']);
$this->Session->Write('eventPermission',$user['Events']);
$this->Session->Write('controlPermission',$user['Control']);
$this->Session->Write('systemPermission',$user['System']);
$this->Session->Write('monitorPermission',$user['Monitors']);
} else {
// if auth is not on, you can do everything
//$userMonitors = $this->User->find('first', $options);
@ -133,8 +138,5 @@ class AppController extends Controller {
$this->Session->Write('systemPermission','Edit');
$this->Session->Write('monitorPermission','Edit');
}
} # end function beforeFilter()
}

View File

@ -2,7 +2,7 @@
App::uses('AppController', 'Controller');
class HostController extends AppController {
public $components = array('RequestHandler');
public function daemonCheck($daemon=false, $args=false) {
@ -30,37 +30,34 @@ class HostController extends AppController {
));
}
function getCredentials() {
function getCredentials() {
// ignore debug warnings from other functions
$this->view='Json';
$credentials = "";
$appendPassword = 0;
$this->loadModel('Config');
$this->view='Json';
$credentials = '';
$appendPassword = 0;
$this->loadModel('Config');
$isZmAuth = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH')))['Config']['Value'];
if ($isZmAuth) {
$zmAuthRelay = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY')))['Config']['Value'];
if ($zmAuthRelay == 'hashed') {
$zmAuthHashIps= $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_IPS')))['Config']['Value'];
$credentials = 'auth='.generateAuthHash($zmAuthHashIps);
}
elseif ($zmAuthRelay == 'plain') {
// user will need to append the store password here
$credentials = "user=".$this->Session->read('user.Username')."&pass=";
$appendPassword = 1;
}
elseif ($zmAuthRelay == 'none') {
$credentials = "user=".$this->Session->read('user.Username');
}
if ( $isZmAuth ) {
$zmAuthRelay = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY')))['Config']['Value'];
if ( $zmAuthRelay == 'hashed' ) {
$zmAuthHashIps= $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_IPS')))['Config']['Value'];
$credentials = 'auth='.generateAuthHash($zmAuthHashIps);
} else if ( $zmAuthRelay == 'plain' ) {
// user will need to append the store password here
$credentials = 'user='.$this->Session->read('user.Username').'&pass=';
$appendPassword = 1;
} else if ( $zmAuthRelay == 'none' ) {
$credentials = 'user='.$this->Session->read('user.Username');
}
}
$this->set(array(
'credentials'=> $credentials,
'append_password'=>$appendPassword,
'_serialize' => array('credentials', 'append_password')
) );
}
}
// If $mid is set, only return disk usage for that monitor
// Else, return an array of total disk usage, and per-monitor
@ -70,7 +67,7 @@ class HostController extends AppController {
$this->loadModel('Monitor');
// If $mid is passed, see if it is valid
if ($mid) {
if ($mid) {
if (!$this->Monitor->exists($mid)) {
throw new NotFoundException(__('Invalid monitor'));
}

View File

@ -0,0 +1,172 @@
<?php
App::uses('AppController', 'Controller');
/**
* Users Controller
*
* @property User $User
*/
class UsersController extends AppController {
/**
* Components
*
* @var array
*/
public $components = array('RequestHandler', 'Paginator');
/**
* index method
*
* @return void
* This also creates a thumbnail for each user.
*/
public function index() {
$this->User->recursive = 0;
$users = $this->Paginator->paginate('User');
$this->set(compact('users'));
}
/**
* view method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function view($id = null) {
$this->loadModel('Config');
$configs = $this->Config->find('list', array(
'fields' => array('Name', 'Value'),
'conditions' => array('Name' => array('ZM_DIR_EVENTS'))
));
$this->User->recursive = 1;
if (!$this->User->exists($id)) {
throw new NotFoundException(__('Invalid user'));
}
$options = array('conditions' => array('User.' . $this->User->primaryKey => $id));
$user = $this->User->find('first', $options);
$this->set(array(
'user' => $user,
'_serialize' => array('user')
));
}
/**
* add method
*
* @return void
*/
public function add() {
if ($this->request->is('post')) {
$this->User->create();
if ($this->User->save($this->request->data)) {
return $this->flash(__('The user has been saved.'), array('action' => 'index'));
}
$this->Session->setFlash(
__('The user could not be saved. Please, try again.')
);
}
}
/**
* edit method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function edit($id = null) {
$this->User->id = $id;
if (!$this->User->exists($id)) {
throw new NotFoundException(__('Invalid user'));
}
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->User->save($this->request->data)) {
$message = 'Saved';
} else {
$message = 'Error';
}
} else {
$this->request->data = $this->User->read(null, $id);
unset($this->request->data['User']['password']);
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
}
/**
* delete method
*
* @throws NotFoundException
* @param string $id
* @return void
*/
public function delete($id = null) {
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
$this->request->allowMethod('post', 'delete');
if ($this->User->delete()) {
$message = 'The user has been deleted.';
} else {
$message = 'The user could not be deleted. Please, try again.';
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
}
public function beforeFilter() {
parent::beforeFilter();
$this->loadModel('Config');
$configs = $this->Config->find('list', array(
'fields' => array('Name', 'Value'),
'conditions' => array('Name' => array('ZM_OPT_USE_AUTH'))
));
if ( $configs['ZM_OPT_USE_AUTH'] ) {
$this->Auth->allow('add','logout');
} else {
$this->Auth->allow();
}
}
public function login() {
$this->loadModel('Config');
$configs = $this->Config->find('list', array(
'fields' => array('Name', 'Value'),
'conditions' => array('Name' => array('ZM_OPT_USE_AUTH'))
));
if ( ! $configs['ZM_OPT_USE_AUTH'] ) {
$this->set(array(
'message' => 'Login is not required.',
'_serialize' => array('message')
));
return;
}
if ($this->request->is('post')) {
if ($this->Auth->login()) {
return $this->redirect($this->Auth->redirectUrl());
}
$this->Session->setFlash(__('Invalid username or password, try again'));
}
}
public function logout() {
return $this->redirect($this->Auth->logout());
}
}

View File

@ -1,12 +1,28 @@
<?php
App::uses('AppModel', 'Model');
/**
* User Model
*
* @property Monitor $Monitor
* @property Frame $Frame
*/
class User extends AppModel {
public $validate = array(
'Username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
)
),
'Password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
)
);
/**
* Use table
*
@ -26,6 +42,48 @@ class User extends AppModel {
*
* @var string
*/
public $displayField = 'Name';
public $displayField = 'Username';
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* belongsTo associations
*
* @var array
*/
public $belongsTo = array(
/*'Monitor' => array(
'className' => 'Monitor',
'foreignKey' => 'MonitorId',
'conditions' => '',
'fields' => '',
'order' => ''
)
*/
);
/**
* hasMany associations
*
* @var array
*/
public $hasMany = array(
/*
'Frame' => array(
'className' => 'Frame',
'foreignKey' => 'UserId',
'dependent' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
*/
);
}

View File

@ -0,0 +1,15 @@
<!-- app/View/Users/add.ctp -->
<div class="users form">
<?php echo $this->Form->create('User'); ?>
<fieldset>
<legend><?php echo __('Add User'); ?></legend>
<?php echo $this->Form->input('username');
echo $this->Form->input('password');
echo $this->Form->input('role', array(
'options' => array('admin' => 'Admin', 'author' => 'Author')
));
?>
</fieldset>
<?php echo $this->Form->end(__('Submit')); ?>
</div>

View File

@ -0,0 +1,5 @@
<?php
$array['users'] = $users;
$array['pagination'] = $this->Paginator->params();
echo json_encode($array);
?>

View File

@ -0,0 +1 @@
echo json_encode($user);

View File

@ -0,0 +1,13 @@
<div class="users form">
<?php echo $this->Session->flash('auth'); ?>
<?php echo $this->Form->create('User'); ?>
<fieldset>
<legend>
<?php echo __('Please enter your username and password'); ?>
</legend>
<?php echo $this->Form->input('username');
echo $this->Form->input('password');
?>
</fieldset>
<?php echo $this->Form->end(__('Login')); ?>
</div>

View File

@ -0,0 +1,2 @@
$xml = Xml::fromArray(array('response' => $events));
echo $xml->asXML();

View File

@ -0,0 +1,2 @@
$xml = Xml::fromArray(array('response' => $event));
echo $xml->asXML();

View File

@ -1,5 +1,5 @@
<?php
require_once( 'database.php' );
require_once('database.php');
class Server {
private $defaults = array(

View File

@ -19,7 +19,7 @@
//
function userLogin($username, $password='', $passwordHashed=false) {
global $user, $cookies;
global $user;
$sql = 'SELECT * FROM Users WHERE Enabled=1';
$sql_values = NULL;
@ -34,7 +34,12 @@ function userLogin($username, $password='', $passwordHashed=false) {
$sql .= ' AND Username=?';
$sql_values = array($username);
}
session_start();
$close_session = 0;
if ( !is_session_started() ) {
Logger::Debug("Starting session in userLogin");
session_start();
$close_session = 1;
}
$_SESSION['username'] = $username;
if ( ZM_AUTH_RELAY == 'plain' ) {
// Need to save this in session
@ -54,7 +59,9 @@ function userLogin($username, $password='', $passwordHashed=false) {
$_SESSION['loginFailed'] = true;
unset($user);
}
session_write_close();
if ( $close_session )
session_write_close();
return $user;
} # end function userLogin
function userLogout() {
@ -121,7 +128,11 @@ function generateAuthHash($useRemoteAddr, $force=false) {
#Logger::Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] );
$auth = md5($authKey);
if ( !$force ) {
session_start();
$close_session = 0;
if ( !is_session_started() ) {
session_start();
$close_session = 1;
}
$_SESSION['AuthHash'] = $auth;
$_SESSION['AuthHashGeneratedAt'] = $time;
session_write_close();
@ -155,4 +166,17 @@ function canEdit($area, $mid=false) {
return ( $user[$area] == 'Edit' && ( !$mid || visibleMonitor($mid) ));
}
function is_session_started() {
if ( php_sapi_name() !== 'cli' ) {
if ( version_compare(phpversion(), '5.4.0', '>=') ) {
return session_status() === PHP_SESSION_ACTIVE ? TRUE : FALSE;
} else {
return session_id() === '' ? FALSE : TRUE;
}
} else {
Warning("php_sapi_name === 'cli'");
}
return FALSE;
}
?>

View File

@ -2064,7 +2064,7 @@ function cache_bust( $file ) {
$dirname = preg_replace( '/\//', '_', $parts['dirname'] );
$cacheFile = $dirname.'_'.$parts['filename'].'-'.$css.'-'.filemtime($file).'.'.$parts['extension'];
if ( file_exists(ZM_DIR_CACHE.'/'.$cacheFile) or symlink(ZM_PATH_WEB.'/'.$file, ZM_DIR_CACHE.'/'.$cacheFile) ) {
return '/zm/cache/'.$cacheFile;
return 'cache/'.$cacheFile;
} else {
Warning("Failed linking $file to $cacheFile");
}

View File

@ -662,6 +662,7 @@ $SLANG = array(
'ShowFilterWindow' => 'Show Filter Window',
'ShowTimeline' => 'Show Timeline',
'SignalCheckColour' => 'Signal Check Colour',
'SignalCheckPoints' => 'Signal Check Points',
'Size' => 'Size',
'SkinDescription' => 'Change the skin for this session',
'CSSDescription' => 'Change the css for this session',
@ -696,6 +697,7 @@ $SLANG = array(
'Stills' => 'Stills',
'Stopped' => 'Stopped',
'Stop' => 'Stop',
'StorageArea' => 'Storage Area',
'StorageScheme' => 'Scheme',
'StreamReplayBuffer' => 'Stream Replay Image Buffer',
'Stream' => 'Stream',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,11 +0,0 @@
var config = {
'.chosen-select' : {},
'.chosen-select-deselect' : { allow_single_deselect: true },
'.chosen-select-no-single' : { disable_search_threshold: 10 },
'.chosen-select-no-results': { no_results_text: 'Oops, nothing found!' },
'.chosen-select-rtl' : { rtl: true },
'.chosen-select-width' : { width: '95%' }
}
for (var selector in config) {
$(selector).chosen(config[selector]);
}

View File

@ -1,16 +0,0 @@
document.observe('dom:loaded', function(evt) {
var config = {
'.chosen-select' : {},
'.chosen-select-deselect' : { allow_single_deselect: true },
'.chosen-select-no-single' : { disable_search_threshold: 10 },
'.chosen-select-no-results': { no_results_text: 'Oops, nothing found!' },
'.chosen-select-rtl' : { rtl: true },
'.chosen-select-width' : { width: '95%' }
}
for (var selector in config) {
$$(selector).each(function(element) {
new Chosen(element, config[selector]);
});
}
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -1,108 +0,0 @@
/**
* okaidia theme for JavaScript, CSS and HTML
* Loosely based on Monokai textmate theme by http://www.monokai.nl/
* @author ocodia
*/
code[class*="language-"],
pre[class*="language-"] {
color: #f8f8f2;
text-shadow: 0 1px rgba(0,0,0,0.3);
font-family: Consolas, Monaco, 'Andale Mono', monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
border-radius: 0.3em;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #272822;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #f8f8f2;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag {
color: #f92672;
}
.token.boolean,
.token.number{
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #f8f8f2;
}
.token.atrule,
.token.attr-value
{
color: #e6db74;
}
.token.keyword{
color: #66d9ef;
}
.token.regex,
.token.important {
color: #fd971f;
}
.token.important {
font-weight: bold;
}
.token.entity {
cursor: help;
}

View File

@ -1,9 +0,0 @@
/**
* Prism: Lightweight, robust, elegant syntax highlighting
* MIT license http://www.opensource.org/licenses/mit-license.php/
* @author Lea Verou http://lea.verou.me
*/(function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var r={};for(var i in e)e.hasOwnProperty(i)&&(r[i]=t.util.clone(e[i]));return r;case"Array":return e.slice()}return e}},languages:{extend:function(e,n){var r=t.util.clone(t.languages[e]);for(var i in n)r[i]=n[i];return r},insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o},DFS:function(e,n){for(var r in e){n.call(e,r,e[r]);t.util.type(e)==="Object"&&t.languages.DFS(e[r],n)}}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"").replace(/\s+/g," ")+" language-"+o;a=r.parentNode;/pre/i.test(a.nodeName)&&(a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+o);var f=r.textContent;if(!f)return;f=f.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ");var l={element:r,language:o,grammar:u,code:f};t.hooks.run("before-highlight",l);if(i&&self.Worker){var c=new Worker(t.filename);c.onmessage=function(e){l.highlightedCode=n.stringify(JSON.parse(e.data),o);t.hooks.run("before-insert",l);l.element.innerHTML=l.highlightedCode;s&&s.call(l.element);t.hooks.run("after-highlight",l)};c.postMessage(JSON.stringify({language:l.language,code:l.code}))}else{l.highlightedCode=t.highlight(l.code,l.grammar,l.language);t.hooks.run("before-insert",l);l.element.innerHTML=l.highlightedCode;s&&s.call(r);t.hooks.run("after-highlight",l)}},highlight:function(e,r,i){return n.stringify(t.tokenize(e,r),i)},tokenize:function(e,n,r){var i=t.Token,s=[e],o=n.rest;if(o){for(var u in o)n[u]=o[u];delete n.rest}e:for(var u in n){if(!n.hasOwnProperty(u)||!n[u])continue;var a=n[u],f=a.inside,l=!!a.lookbehind,c=0;a=a.pattern||a;for(var h=0;h<s.length;h++){var p=s[h];if(s.length>e.length)break e;if(p instanceof i)continue;a.lastIndex=0;var d=a.exec(p);if(d){l&&(c=d[1].length);var v=d.index-1+c,d=d[0].slice(c),m=d.length,g=v+m,y=p.slice(0,v+1),b=p.slice(g+1),w=[h,1];y&&w.push(y);var E=new i(u,f?t.tokenize(d,f):d);w.push(E);b&&w.push(b);Array.prototype.splice.apply(s,w)}}}return s},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t){this.type=e;this.content=t};n.stringify=function(e,r,i){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]")return e.map(function(t){return n.stringify(t,r,e)}).join("");var s={type:e.type,content:n.stringify(e.content,r,i),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:i};s.type=="comment"&&(s.attributes.spellcheck="true");t.hooks.run("wrap",s);var o="";for(var u in s.attributes)o+=u+'="'+(s.attributes[u]||"")+'"';return"<"+s.tag+' class="'+s.classes.join(" ")+'" '+o+">"+s.content+"</"+s.tag+">"};if(!self.document){self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,i=n.code;self.postMessage(JSON.stringify(t.tokenize(i,t.languages[r])));self.close()},!1);return}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}})();;
Prism.languages.markup={comment:/&lt;!--[\w\W]*?-->/g,prolog:/&lt;\?.+?\?>/,doctype:/&lt;!DOCTYPE.+?>/,cdata:/&lt;!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/&lt;\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|\w+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^&lt;\/?[\w:-]+/i,inside:{punctuation:/^&lt;\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/&amp;#?[\da-z]{1,8};/gi};Prism.hooks.add("wrap",function(e){e.type==="entity"&&(e.attributes.title=e.content.replace(/&amp;/,"&"))});;
Prism.languages.css={comment:/\/\*[\w\W]*?\*\//g,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*{))/gi,inside:{punctuation:/[;:]/g}},url:/url\((["']?).*?\1\)/gi,selector:/[^\{\}\s][^\{\};]*(?=\s*\{)/g,property:/(\b|\B)[\w-]+(?=\s*:)/ig,string:/("|')(\\?.)*?\1/g,important:/\B!important\b/gi,ignore:/&(lt|gt|amp);/gi,punctuation:/[\{\};:]/g};Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{style:{pattern:/(&lt;|<)style[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/style(>|&gt;)/ig,inside:{tag:{pattern:/(&lt;|<)style[\w\W]*?(>|&gt;)|(&lt;|<)\/style(>|&gt;)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css}}});;
Prism.languages.clike={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/ig,inside:{punctuation:/\(/}}, number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|&lt;=?|>=?|={1,3}|(&amp;){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};;
Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(var|let|if|else|while|do|for|return|in|instanceof|function|new|with|typeof|try|catch|finally|null|break|continue)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g});Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}});Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(&lt;|<)script[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/script(>|&gt;)/ig,inside:{tag:{pattern:/(&lt;|<)script[\w\W]*?(>|&gt;)|(&lt;|<)\/script(>|&gt;)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});;

View File

@ -1,3 +0,0 @@
Outdated, source-less minified file. Original may have been lost.
https://github.com/harvesthq/chosen/issues/3005

View File

@ -1,219 +0,0 @@
/* Reset */
html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; }
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; }
blockquote, q { quotes: none; }
blockquote:before, blockquote:after, q:before, q:after { content: ""; content: none; }
ins { background-color: #ff9; color: #000; text-decoration: none; }
mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
del { text-decoration: line-through; }
abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
table { border-collapse: collapse; border-spacing: 0; }
hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
input, select { vertical-align: middle; }
body { font:13px/1.231 sans-serif; *font-size:small; } /* Hack retained to preserve specificity */
select, input, textarea, button { font:99% sans-serif; }
pre, code, kbd, samp { font-family: monospace, sans-serif; }
body { background: #EEE; color: #444; line-height: 1.4em; }
header h1 { color: black; font-size: 2em; line-height: 1.1em; display: inline-block; height: 27px; margin: 20px 0 25px; }
header h1 small { font-size: 0.6em; }
div#content { background: white; border: 1px solid #ccc; border-width: 0 1px 1px; margin: 0 auto; padding: 40px 50px 40px; width: 738px; }
footer { color: #999; padding-top: 40px; font-size: 0.8em; text-align: center; }
body { font-family: sans-serif; font-size: 1em; }
p { margin: 0 0 .7em; max-width: 700px; }
table+p { margin-top: 1em; }
h2 { border-bottom: 1px solid #ccc; font-size: 1.2em; margin: 3em 0 1em 0; font-weight: bold;}
h3 { font-weight: bold; }
h2.intro { border-bottom: none; font-size: 1em; font-weight: normal; margin-top:0; }
ul li { list-style: disc; margin-left: 1em; margin-bottom: 1.25em; }
ol li { margin-left: 1.25em; }
ol ul, ul ul { margin: .25em 0 0; }
ol ul li, ul ul li { list-style-type: circle; margin: 0 0 .25em 1em; }
li > p { margin-top: .25em; }
div.side-by-side { width: 100%; margin-bottom: 1em; }
div.side-by-side > div { float: left; width: 49%; }
div.side-by-side > div > em { margin-bottom: 10px; display: block; }
.faqs em { display: block; }
.clearfix:after {
content: "\0020";
display: block;
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
a { color: #F36C00; outline: none; text-decoration: none; }
a:hover { text-decoration: underline; }
ul.credits li { margin-bottom: .25em; }
strong { font-weight: bold; }
i { font-style: italic; }
.button {
background: #fafafa;
background: -webkit-linear-gradient(top, #ffffff, #eeeeee);
background: -moz-linear-gradient(top, #ffffff, #eeeeee);
background: -o-linear-gradient(top, #ffffff, #eeeeee);
background: linear-gradient(to bottom, #ffffff, #eeeeee);
border: 1px solid #bbbbbb;
border-radius: 4px;
box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.2);
color: #555555;
cursor: pointer;
display: inline-block;
font-family: "Helvetica Neue", Arial, Verdana, "Nimbus Sans L", sans-serif;
font-size: 13px;
font-weight: 500;
height: 31px;
line-height: 28px;
outline: none;
padding: 0 13px;
text-shadow: 0 1px 0 white;
text-decoration: none;
vertical-align: middle;
white-space: nowrap;
-webkit-font-smoothing: antialiased;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.button-blue {
background: #1385e5;
background: -webkit-linear-gradient(top, #53b2fc, #1385e5);
background: -moz-linear-gradient(top, #53b2fc, #1385e5);
background: -o-linear-gradient(top, #53b2fc, #1385e5);
background: linear-gradient(to bottom, #53b2fc, #1385e5);
border-color: #075fa9;
color: white;
font-weight: bold;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);
}
/* Tweak navbar brand link to be super sleek
-------------------------------------------------- */
.oss-bar {
top: 0;
right: 20px;
position: fixed;
z-index: 1030;
}
.oss-bar ul {
float: right;
margin: 0;
list-style: none;
}
.oss-bar ul li {
list-style: none;
float: left;
line-height: 0;
margin: 0;
}
.oss-bar ul li a {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
border: 0;
margin-top: -10px;
display: block;
height: 58px;
background: #F36C00 url(oss-credit.png) no-repeat 20px 22px;
padding: 22px 20px 12px 20px;
text-indent: 120%; /* stupid padding */
white-space: nowrap;
overflow: hidden;
-webkit-transition: all 0.10s ease-in-out;
-moz-transition: all 0.10s ease-in-out;
transition: all 0.15s ease-in-out;
}
.oss-bar ul li a:hover {
margin-top: 0px;
}
.oss-bar a.harvest {
width: 196px;
background-color: #F36C00;
background-position: -142px 22px;
padding-right: 22px; /* optical illusion */
}
.oss-bar a.fork {
width: 162px;
background-color: #333333;
}
.docs-table th, .docs-table td {
border: 1px solid #000;
padding: 4px 6px;
white-space: nowrap;
}
.docs-table td:last-child {
white-space: normal;
}
.docs-table th {
font-weight: bold;
text-align: left;
}
#content pre[class*=language-] {
font-size: 14px;
margin-bottom: 20px;
}
#content pre[class*=language-] code {
font-size: 14px;
padding: 0;
}
#content code[class*=language-] {
font-size: 12px;
padding: 2px 4px;
}
.anchor {
color: inherit;
position: relative;
}
.anchor:hover {
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSI3Ij48ZyBmaWxsPSIjNDE0MDQyIj48cGF0aCBkPSJNOS44IDdoLS45bC0uOS0uMWMtLjctLjMtMS40LS43LTEuOC0xLjMtLjItLjEtLjMtLjMtLjMtLjVsLS4zLS40Yy0uMS0uNC0uMi0uOC0uMi0xLjIgMC0uNC4xLS44LjItMS4yaDEuN2MtLjMuNC0uNC44LS40IDEuMiAwIC40LjEuOC4zIDEuMS4xLjIuMi4zLjQuNC4xLjEuMi4yLjQuMy4zLjIuNy4zIDEgLjNoMy40YzEuMiAwIDIuMi0uOSAyLjItMi4xcy0xLTIuMS0yLjItMi4xaC0xLjRjLS4zLS42LS43LTEtMS4yLTEuNGgyLjZjMiAwIDMuNiAxLjYgMy42IDMuNXMtMS42IDMuNS0zLjYgMy41aC0yLjZ6TTguNCAyYy0uMS0uMS0uMi0uMy0uNC0uMy0uMy0uMi0uNy0uMy0xLS4zaC0zLjRjLTEuMiAwLTIuMi45LTIuMiAyLjEgMCAxLjIgMSAyLjEgMi4yIDIuMWgxLjRjLjMuNS43IDEgMS4yIDEuNGgtMi42Yy0yIDAtMy42LTEuNi0zLjYtMy41czEuNi0zLjUgMy42LTMuNWgzLjUwMDAwMDAwMDAwMDAwMDRsLjkuMWMuNy4yIDEuNC43IDEuOCAxLjMuMS4xLjIuMy4zLjUuMS4xLjIuMy4yLjUuMS40LjIuOC4yIDEuMiAwIC40LS4xLjgtLjIgMS4yaC0xLjZjLjMtLjUuNC0uOS40LTEuM3MtLjEtLjgtLjMtMS4xYy0uMS0uMi0uMi0uMy0uNC0uNHoiLz48L2c+PC9zdmc+) 0 50% no-repeat;
background-size: 21px 9px;
margin-left: -27px;
padding-left: 27px;
text-decoration: none;
}
.select,
.chosen-select,
.chosen-select-no-single,
.chosen-select-no-results,
.chosen-select-deselect,
.chosen-select-rtl,
.chosen-select-width {
width: 350px;
}
.jquery-version-refer {
margin-top: 40px;
font-style: italic;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,306 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Chosen: A jQuery Plugin by Harvest to Tame Unwieldy Select Boxes</title>
<link rel="stylesheet" href="docsupport/style.css">
<link rel="stylesheet" href="docsupport/prism.css">
<link rel="stylesheet" href="chosen.css">
<style type="text/css" media="all">
/* fix rtl for demo */
.chosen-rtl .chosen-drop { left: -9000px; }
</style>
</head>
<body>
<div id="container">
<div id="content">
<header>
<h1>Chosen <small>(<span id="latest-version">v1.8.2</span>)</small></h1>
</header>
<p>Chosen has a number of options and attributes that allow you to have full control of your select boxes.</p>
<h2><a name="options" class="anchor" href="#options">Options</a></h2>
<p>The following options are available to pass into Chosen on instantiation.</p>
<h3>Example:</h3>
<pre>
<code class="language-javascript">$(".my_select_box").chosen({
disable_search_threshold: 10,
no_results_text: "Oops, nothing found!",
width: "95%"
});</code>
</pre>
<table class="docs-table">
<tr>
<th>Option</th><th>Default</th><th>Description</th>
</tr>
<tr>
<td>allow_single_deselect</td>
<td>false</td>
<td>When set to <code class="language-javascript">true</code> on a single select, Chosen adds a UI element which selects the first element (if it is blank).</td>
</tr>
<tr>
<td>disable_search</td>
<td>false</td>
<td>When set to <code class="language-javascript">true</code>, Chosen will not display the search field (single selects only).</td>
</tr>
<tr>
<td>disable_search_threshold</td>
<td>0</td>
<td>Hide the search input on single selects if there are <i>n</i> or fewer options.</td>
</tr>
<tr>
<td>enable_split_word_search</td>
<td>true</td>
<td>By default, searching will match on any word within an option tag. Set this option to <code class="language-javascript">false</code> if you want to only match on the entire text of an option tag.</td>
</tr>
<tr>
<td>inherit_select_classes</td>
<td>false</td>
<td>When set to <code class="language-javascript">true</code>, Chosen will grab any classes on the original select field and add them to Chosens container div.</td>
</tr>
<tr>
<td>max_selected_options</td>
<td>Infinity</td>
<td>Limits how many options the user can select. When the limit is reached, the <code class="language-javascript">chosen:maxselected</code> event is triggered.</td>
</tr>
<tr>
<td>no_results_text</td>
<td>"No results match"</td>
<td>The text to be displayed when no matching results are found. The current search is shown at the end of the text (<i>e.g.</i>,
No results match "Bad Search").</td>
</tr>
<tr>
<td>placeholder_text_multiple</td>
<td>"Select Some Options"</td>
<td>The text to be displayed as a placeholder when no options are selected for a multiple select.</td>
</tr>
<tr>
<td>placeholder_text_single</td>
<td>"Select an Option"</td>
<td>The text to be displayed as a placeholder when no options are selected for a single select.</td>
</tr>
<tr>
<td>search_contains</td>
<td>false</td>
<td>By default, Chosens search matches starting at the beginning of a word. Setting this option to <code class="language-javascript">true</code> allows matches starting from anywhere within a word. This is especially useful for options that include a lot of special characters or phrases in ()s and []s.</td>
</tr>
<tr>
<td>single_backstroke_delete</td>
<td>true</td>
<td>By default, pressing delete/backspace on multiple selects will remove a selected choice. When <code class="language-javascript">false</code>, pressing delete/backspace will highlight the last choice, and a second press deselects it.</td>
</tr>
<tr>
<td>width</td>
<td>Original select width.</td>
<td>The width of the Chosen select box. By default, Chosen attempts to match the width of the select box you are replacing. If your select is hidden when Chosen is instantiated, you must specify a width or the select will show up with a width of 0.</td>
</tr>
<tr>
<td>display_disabled_options</td>
<td>true</td>
<td>By default, Chosen includes disabled options in search results with a special styling. Setting this option to false will hide disabled results and exclude them from searches.</td>
</tr>
<tr>
<td>display_selected_options</td>
<td>true</td>
<td>
<p>By default, Chosen includes selected options in search results with a special styling. Setting this option to false will hide selected results and exclude them from searches.</p>
<p><strong>Note:</strong> this is for multiple selects only. In single selects, the selected result will always be displayed.</p>
</td>
</tr>
<tr>
<td>include_group_label_in_selected</td>
<td>false</td>
<td>
<p>By default, Chosen only shows the text of a selected option. Setting this option to <code class="language-javascript">true</code> will show the text and group (if any) of the selected option.</p>
</td>
</tr>
<tr>
<td>max_shown_results</td>
<td>Infinity</td>
<td>
<p>Only show the first (n) matching options in the results. This can be used to increase performance for selects with very many options.</p>
</td>
</tr>
<tr>
<td>case_sensitive_search</td>
<td>false</td>
<td>
<p>By default Chosen's search is case-insensitive. Setting this option to <code class="language-javascript">true</code> makes the search case-sensitive.</p>
</td>
</tr>
<tr>
<td>hide_results_on_select</td>
<td>true</td>
<td>
<p>By default Chosen's results are hidden after a option is selected. Setting this option to <code class="language-javascript">false</code> will keep the results open after selection. This only applies to multiple selects.</p>
</td>
</tr>
<tr>
<td>rtl</td>
<td>false</td>
<td>
<p>Chosen supports right-to-left text in select boxes. Set this option to <code class="language-javascript">true</code> to support right-to-left text options.</p>
<p><strong>Note:</strong> <a href="#classes">the <code class="language-javascript">chosen-rtl</code> class</a> on the select has precedence over this option. However, the classname approach is deprecated and will be removed in future versions of Chosen.</p>
</td>
</tr>
</table>
<h2><a name="attributes" class="anchor" href="#attributes">Attributes</a></h2>
<p>Certain attributes placed on the select tag or its options can be used to configure Chosen.</p>
<h3>Example:</h3>
<pre>
<code class="language-markup">&lt;select class="my_select_box" data-placeholder="Select Your Options"&gt;
&lt;option value="1"&gt;Option 1&lt;/option&gt;
&lt;option value="2" selected&gt;Option 2&lt;/option&gt;
&lt;option value="3" disabled&gt;Option 3&lt;/option&gt;
&lt;/select&gt;</code>
</pre>
<table class="docs-table">
<tr>
<th>Attribute</th><th>Description</th>
</tr>
<tr>
<td>data-placeholder</td>
<td>
<p>The text to be displayed as a placeholder when no options are selected for a select. Defaults to "Select an Option" for single selects or "Select Some Options" for multiple selects.</p>
<p><strong>Note:</strong>This attribute overrides anything set in the <code class="language-javascript">placeholder_text_multiple</code> or <code class="language-javascript">placeholder_text_single</code> options.</p>
</td>
</tr>
<tr>
<td>multiple</td>
<td>The attribute <code class="language-html">multiple</code> on your select box dictates whether Chosen will render a multiple or single select.</td>
</tr>
<tr>
<td>selected, disabled</td>
<td>Chosen automatically highlights selected options and disables disabled options.</td>
</tr>
</table>
<h2><a name="classes" class="anchor" href="#classes">Classes</a></h2>
<p>Classes placed on the select tag can be used to configure Chosen.</p>
<h3>Example:</h3>
<pre>
<code class="language-markup">&lt;select class="my_select_box chosen-rtl"&gt;
&lt;option value="1"&gt;Option 1&lt;/option&gt;
&lt;option value="2"&gt;Option 2&lt;/option&gt;
&lt;option value="3"&gt;Option 3&lt;/option&gt;
&lt;/select&gt;</code>
</pre>
<table class="docs-table">
<tr>
<th>Classname</th>
<th>Description</th>
</tr>
<tr>
<td>chosen-rtl</td>
<td>
<p>Chosen supports right-to-left text in select boxes. Add the class <code class="language-html">chosen-rtl</code> to your select tag to support right-to-left text options.</p>
<p><strong>Note:</strong> The <code class="language-html">chosen-rtl</code> class will pass through to the Chosen select even when the <code class="language-javascript">inherit_select_classes</code> option is set to <code class="language-javascript">false</code>.</p>
<p><strong>Note:</strong> This is deprecated in favor of using the <code class="language-javascript">rtl: true</code> option (see the <a href="#options">Options section</a>).</p>
</td>
</tr>
</table>
<h2><a name="triggered-events" class="anchor" href="#triggered-events">Triggered Events</a></h2>
<p>Chosen triggers a number of standard and custom events on the original select field.</p>
<h3>Example:</h3>
<pre>
<code class="language-javascript">$('.my_select_box').on('change', function(evt, params) {
do_something(evt, params);
});</code>
</pre>
<table class="docs-table">
<tr>
<th>Event</th><th>Description</th>
</tr>
<tr>
<td>change</td>
<td>
<p>Chosen triggers the standard DOM event whenever a selection is made (it also sends a <code class="language-javascript">selected</code> or <code class="language-javascript">deselected</code> parameter that tells you which option was changed).</p>
<p><strong>Note:</strong> The selected and deselected parameters are not available for Prototype.</p>
</td>
</tr>
<tr>
<td>chosen:ready</td>
<td>Triggered after Chosen has been fully instantiated.</td>
</tr>
<tr>
<td>chosen:maxselected</td>
<td>Triggered if <code class="language-javascript">max_selected_options</code> is set and that total is broken.</td>
</tr>
<tr>
<td>chosen:showing_dropdown</td>
<td>Triggered when Chosens dropdown is opened.</td>
</tr>
<tr>
<td>chosen:hiding_dropdown</td>
<td>Triggered when Chosens dropdown is closed.</td>
</tr>
<tr>
<td>chosen:no_results</td>
<td>Triggered when a search returns no matching results.</td>
</tr>
</table>
<p>
<strong>Note:</strong> all custom Chosen events (those that begin with <code class="language-javascript">chosen:</code>) also include the <code class="language-javascript">chosen</code> object as a parameter.
</p>
<h2><a name="triggerable-events" class="anchor" href="#triggerable-events">Triggerable Events</a></h2>
<p>You can trigger several events on the original select field to invoke a behavior in Chosen.</p>
<h3>Example:</h3>
<pre>
<code class="language-javascript">// tell Chosen that a select has changed
$('.my_select_box').trigger('chosen:updated');</code>
</pre>
<table class="docs-table">
<tr>
<th>Event</th><th>Description</th>
</tr>
<tr>
<td>chosen:updated</td>
<td>This event should be triggered whenever Chosens underlying select element changes (such as a change in selected options).</td>
</tr>
<tr>
<td>chosen:activate</td>
<td>This is the equivalant of focusing a standard HTML select field. When activated, Chosen will capure keypress events as if you had clicked the field directly.</td>
</tr>
<tr>
<td>chosen:open</td>
<td>This event activates Chosen and also displays the search results.</td>
</tr>
<tr>
<td>chosen:close</td>
<td>This event deactivates Chosen and hides the search results.</td>
</tr>
</table>
<footer>
&copy; 2011&ndash;2016 <a href="http://www.getharvest.com/">Harvest</a>. Chosen is licensed under the <a href="https://github.com/harvesthq/chosen/blob/master/LICENSE.md">MIT license</a>.
</footer>
</div>
</div>
<div class="oss-bar">
<ul>
<li><a class="fork" href="https://github.com/harvesthq/chosen">Fork on Github</a></li>
<li><a class="harvest" href="http://www.getharvest.com/">Built by Harvest</a></li>
</ul>
</div>
<script src="docsupport/prism.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -318,7 +318,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
<?php
}
?>
<td class="colZones"><?php echo makePopupLink( '?view=zones&amp;mid='.$monitor['Id'], 'zmZones', array( 'zones', $monitor['Width'], $monitor['Height'] ), $monitor['ZoneCount'], $running && canView('Monitors') ) ?></td>
<td class="colZones"><?php echo makePopupLink('?view=zones&amp;mid='.$monitor['Id'], 'zmZones', array('zones', $monitor['Width'], $monitor['Height']), $monitor['ZoneCount'], canView('Monitors')) ?></td>
<?php
if ( canEdit('Monitors') ) {
?>

View File

@ -118,11 +118,11 @@ echo " };\n";
echo "var maxScore=$maxScore;\n"; // used to skip frame load if we find no alarms.
} // end if initialmodeislive
echo "var Storage = []\n";
echo "var Storage = [];\n";
foreach ( Storage::find_all() as $Storage ) {
echo 'Storage[' . $Storage->Id() . '] = ' . json_encode($Storage). ";\n";
}
echo "var Servers = []\n";
echo "\nvar Servers = [];\n";
foreach ( Server::find_all() as $Server ) {
echo 'Servers[' . $Server->Id() . '] = ' . json_encode($Server). ";\n";
}

View File

@ -173,7 +173,7 @@ if ( !empty($_REQUEST['preset']) ) {
}
}
if ( !empty($_REQUEST['probe']) ) {
$probe = unserialize(base64_decode($_REQUEST['probe']));
$probe = unserialize($_REQUEST['probe']);
foreach ( $probe as $name=>$value ) {
if ( isset($value) ) {
# Does isset handle NULL's? I don't think this code is correct.

View File

@ -18,337 +18,299 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canEdit( 'Monitors' ) )
{
$view = "error";
return;
if ( !canEdit('Monitors') ) {
$view = 'error';
return;
}
// Probe Local Cameras
function probeV4L() {
$cameras = array();
$command = getZmuCommand(' --query --device');
if ( !empty($_REQUEST['device']) )
$command .= '='.escapeshellarg($_REQUEST['device']);
$result = exec(escapeshellcmd($command), $output, $status);
if ( $status ) {
Error("Unable to probe local cameras, status is '$status'");
return $cameras;
}
$monitors = array();
foreach ( dbFetchAll("SELECT Id, Name, Device,Channel FROM Monitors WHERE Type = 'Local' ORDER BY Device, Channel" ) as $monitor )
$monitors[$monitor['Device'].':'.$monitor['Channel']] = $monitor;
$devices = array();
$preferredStandards = array('PAL', 'NTSC');
$preferredFormats = array('BGR3', 'RGB3', 'YUYV', 'UYVY', 'JPEG', 'MJPG', '422P', 'YU12', 'GREY');
foreach ( $output as $line ) {
if ( !preg_match('/^d:([^|]+).*S:([^|]*).*F:([^|]+).*I:(\d+)\|(.+)$/', $line, $deviceMatches) )
Fatal("Can't parse command output '$line'");
$standards = explode('/',$deviceMatches[2]);
$preferredStandard = false;
foreach ( $preferredStandards as $standard ) {
if ( in_array( $standard, $standards ) ) {
$preferredStandard = $standard;
break;
}
}
$formats = explode('/',$deviceMatches[3]);
$preferredFormat = false;
foreach ( $preferredFormats as $format ) {
if ( in_array($format, $formats) ) {
$preferredFormat = $format;
break;
}
}
$device = array(
'device' => $deviceMatches[1],
'standards' => $standard,
'preferredStandard' => $preferredStandard,
'formats' => $formats,
'preferredFormat' => $preferredFormat,
);
$inputs = array();
for ( $i = 0; $i < $deviceMatches[4]; $i++ ) {
if ( !preg_match('/i'.$i.':([^|]+)\|i'.$i.'T:([^|]+)\|/', $deviceMatches[5], $inputMatches) )
Fatal("Can't parse input '".$deviceMatches[5]."'");
if ( $inputMatches[2] == 'Camera' ) {
$input = array(
'index' => $i,
'id' => $deviceMatches[1].':'.$i,
'name' => $inputMatches[1],
'free' => empty($monitors[$deviceMatches[1].':'.$i]),
);
$inputMonitor = array(
'Type' => 'Local',
'Device' => $deviceMatches[1],
'Channel' => $i,
'Colours' => 3,
'Format' => $preferredStandard,
'Palette' => $preferredFormat,
);
if ( $preferredStandard == 'NTSC' ) {
$inputMonitor['Width'] = 320;
$inputMonitor['Height'] = 240;
} else {
$inputMonitor['Width'] = 384;
$inputMonitor['Height'] = 288;
}
if ( $preferredFormat == 'GREY' ) {
$inputMonitor['Colours'] = 1;
$inputMonitor['SignalCheckColour'] = '#000023';
}
$inputDesc = base64_encode(serialize($inputMonitor));
$inputString = $deviceMatches[1].', chan '.$i.($input['free']?(' - '.translate('Available')):(' ('.$monitors[$input['id']]['Name'].')'));
$inputs[] = $input;
$cameras[$inputDesc] = $inputString;
}
}
$device['inputs'] = $inputs;
$devices[] = $device;
} # end foreach output line
return $cameras;
} # end function probeV4L
// Probe Network Cameras
//
function probeAxis($ip) {
$url = 'http://'.$ip.'/axis-cgi/admin/param.cgi?action=list&group=Brand';
$camera = array(
'model' => 'Unknown Axis Camera',
'monitor' => array(
'Type' => 'Remote',
'Protocol' => 'http',
'Host' => $ip,
'Port' => 80,
'Path' => '/axis-cgi/mjpg/video.cgi?resolution=320x240',
'Colours' => 3,
'Width' => 320,
'Height' => 240,
),
);
if ( $lines = @file($url) ) {
foreach ( $lines as $line ) {
$line = rtrim( $line );
if ( preg_match('/^(.+)=(.+)$/', $line, $matches) ) {
if ( $matches[1] == 'root.Brand.ProdShortName' ) {
$camera['model'] = $matches[2];
break;
}
}
}
}
return $camera;
}
function probePana($ip) {
$url = 'http://'.$ip.'/Get?Func=Model&Kind=1';
$camera = array(
'model' => 'Unknown Panasonic Camera',
'monitor' => array(
'Type' => 'Remote',
'Protocol' => 'http',
'Host' => $ip,
'Port' => 80,
'Path' => '/nphMotionJpeg?Resolution=320x240&Quality=Standard',
'Colours' => 3,
'Width' => 320,
'Height' => 240,
),
);
return $camera;
}
function probeActi($ip) {
$url = 'http://'.$ip.'/cgi-bin/system?USER=Admin&PWD=123456&SYSTEM_INFO';
$camera = array(
'model' => 'Unknown Panasonic Camera',
'monitor' => array(
'Type' => 'Remote',
'Protocol' => 'rtsp',
'Method' => 'rtpUni',
'Host' => 'Admin:123456@'.$ip,
'Port' => 7070,
'Path' => '',
'Colours' => 3,
'Width' => 320,
'Height' => 240,
),
);
if ( $lines = @file($url) ) {
foreach ( $lines as $line ) {
$line = rtrim( $line );
if ( preg_match('/^(.+?)\s*=\s*(.+)$/', $line, $matches) ) {
if ( $matches[1] == 'Production ID' ) {
$camera['model'] = 'ACTi '.substr($matches[2], 0, strpos($matches[2], '-'));
break;
}
}
}
}
return $camera;
}
function probeVivotek($ip) {
$url = 'http://'.$ip.'/cgi-bin/viewer/getparam.cgi';
$camera = array(
'model' => 'Unknown Vivotek Camera',
'monitor' => array(
'Type' => 'Remote',
'Protocol' => 'rtsp',
'Method' => 'rtpUni',
'Host' => $ip,
'Port' => 554,
'Path' => '',
'Colours' => 3,
'Width' => 352,
'Height' => 240,
),
);
if ( $lines = @file($url) ) {
foreach ( $lines as $line ) {
$line = rtrim($line);
if ( preg_match('/^(.+?)\s*=\'(.+)\'$/', $line, $matches) ) {
if ( $matches[1] == 'system_info_modelname' ) {
$camera['model'] = 'Vivotek '.$matches[2];
} elseif ( $matches[1] == 'network_rtsp_port' ) {
$camera['monitor']['Port'] = $matches[2];
} elseif ( $matches[1] == 'network_rtsp_s0_accessname' ) {
$camera['monitor']['Path'] = $matches[2];
}
}
}
}
return $camera;
}
function probeWansview($ip) {
$camera = array(
'model' => 'Wansview Camera',
'monitor' => array(
'Type' => 'Remote',
'Protocol' => 'http',
'Host' => 'admin:123456@'.$ip,
'Port' => 80,
'Path' => 'videostream.cgi',
'Width' => 640,
'Height' => 480,
'Palette' => 3
),
);
return $camera;
}
function probeNetwork() {
$cameras = array();
$arp_command = ZM_PATH_ARP;
$result = explode(' ', $arp_command);
if ( !is_executable($result[0]) ) {
Error("ARP compatible binary not found or not executable by the web user account. Verify ZM_PATH_ARP points to a valid arp tool.");
return;
}
$result = exec(escapeshellcmd($arp_command), $output, $status);
if ( $status ) {
Error("Unable to probe network cameras, status is '$status'");
return;
}
$monitors = array();
foreach ( dbFetchAll("SELECT Id, Name, Host FROM Monitors WHERE Type = 'Remote' ORDER BY Host") as $monitor ) {
if ( preg_match('/^(.+)@(.+)$/', $monitor['Host'], $matches) ) {
//echo "1: ".$matches[2]." = ".gethostbyname($matches[2])."<br/>";
$monitors[gethostbyname($matches[2])] = $monitor;
} else {
//echo "2: ".$monitor['Host']." = ".gethostbyname($monitor['Host'])."<br/>";
$monitors[gethostbyname($monitor['Host'])] = $monitor;
}
}
$macBases = array(
'00:40:8c' => array('type'=>'Axis', 'probeFunc'=>'probeAxis'),
'00:80:f0' => array('type'=>'Panasonic','probeFunc'=>'probePana'),
'00:0f:7c' => array('type'=>'ACTi','probeFunc'=>'probeACTi'),
'00:02:d1' => array('type'=>'Vivotek','probeFunc'=>'probeVivotek'),
'7c:dd:90' => array('type'=>'Wansview','probeFunc'=>'probeWansview'),
'78:a5:dd' => array('type'=>'Wansview','probeFunc'=>'probeWansview')
);
foreach ( $output as $line ) {
if ( !preg_match('/(\d+\.\d+\.\d+\.\d+).*(([0-9a-f]{2}:){5})/', $line, $matches) )
continue;
$ip = $matches[1];
$host = $ip;
$mac = $matches[2];
//echo "I:$ip, H:$host, M:$mac<br/>";
$macRoot = substr($mac,0,8);
if ( isset($macBases[$macRoot]) ) {
$macBase = $macBases[$macRoot];
$camera = call_user_func($macBase['probeFunc'], $ip);
$sourceDesc = htmlspecialchars(serialize($camera['monitor']));
$sourceString = $camera['model'].' @ '.$host;
if ( isset($monitors[$ip]) ) {
$monitor = $monitors[$ip];
$sourceString .= ' ('.$monitor['Name'].')';
} else {
$sourceString .= ' - '.translate('Available');
}
$cameras[$sourceDesc] = $sourceString;
}
} # end foreach output line
return $cameras;
} # end function probeNetwork()
$cameras = array();
$cameras[0] = translate('ChooseDetectedCamera');
if ( ZM_HAS_V4L2 )
{
// Probe Local Cameras
//
$command = getZmuCommand( " --query --device" );
if ( !empty($_REQUEST['device']) )
$command .= "=".escapeshellarg($_REQUEST['device']);
$cameras += probeV4L();
$cameras += probeNetwork();
$result = exec( escapeshellcmd($command), $output, $status );
if ( $status )
Fatal( "Unable to probe local cameras, status is '$status'" );
$monitors = array();
foreach ( dbFetchAll( "select Id, Name, Device,Channel from Monitors where Type = 'Local' order by Device, Channel" ) as $monitor )
$monitors[$monitor['Device'].':'.$monitor['Channel']] = $monitor;
$devices = array();
$preferredStandards = array( 'PAL', 'NTSC' );
$preferredFormats = array( 'BGR3', 'RGB3', 'YUYV', 'UYVY', 'JPEG', 'MJPG', '422P', 'YU12', 'GREY' );
foreach ( $output as $line )
{
if ( !preg_match( '/^d:([^|]+).*S:([^|]*).*F:([^|]+).*I:(\d+)\|(.+)$/', $line, $deviceMatches ) )
Fatal( "Can't parse command output '$line'" );
$standards = explode('/',$deviceMatches[2]);
$preferredStandard = false;
foreach ( $preferredStandards as $standard )
{
if ( in_array( $standard, $standards ) )
{
$preferredStandard = $standard;
break;
}
}
$formats = explode('/',$deviceMatches[3]);
$preferredFormat = false;
foreach ( $preferredFormats as $format )
{
if ( in_array( $format, $formats ) )
{
$preferredFormat = $format;
break;
}
}
$device = array( 'device'=>$deviceMatches[1], 'standards'=>$standard, 'preferredStandard'=>$preferredStandard, 'formats'=>$formats, 'preferredFormat'=>$preferredFormat );
$inputs = array();
for ( $i = 0; $i < $deviceMatches[4]; $i++ )
{
if ( !preg_match( '/i'.$i.':([^|]+)\|i'.$i.'T:([^|]+)\|/', $deviceMatches[5], $inputMatches ) )
Fatal( "Can't parse input '".$deviceMatches[5]."'" );
if ( $inputMatches[2] == 'Camera' )
{
$input = array( 'index'=>$i, 'id'=>$deviceMatches[1].':'.$i, 'name'=>$inputMatches[1], 'free'=>empty($monitors[$deviceMatches[1].':'.$i]) );
$inputMonitor = array(
'Type' => 'Local',
'Device' => $deviceMatches[1],
'Channel' => $i,
'Colours' => 3,
'Format' => $preferredStandard,
'Palette' => $preferredFormat,
);
if ( $preferredStandard == 'NTSC' )
{
$inputMonitor['Width'] = 320;
$inputMonitor['Height'] = 240;
}
else
{
$inputMonitor['Width'] = 384;
$inputMonitor['Height'] = 288;
}
if ( $preferredFormat == 'GREY' )
{
$inputMonitor['Colours'] = 1;
$inputMonitor['SignalCheckColour'] = '#000023';
}
$inputDesc = base64_encode(serialize($inputMonitor));
$inputString = $deviceMatches[1].', chan '.$i.($input['free']?(" - ".translate('Available')):(" (".$monitors[$input['id']]['Name'].")"));
$inputs[] = $input;
$cameras[$inputDesc] = $inputString;
}
}
$device['inputs'] = $inputs;
$devices[] = $device;
}
}
// Probe Network Cameras
//
function probeAxis( $ip )
{
$url = 'http://'.$ip.'/axis-cgi/admin/param.cgi?action=list&group=Brand';
$camera = array(
'model' => "Unknown Axis Camera",
'monitor' => array(
'Type' => 'Remote',
'Protocol' => 'http',
'Host' => $ip,
'Port' => 80,
'Path' => '/axis-cgi/mjpg/video.cgi?resolution=320x240',
'Colours' => 3,
'Width' => 320,
'Height' => 240,
),
);
if ( $lines = @file( $url ) )
{
foreach ( $lines as $line )
{
$line = rtrim( $line );
if ( preg_match( '/^(.+)=(.+)$/', $line, $matches ) )
{
if ( $matches[1] == 'root.Brand.ProdShortName' )
{
$camera['model'] = $matches[2];
break;
}
}
}
}
return( $camera );
}
function probePana( $ip )
{
$url = 'http://'.$ip.'/Get?Func=Model&Kind=1';
$camera = array(
'model' => "Unknown Panasonic Camera",
'monitor' => array(
'Type' => 'Remote',
'Protocol' => 'http',
'Host' => $ip,
'Port' => 80,
'Path' => '/nphMotionJpeg?Resolution=320x240&Quality=Standard',
'Colours' => 3,
'Width' => 320,
'Height' => 240,
),
);
return( $camera );
}
function probeActi( $ip )
{
$url = 'http://'.$ip.'/cgi-bin/system?USER=Admin&PWD=123456&SYSTEM_INFO';
$camera = array(
'model' => "Unknown Panasonic Camera",
'monitor' => array(
'Type' => 'Remote',
'Protocol' => 'rtsp',
'Method' => 'rtpUni',
'Host' => 'Admin:123456@'.$ip,
'Port' => 7070,
'Path' => '',
'Colours' => 3,
'Width' => 320,
'Height' => 240,
),
);
if ( $lines = @file( $url ) )
{
foreach ( $lines as $line )
{
$line = rtrim( $line );
if ( preg_match( '/^(.+?)\s*=\s*(.+)$/', $line, $matches ) )
{
if ( $matches[1] == 'Production ID' )
{
$camera['model'] = "ACTi ".substr( $matches[2], 0, strpos( $matches[2], '-' ));
break;
}
}
}
}
return( $camera );
}
function probeVivotek( $ip )
{
$url = 'http://'.$ip.'/cgi-bin/viewer/getparam.cgi';
$camera = array(
'model' => "Unknown Vivotek Camera",
'monitor' => array(
'Type' => 'Remote',
'Protocol' => 'rtsp',
'Method' => 'rtpUni',
'Host' => $ip,
'Port' => 554,
'Path' => '',
'Colours' => 3,
'Width' => 352,
'Height' => 240,
),
);
if ( $lines = @file( $url ) )
{
foreach ( $lines as $line )
{
$line = rtrim( $line );
if ( preg_match( '/^(.+?)\s*=\'(.+)\'$/', $line, $matches ) )
{
if ( $matches[1] == 'system_info_modelname' )
{
$camera['model'] = "Vivotek ".$matches[2];
}
elseif ( $matches[1] == 'network_rtsp_port' )
{
$camera['monitor']['Port'] = $matches[2];
}
elseif ( $matches[1] == 'network_rtsp_s0_accessname' )
{
$camera['monitor']['Path'] = $matches[2];
}
}
}
}
return( $camera );
}
function probeWansview( $ip )
{
$camera = array(
'model' => "Wansview Camera",
'monitor' => array(
'Type' => 'Remote',
'Protocol' => 'http',
'Host' => 'admin:123456@'.$ip,
'Port' => 80,
'Path' => 'videostream.cgi',
'Width' => 640,
'Height' => 480,
'Palette' => 3
),
);
return( $camera );
}
$monitors = array();
foreach ( dbFetchAll( "select Id, Name, Host from Monitors where Type = 'Remote' order by Host" ) as $monitor )
{
if ( preg_match( '/^(.+)@(.+)$/', $monitor['Host'], $matches ) )
{
//echo "1: ".$matches[2]." = ".gethostbyname($matches[2])."<br/>";
$monitors[gethostbyname($matches[2])] = $monitor;
}
else
{
//echo "2: ".$monitor['Host']." = ".gethostbyname($monitor['Host'])."<br/>";
$monitors[gethostbyname($monitor['Host'])] = $monitor;
}
}
$macBases = array(
'00:40:8c' => array( 'type'=>'Axis', 'probeFunc'=>'probeAxis' ),
'00:80:f0' => array( 'type'=>'Panasonic','probeFunc'=>'probePana' ),
'00:0f:7c' => array( 'type'=>'ACTi','probeFunc'=>'probeACTi' ),
'00:02:d1' => array( 'type'=>'Vivotek','probeFunc'=>'probeVivotek' ),
'7c:dd:90' => array( 'type'=>'Wansview','probeFunc'=>'probeWansview' ),
'78:a5:dd' => array( 'type'=>'Wansview','probeFunc'=>'probeWansview' )
);
unset($output);
// Calling arp without the full path was reported to fail on some systems
// Use the builtin unix command "type" to tell us where the command is
$arp_command = '';
$result = explode( " ", ZM_PATH_ARP );
if ( !is_executable( $result[0] ) ) {
if ( ZM_PATH_ARP ) {
Warning( "User assigned ARP tool not found. Verify ZM_PATH_ARP points to a valid arp tool and is executable by the web user account." );
}
$result = exec( 'type -p arp', $output, $status );
if ( $status ) {
Warning( "Unable to determine path for arp command, type -p arp returned '$status' output is: " . implode( "\n", $output ) );
unset($output);
$result = exec( 'which arp', $output, $status );
if ( $status ) {
Warning( "Unable to determine path for arp command, which arp returned '$status'" );
if ( file_exists( '/usr/sbin/arp' ) ) {
$arp_command = '/usr/sbin/arp -a';
}
} else {
$arp_command = $output[0]." -a";
}
} else {
$arp_command = $output[0]." -a";
}
} else {
$arp_command = ZM_PATH_ARP;
}
// Now that we know where arp is, call it using the full path
unset($output);
$result = exec( escapeshellcmd($arp_command), $output, $status );
if ( $status )
Fatal( "Unable to probe network cameras, status is '$status'" );
foreach ( $output as $line )
{
if ( !preg_match( '/(\d+\.\d+\.\d+\.\d+).*(([0-9a-f]{2}:){5})/', $line, $matches ) )
continue;
$ip = $matches[1];
$host = $ip;
$mac = $matches[2];
//echo "I:$ip, H:$host, M:$mac<br/>";
$macRoot = substr($mac,0,8);
if ( isset($macBases[$macRoot]) )
{
$macBase = $macBases[$macRoot];
$camera = call_user_func( $macBase['probeFunc'], $ip );
$sourceDesc = htmlspecialchars(serialize($camera['monitor']));
$sourceString = $camera['model'].' @ '.$host;
if ( isset($monitors[$ip]) )
{
$monitor = $monitors[$ip];
$sourceString .= " (".$monitor['Name'].")";
}
else
{
$sourceString .= " - ".translate('Available');
}
$cameras[$sourceDesc] = $sourceString;
}
}
if ( count($cameras) <= 0 )
$cameras[0] = translate('NoDetectedCameras');
if ( count($cameras) <= 1 )
$cameras[0] = translate('NoDetectedCameras');
$focusWindow = true;
@ -367,10 +329,13 @@ xhtmlHeaders(__FILE__, translate('MonitorProbe') );
<?php echo translate('MonitorProbeIntro') ?>
</p>
<p>
<label for="probe"><?php echo translate('DetectedCameras') ?></label><?php echo buildSelect( "probe", $cameras, 'configureButtons( this )' ); ?>
<label for="probe"><?php echo translate('DetectedCameras') ?></label>
<?php echo buildSelect('probe', $cameras, 'configureButtons(this)'); ?>
</p>
<div id="contentButtons">
<input type="submit" name="saveBtn" value="<?php echo translate('Save') ?>" onclick="submitCamera( this )" disabled="disabled"/><input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow()"/>
<button type="button" name="saveBtn" value="Save" onclick="submitCamera(this);" disabled="disabled">
<?php echo translate('Save') ?></button>
<button type="button" onclick="closeWindow();"><?php echo translate('Cancel') ?></button>
</div>
</form>
</div>