diff --git a/CMakeLists.txt b/CMakeLists.txt index eb58ed6db..50773db54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,8 @@ set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_C_FLAGS_DEBUG "-Wall -D__STDC_CONSTANT_MACROS -g") set(CMAKE_CXX_FLAGS_DEBUG "-Wall -D__STDC_CONSTANT_MACROS -g") +set(CMAKE_C_FLAGS_OPTIMISED "-Wall -D__STDC_CONSTANT_MACROS -O3") +set(CMAKE_CXX_FLAGS_OPTIMISED "-Wall -D__STDC_CONSTANT_MACROS -O3") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") set (CMAKE_CXX_STANDARD 11) @@ -189,6 +191,8 @@ set(ZM_NO_MMAP "OFF" CACHE BOOL experience problems with the shared memory. default: OFF") set(ZM_NO_LIBVLC "OFF" CACHE BOOL "Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF") +set(ZM_NO_LIBVNC "OFF" CACHE BOOL +"Set to ON to skip libvnc checks and force building ZM without libvnc. default: OFF") set(ZM_NO_CURL "OFF" CACHE BOOL "Set to ON to skip cURL checks and force building ZM without cURL. default: OFF") set(ZM_NO_X10 "OFF" CACHE BOOL @@ -321,7 +325,7 @@ if(NOT ZM_NO_CURL) find_package(CURL) if(CURL_FOUND) set(HAVE_LIBCURL 1) - list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES}) + #list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES}) include_directories(${CURL_INCLUDE_DIRS}) set(CMAKE_REQUIRED_INCLUDES ${CURL_INCLUDE_DIRS}) check_include_file("curl/curl.h" HAVE_CURL_CURL_H) @@ -349,19 +353,50 @@ else(JPEG_FOUND) "ZoneMinder requires jpeg but it was not found on your system") endif(JPEG_FOUND) +# LIBJWT +find_package(LibJWT) +if(LIBJWT_FOUND) + set(HAVE_LIBJWT 1) + set(optlibsfound "${optlibsfound} LIBJWT") + list(APPEND ZM_BIN_LIBS "${LIBJWT_LIBRARY}") +else(LIBJWT_FOUND) + set(optlibsnotfound "${optlibsnotfound} LIBJWT") +endif(LIBJWT_FOUND) + +# gnutls (using find_library and find_path) +if(HAVE_LIBJWT) + find_library(GNUTLS_LIBRARIES gnutls) + if(GNUTLS_LIBRARIES) + set(HAVE_LIBGNUTLS 1) + list(APPEND ZM_BIN_LIBS "${GNUTLS_LIBRARIES}") + find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h) + if(GNUTLS_INCLUDE_DIR) + include_directories("${GNUTLS_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") + endif(GNUTLS_INCLUDE_DIR) + mark_as_advanced(FORCE GNUTLS_LIBRARIES GNUTLS_INCLUDE_DIR) + check_include_file("gnutls/gnutls.h" HAVE_GNUTLS_GNUTLS_H) + set(optlibsfound "${optlibsfound} GnuTLS") + else(GNUTLS_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} GnuTLS") + endif(GNUTLS_LIBRARIES) +endif(HAVE_LIBJWT) + # OpenSSL -find_package(OpenSSL) -if(OPENSSL_FOUND) - set(HAVE_LIBOPENSSL 1) - set(HAVE_LIBCRYPTO 1) - list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}") - include_directories("${OPENSSL_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") - check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H) - set(optlibsfound "${optlibsfound} OpenSSL") -else(OPENSSL_FOUND) - set(optlibsnotfound "${optlibsnotfound} OpenSSL") -endif(OPENSSL_FOUND) +if(NOT HAVE_LIBGNUTLS OR NOT HAVE_LIBJWT) + find_package(OpenSSL) + if(OPENSSL_FOUND) + set(HAVE_LIBOPENSSL 1) + set(HAVE_LIBCRYPTO 1) + list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}") + include_directories("${OPENSSL_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") + check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H) + set(optlibsfound "${optlibsfound} OpenSSL") + else(OPENSSL_FOUND) + set(optlibsnotfound "${optlibsnotfound} OpenSSL") + endif(OPENSSL_FOUND) +endif(NOT HAVE_LIBGNUTLS OR NOT HAVE_LIBJWT) # pthread (using find_library and find_path) find_library(PTHREAD_LIBRARIES pthread) @@ -418,28 +453,6 @@ else(GCRYPT_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} GCrypt") endif(GCRYPT_LIBRARIES) -# gnutls (using find_library and find_path) -find_library(GNUTLS_LIBRARIES gnutls-openssl) -if(NOT GNUTLS_LIBRARIES) - find_library(GNUTLS_LIBRARIES gnutls) -endif(NOT GNUTLS_LIBRARIES) - -if(GNUTLS_LIBRARIES) - set(HAVE_LIBGNUTLS 1) - list(APPEND ZM_BIN_LIBS "${GNUTLS_LIBRARIES}") - find_path(GNUTLS_INCLUDE_DIR gnutls/gnutls.h) - if(GNUTLS_INCLUDE_DIR) - include_directories("${GNUTLS_INCLUDE_DIR}") - set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") - endif(GNUTLS_INCLUDE_DIR) - mark_as_advanced(FORCE GNUTLS_LIBRARIES GNUTLS_INCLUDE_DIR) - check_include_file("gnutls/openssl.h" HAVE_GNUTLS_OPENSSL_H) - check_include_file("gnutls/gnutls.h" HAVE_GNUTLS_GNUTLS_H) - set(optlibsfound "${optlibsfound} GnuTLS") -else(GNUTLS_LIBRARIES) - set(optlibsnotfound "${optlibsnotfound} GnuTLS") -endif(GNUTLS_LIBRARIES) - # mysqlclient (using find_library and find_path) find_library(MYSQLCLIENT_LIBRARIES mysqlclient PATH_SUFFIXES mysql) if(MYSQLCLIENT_LIBRARIES) @@ -655,7 +668,7 @@ if(NOT ZM_NO_LIBVLC) find_library(LIBVLC_LIBRARIES vlc) if(LIBVLC_LIBRARIES) set(HAVE_LIBVLC 1) - list(APPEND ZM_BIN_LIBS "${LIBVLC_LIBRARIES}") + #list(APPEND ZM_BIN_LIBS "${LIBVLC_LIBRARIES}") find_path(LIBVLC_INCLUDE_DIR "vlc/vlc.h") if(LIBVLC_INCLUDE_DIR) include_directories("${LIBVLC_INCLUDE_DIR}") @@ -669,6 +682,25 @@ if(NOT ZM_NO_LIBVLC) endif(LIBVLC_LIBRARIES) endif(NOT ZM_NO_LIBVLC) +if(NOT ZM_NO_LIBVNC) + # libvncclient (using find_library and find_path) + find_library(LIBVNC_LIBRARIES vncclient) + if(LIBVNC_LIBRARIES) + set(HAVE_LIBVNC 1) + #list(APPEND ZM_BIN_LIBS "${LIBVNC_LIBRARIES}") + find_path(LIBVNC_INCLUDE_DIR "rfb/rfb.h") + if(LIBVNC_INCLUDE_DIR) + include_directories("${LIBVNC_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${LIBVNC_INCLUDE_DIR}") + endif(LIBVNC_INCLUDE_DIR) + mark_as_advanced(FORCE LIBVNC_LIBRARIES LIBVNC_INCLUDE_DIR) + check_include_file("rfb/rfb.h" HAVE_RFB_RFB_H) + set(optlibsfound "${optlibsfound} libVNC") + else(LIBVNC_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} libVNC") + endif(LIBVNC_LIBRARIES) +endif(NOT ZM_NO_LIBVNC) + #find_package(Boost 1.36.0) #if(Boost_FOUND) #include_directories(${Boost_INCLUDE_DIRS}) @@ -734,14 +766,7 @@ if(HAVE_OPENSSL_MD5_H) "unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h" HAVE_MD5_OPENSSL) endif(HAVE_OPENSSL_MD5_H) -if(HAVE_GNUTLS_OPENSSL_H) - set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") - set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") - check_prototype_definition( - MD5 - "unsigned char *MD5 (const unsigned char *buf, unsigned long len, unsigned char *md)" "NULL" "gnutls/openssl.h" - HAVE_MD5_GNUTLS) -endif(HAVE_GNUTLS_OPENSSL_H) + if(HAVE_GNUTLS_GNUTLS_H) set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") @@ -750,13 +775,17 @@ if(HAVE_GNUTLS_GNUTLS_H) "int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h" HAVE_DECL_GNUTLS_FINGERPRINT) endif(HAVE_GNUTLS_GNUTLS_H) -if(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) + +if(NOT HAVE_DECL_GNUTLS_FINGERPRINT AND HAVE_MD5_OPENSSL) set(HAVE_DECL_MD5 1) -else(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) +endif(NOT HAVE_DECL_GNUTLS_FINGERPRINT AND HAVE_MD5_OPENSSL) + +if((NOT HAVE_MD5_OPENSSL) AND (NOT HAVE_DECL_GNUTLS_FINGERPRINT)) message(AUTHOR_WARNING - "ZoneMinder requires a working MD5 function for hashed authenication but - none were found - hashed authenication will not be available") -endif(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) + "ZoneMinder requires a working MD5 function for hashed authentication but + none were found - hashed authentication will not be available") +endif((NOT HAVE_MD5_OPENSSL) AND (NOT HAVE_DECL_GNUTLS_FINGERPRINT)) + # Dirty fix for zm_user only using openssl's md5 if gnutls and gcrypt are not available. # This needs to be fixed in zm_user.[h,cpp] but such fix will also require changes to configure.ac if(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL) diff --git a/README.md b/README.md index b9d215a40..e1ed37087 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,6 @@ https://github.com/ZoneMinder/zmdockerfiles ## Installation Methods -### Building from Source is Discouraged - -Historically, installing ZoneMinder onto your system required building from source code by issuing the traditional configure, make, make install commands. To get ZoneMinder to build, all of its dependencies had to be determined and installed beforehand. Init and logrotate scripts had to be manually copied into place following the build. Optional packages such as jscalendar and Cambozola had to be manually installed. Uninstalls could leave stale files around, which could cause problems during an upgrade. Speaking of upgrades, when it comes time to upgrade all these manual steps must be repeated again. - -Better methods exist today that do much of this for you. The current development team, along with other volunteers, have taken great strides in providing the resources necessary to avoid building from source. - ### Install from a Package Repository This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros: @@ -43,6 +37,13 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde If a repository that hosts ZoneMinder packages is not available for your distro, then you are encouraged to build your own package, rather than build from source. While each distro is different in ways that set it apart from all the others, they are often similar enough to allow you to adapt another distro's package building instructions to your own. +### Building from Source is Discouraged + +Historically, installing ZoneMinder onto your system required building from source code by issuing the traditional configure, make, make install commands. To get ZoneMinder to build, all of its dependencies had to be determined and installed beforehand. Init and logrotate scripts had to be manually copied into place following the build. Optional packages such as jscalendar and Cambozola had to be manually installed. Uninstalls could leave stale files around, which could cause problems during an upgrade. Speaking of upgrades, when it comes time to upgrade all these manual steps must be repeated again. + +Better methods exist today that do much of this for you. The current development team, along with other volunteers, have taken great strides in providing the resources necessary to avoid building from source. + + ### Building a ZoneMinder Package ### Building ZoneMinder into a package is not any harder than building from source. As a matter of fact, if you have successfully built ZoneMinder from source in the past, then you may find these steps to be easier. diff --git a/cmake/Modules/FindLibJWT.cmake b/cmake/Modules/FindLibJWT.cmake new file mode 100644 index 000000000..e0c834609 --- /dev/null +++ b/cmake/Modules/FindLibJWT.cmake @@ -0,0 +1,28 @@ +include(FindPackageHandleStandardArgs) + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_LIBJWT QUIET libjwt) + +find_path(LIBJWT_INCLUDE_DIR + NAMES jwt.h + HINTS ${PC_LIBJWT_INCLUDEDIR} ${PC_LIBJWT_INCLUDE_DIRS} + ) + +find_library(LIBJWT_LIBRARY + NAMES jwt-gnutls libjwt-gnutls liblibjwt-gnutls + HINTS ${PC_LIBJWT_LIBDIR} ${PC_LIBJWT_LIBRARY_DIR} + ) + +find_package_handle_standard_args(LibJWT + REQUIRED_VARS LIBJWT_INCLUDE_DIR LIBJWT_LIBRARY + ) + +if(LIBJWT_FOUND) + add_library(libjwt STATIC IMPORTED GLOBAL) + set_target_properties(libjwt PROPERTIES + IMPORTED_LOCATION "${LIBJWT_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBJWT_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(LIBJWT_INCLUDE_DIR LIBJWT_LIBRARY) \ No newline at end of file diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 35d613067..899743355 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -63,7 +63,7 @@ DROP TABLE IF EXISTS `Controls`; CREATE TABLE `Controls` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Protocol` varchar(64) default NULL, `CanWake` tinyint(3) unsigned NOT NULL default '0', `CanSleep` tinyint(3) unsigned NOT NULL default '0', @@ -406,7 +406,7 @@ DROP TABLE IF EXISTS `MonitorPresets`; CREATE TABLE `MonitorPresets` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Device` tinytext, `Channel` tinyint(3) unsigned default NULL, `Format` int(10) unsigned default NULL, @@ -440,7 +440,7 @@ CREATE TABLE `Monitors` ( `Notes` TEXT, `ServerId` int(10) unsigned, `StorageId` smallint(5) unsigned default 0, - `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local', + `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor', `Enabled` tinyint(3) unsigned NOT NULL default '1', `LinkedMonitors` varchar(255), @@ -734,13 +734,14 @@ CREATE TABLE `Storage` ( `Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium', `ServerId` int(10) unsigned, `DoDelete` BOOLEAN NOT NULL DEFAULT true, + `Enabled` BOOLEAN NOT NULL DEFAULT true, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Create a default storage location -- -insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, NULL, 'Medium', 0, true ); +insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, NULL, 'Medium', 0, true, true ); /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; diff --git a/db/zm_update-1.34.6.sql b/db/zm_update-1.34.6.sql new file mode 100644 index 000000000..1a58bee1f --- /dev/null +++ b/db/zm_update-1.34.6.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.5 database to 1.34.6 +-- +-- No changes required +-- diff --git a/db/zm_update-1.34.7.sql b/db/zm_update-1.34.7.sql new file mode 100644 index 000000000..ba86b1202 --- /dev/null +++ b/db/zm_update-1.34.7.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.34.6 database to 1.34.7 +-- +-- No changes required +-- diff --git a/db/zm_update-1.35.1.sql b/db/zm_update-1.35.1.sql new file mode 100644 index 000000000..d250cf751 --- /dev/null +++ b/db/zm_update-1.35.1.sql @@ -0,0 +1,12 @@ + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Storage' + AND column_name = 'Enabled' + ) > 0, +"SELECT 'Column Enabled already exists in Storage'", +"ALTER TABLE `Storage` ADD `Enabled` BOOLEAN NOT NULL default true AFTER `DoDelete`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.35.2.sql b/db/zm_update-1.35.2.sql new file mode 100644 index 000000000..55eabd887 --- /dev/null +++ b/db/zm_update-1.35.2.sql @@ -0,0 +1 @@ +ALTER TABLE Monitors MODIFY `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local'; diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index c0ac0af70..923e0c068 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -28,7 +28,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.35.0 +Version: 1.35.2 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons diff --git a/distros/ubuntu1204/NEWS b/distros/ubuntu1204/NEWS deleted file mode 100644 index e69de29bb..000000000 diff --git a/distros/ubuntu1204/changelog b/distros/ubuntu1204/changelog deleted file mode 100644 index b19a3f228..000000000 --- a/distros/ubuntu1204/changelog +++ /dev/null @@ -1,8 +0,0 @@ -<<<<<<< HEAD -zoneminder (1.31.0-trusty) trusty; urgency=medium - - * placeholder - - -- Isaac Connor Fri, 13 May 2016 09:45:49 -0400 -======= ->>>>>>> master diff --git a/distros/ubuntu1204/watch b/distros/ubuntu1204/watch deleted file mode 100644 index 7ee690edb..000000000 --- a/distros/ubuntu1204/watch +++ /dev/null @@ -1,7 +0,0 @@ -version=3 - -opts=\ -repacksuffix=+dfsg,\ -dversionmangle=s{\+dfsg\d*}{},\ - https://github.com/ZoneMinder/ZoneMinder/releases \ - .*/ZoneMinder/archive/v(.*).tar.gz diff --git a/distros/ubuntu1204/zoneminder.init b/distros/ubuntu1204/zoneminder.init deleted file mode 100644 index de552848b..000000000 --- a/distros/ubuntu1204/zoneminder.init +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: zoneminder -# Required-Start: $network $remote_fs $syslog -# Required-Stop: $network $remote_fs $syslog -# Should-Start: mysql -# Should-Stop: mysql -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Control ZoneMinder as a Service -# Description: ZoneMinder CCTV recording and surveillance system -### END INIT INFO -# chkconfig: 2345 20 20 - -# Source function library. -. /lib/lsb/init-functions - -prog=ZoneMinder -ZM_PATH_BIN="/usr/bin" -RUNDIR="/var/run/zm" -TMPDIR="/tmp/zm" -command="$ZM_PATH_BIN/zmpkg.pl" - -start() { - echo -n "Starting $prog: " - # Wait for mysqld to start. Continue if it takes too long. - count=0 - while [ ! -e /var/run/mysqld/mysqld.sock ] && [ $count -lt 60 ] - do - sleep 1 - count=$((count+1)) - done - export TZ=:/etc/localtime - mkdir -p "$RUNDIR" && chown www-data:www-data "$RUNDIR" - mkdir -p "$TMPDIR" && chown www-data:www-data "$TMPDIR" - $command start - RETVAL=$? - [ $RETVAL = 0 ] && echo success - [ $RETVAL != 0 ] && echo failure - echo - [ $RETVAL = 0 ] && touch /var/lock/zm - return $RETVAL -} -stop() { - echo -n "Stopping $prog: " - # - # Why is this status check being done? - # as $command stop returns 1 if zoneminder - # is stopped, which will result in - # this returning 1, which will stuff - # dpkg when it tries to stop zoneminder before - # uninstalling . . . - # - result=`$command status` - if [ ! "$result" = "running" ]; then - echo "Zoneminder already stopped" - echo - RETVAL=0 - else - $command stop - RETVAL=$? - [ $RETVAL = 0 ] && echo success - [ $RETVAL != 0 ] && echo failure - echo - [ $RETVAL = 0 ] && rm -f /var/lock/zm - fi -} -status() { - result=`$command status` - if [ "$result" = "running" ]; then - echo "ZoneMinder is running" - RETVAL=0 - else - echo "ZoneMinder is stopped" - RETVAL=1 - fi -} - -case "$1" in -'start') - start - ;; -'stop') - stop - ;; -'restart' | 'force-reload') - stop - start - ;; -'status') - status - ;; -*) - echo "Usage: $0 { start | stop | restart | status }" - RETVAL=1 - ;; -esac -exit $RETVAL diff --git a/distros/ubuntu1204/zoneminder.links b/distros/ubuntu1204/zoneminder.links deleted file mode 100644 index 373548919..000000000 --- a/distros/ubuntu1204/zoneminder.links +++ /dev/null @@ -1 +0,0 @@ -/tmp/zm /usr/share/zoneminder/www/api/app/tmp diff --git a/distros/ubuntu1204/zoneminder.postinst b/distros/ubuntu1204/zoneminder.postinst deleted file mode 100644 index 603786ff6..000000000 --- a/distros/ubuntu1204/zoneminder.postinst +++ /dev/null @@ -1,66 +0,0 @@ -#! /bin/sh - -set -e - -if [ "$1" = "configure" ]; then - - . /etc/zm/zm.conf - for i in /etc/zm/conf.d/*.conf; do - . $i - done; - - - # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group - chown www-data:root /var/log/zm - chown www-data:www-data /var/lib/zm - if [ -z "$2" ]; then - chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* - fi - - # Do this every time the package is installed or upgraded - # Ensure zoneminder is stopped - invoke-rc.d zoneminder stop || true - - if [ "$ZM_DB_HOST" = "localhost" ]; then - if [ -e "/etc/init.d/mysql" ]; then - # - # Get mysql started if it isn't - # - if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then - invoke-rc.d mysql start - fi - if $(/etc/init.d/mysql status >/dev/null 2>&1); then - mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload - # test if database if already present... - if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then - cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf - if [ $? -ne 0 ]; then - echo "Error creating db." - exit 1; - fi - # This creates the user. - echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql - else - echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql - fi - - zmupdate.pl --nointeractive - zmupdate.pl --nointeractive -f - - # Add any new PTZ control configurations to the database (will not overwrite) - zmcamtool.pl --import >/dev/null 2>&1 - - else - echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' - fi - else - echo 'mysql not found, assuming remote server.' - fi - else - echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" - fi - echo "Done Updating, starting ZoneMinder" - invoke-rc.d zoneminder start || true -fi - -#DEBHELPER# diff --git a/distros/ubuntu1204/zoneminder.preinst b/distros/ubuntu1204/zoneminder.preinst deleted file mode 100644 index 3f75a1b3e..000000000 --- a/distros/ubuntu1204/zoneminder.preinst +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/sh - -set -e - -## Remove obsolete symlink which is in the way of dh_apache2: -ol="/etc/apache2/conf-available/zoneminder.conf" -if [ -h "${ol}" ]; then - [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" -fi - -abort=false -if [ -h /usr/share/zoneminder/www/events ]; then - l=$(readlink /usr/share/zoneminder/www/events) - if [ "$l" != "/var/cache/zoneminder/events" ]; then - abort=true - fi -fi -if [ -h /usr/share/zoneminder/www/images ]; then - l=$(readlink /usr/share/zoneminder/www/images ) - if [ "$l" != "/var/cache/zoneminder/images" ]; then - abort=true - fi -fi - -if [ "$abort" = "true" ]; then - cat >&2 << EOF -Aborting installation of zoneminder due to non-default symlinks in -/usr/share/zoneminder for the images and/or events directory, which could -result in loss of data. Please move your data in each of these directories to -/var/cache/zoneminder before installing zoneminder from the package. -EOF - exit 1 - -fi - -#DEBHELPER# diff --git a/distros/ubuntu1204/zoneminder.tmpfile b/distros/ubuntu1204/zoneminder.tmpfile deleted file mode 100644 index 017955900..000000000 --- a/distros/ubuntu1204/zoneminder.tmpfile +++ /dev/null @@ -1,3 +0,0 @@ -d /var/run/zm 0755 www-data www-data -d /tmp/zm 0755 www-data www-data -d /var/tmp/zm 0755 www-data www-data diff --git a/distros/ubuntu1410/README.Debian b/distros/ubuntu1410/README.Debian deleted file mode 100644 index a49b6be72..000000000 --- a/distros/ubuntu1410/README.Debian +++ /dev/null @@ -1,51 +0,0 @@ -zoneminder for Debian ---------------------- - -There is one manual step to get the web interface working. -You need to link /etc/zm/apache.conf to /etc/apache2/conf.d/zoneminder.conf, -then reload the apache config (i.e. /etc/init.d/apache2 reload) - -Changing the location for images and events -------------------------------------------- - -Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This -package modifies that by changing /usr/share/zoneminder/images and -/usr/share/zoneminder/events to symlinks to directories under -/var/cache/zoneminder. - -There are numerous places these could be put and ways to do it. But, at the -moment, if you change this, an upgrade will fail with a warning about these -locations having changed (the reason for this was that previously, an upgrade -would silently revert the changes and cause event loss - refer -bug #608793). - -If you do want to change the location, here are a couple of suggestions. - -These lines would mount /dev/sdX1 to /video_storage, and then 'link' /video_storage -to the locations that ZoneMinder expects them to be at. - - /dev/sdX1 /video_storage ext4 defaults 0 2 - /video_storage/zoneminder/images /var/cache/zoneminder/images none bind 0 2 - /video_storage/zoneminder/events /var/cache/zoneminder/events none bind 0 2 - - or if you have a separate partition for each: - - /dev/sdX1 /var/cache/zoneminder/images ext4 defaults 0 2 - /dev/sdX2 /var/cache/zoneminder/events ext4 defaults 0 2 - - - - -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100 - -Access to /dev/video* ---------------------- - -For cameras which require access to /dev/video*, zoneminder may need the -www-data user added to the video group in order to see those cameras: - - adduser www-data video - -Note that all web applications running on the zoneminder server will then have -access to all video devices on the system. - - -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 diff --git a/distros/ubuntu1410/apache.conf b/distros/ubuntu1410/apache.conf deleted file mode 100644 index 92a2b6414..000000000 --- a/distros/ubuntu1410/apache.conf +++ /dev/null @@ -1,9 +0,0 @@ -Alias /zm /usr/share/zoneminder - - - php_flag register_globals off - Options Indexes FollowSymLinks - - DirectoryIndex index.php - - diff --git a/distros/ubuntu1410/changelog b/distros/ubuntu1410/changelog deleted file mode 100644 index 55e3d17b0..000000000 --- a/distros/ubuntu1410/changelog +++ /dev/null @@ -1,323 +0,0 @@ -zoneminder (1.30.2-trusty-2016033001) trusty; urgency=medium - - * merge master - - -- Isaac Connor Wed, 30 Mar 2016 14:09:48 -0400 - -zoneminder (1.30.2-trusty-2016032901) trusty; urgency=medium - - * filter fixes, merge options rework by Kyle - - -- Isaac Connor Tue, 29 Mar 2016 12:27:57 -0400 - -zoneminder (1.30.2-trusty-2016030702) trusty; urgency=medium - - * - - -- Isaac Connor Mon, 07 Mar 2016 22:14:03 -0500 - -zoneminder (1.30.2-trusty-2016030701) trusty; urgency=medium - - * merge master. with telemetry - - -- Isaac Connor Mon, 07 Mar 2016 21:47:53 -0500 - -zoneminder (1.30.2-trusty-2016022101) trusty; urgency=medium - - * merge zmtrigger fix - - -- Isaac Connor Mon, 22 Feb 2016 09:15:53 -0500 - -zoneminder (1.30.2-trusty-2016021901) trusty; urgency=medium - - * zmtrigger improvements - - -- Isaac Connor Fri, 19 Feb 2016 11:09:57 -0500 - -zoneminder (1.30.2-trusty-2016021701) trusty; urgency=medium - - * printout id, and ip address when failing to connect - - -- Isaac Connor Wed, 17 Feb 2016 09:40:49 -0500 - -zoneminder (1.30.2-trusty-2016021001) trusty; urgency=medium - - * - - -- Isaac Connor Wed, 10 Feb 2016 13:06:09 -0500 - -zoneminder (1.29.111-trusty-2016020101) trusty; urgency=medium - - * Fix video download and use of Storage Areas - - -- Isaac Connor Mon, 01 Feb 2016 13:42:06 -0500 - -zoneminder (1.29.111-trusty-2016011401) trusty; urgency=medium - - * fix timeline view for storageareas - - -- Isaac Connor Thu, 14 Jan 2016 14:03:41 -0500 - -zoneminder (1.29.111-trusty-2016010801) trusty; urgency=medium - - * Add better debug and skip when event links are not just digits. Merge multi-server stuff from master. - - -- Isaac Connor Fri, 08 Jan 2016 10:37:16 -0500 - -zoneminder (1.29.111-trusty-2016010401) trusty; urgency=medium - - * include fix to rotate image dimensions when applying a rotation - - -- Isaac Connor Mon, 04 Jan 2016 13:24:42 -0500 - -zoneminder (1.29.111-trusty-2016010101) trusty; urgency=medium - - * fix logging with multi-server - - -- Isaac Connor Fri, 01 Jan 2016 17:11:09 -0500 - -zoneminder (1.29.111-trusty-2015123101) trusty; urgency=medium - - * Add log filtering from multi-server - - -- Isaac Connor Thu, 31 Dec 2015 10:18:03 -0500 - -zoneminder (1.29.109-trusty-2015122401) trusty; urgency=medium - - * fix delete events not in database in zmaudit.pl - - -- Isaac Connor Thu, 24 Dec 2015 12:38:05 -0500 - -zoneminder (1.29.109-trusty-2015122301) trusty; urgency=medium - - * todays release - - -- Isaac Connor Wed, 23 Dec 2015 09:33:46 -0500 - -zoneminder (1.29.109-trusty-2015122202) trusty; urgency=medium - - * more object work and zmaudit - - -- Isaac Connor Tue, 22 Dec 2015 13:13:56 -0500 - -zoneminder (1.29.109-trusty-2015122201) trusty; urgency=medium - - * merge multi-server, and master stuff, start work on zmaudit storage areas support - - -- Isaac Connor Tue, 22 Dec 2015 11:11:44 -0500 - -zoneminder (1.29.109-trusty-2015122103) trusty; urgency=medium - - * Fixes - - -- Isaac Connor Mon, 21 Dec 2015 15:20:15 -0500 - -zoneminder (1.29.109-trusty-2015122102) trusty; urgency=medium - - * fix deleting events - - -- Isaac Connor Mon, 21 Dec 2015 14:49:12 -0500 - -zoneminder (1.29.109-trusty-2015122101) trusty; urgency=medium - - * Make zmfilter work. - - -- Isaac Connor Mon, 21 Dec 2015 12:32:27 -0500 - -zoneminder (1.29.109-trusty-2015121803) trusty; urgency=medium - - * merge zmvideo improvements - - -- Isaac Connor Fri, 18 Dec 2015 14:17:56 -0500 - -zoneminder (1.29.109-trusty-2015121802) trusty; urgency=medium - - * Add some missing files to the autoconf build - - -- Isaac Connor Fri, 18 Dec 2015 12:14:08 -0500 - -zoneminder (1.29.109-trusty-2015121801) trusty; urgency=medium - - * - - -- Isaac Connor Fri, 18 Dec 2015 11:05:58 -0500 - -zoneminder (1.29.109-trusty-2015121701) trusty; urgency=medium - - * Merge master + better zmvideo - - -- Isaac Connor Thu, 17 Dec 2015 15:11:18 -0500 - -zoneminder (1.29.0-trusty-2015112301) trusty; urgency=medium - - * apply fix for zms crash - - -- Isaac Connor Mon, 23 Nov 2015 10:47:49 -0500 - -zoneminder (1.29.0-trusty-2015110601) trusty; urgency=medium - - * add a FIONREAD test on timeout - - -- Isaac Connor Wed, 22 Jul 2015 09:55:37 -0400 - -zoneminder (1.29.0-trusty-2015072201) trusty; urgency=medium - - * add AnalysisFPS - - -- Isaac Connor Wed, 22 Jul 2015 09:55:37 -0400 - -zoneminder (1.29.0-trusty-2015071601) trusty; urgency=medium - - * Merge master and zmtrigger - - -- Isaac Connor Thu, 16 Jul 2015 13:15:58 -0400 - -zoneminder (1.29.0-trusty-38) trusty; urgency=medium - - * Merge master - - -- Isaac Connor Tue, 14 Jul 2015 10:15:00 -0400 - -zoneminder (1.29.0-trusty-37) trusty; urgency=medium - - * merge master api stuff, set sleep after failure to capture to 5000 instead of 5000000 - - -- Isaac Connor Fri, 19 Jun 2015 09:59:54 -0400 - -zoneminder (1.29.0-trusty-36) trusty; urgency=medium - - * Detect select interuption when no action on our fd - - -- Isaac Connor Thu, 28 May 2015 09:35:59 -0400 - -zoneminder (1.29.0-trusty-35) trusty; urgency=medium - - * better logging - - -- Isaac Connor Fri, 22 May 2015 15:30:42 -0400 - -zoneminder (1.29.0-trusty-34) trusty; urgency=medium - - * Faster shutdown and changes to ReadData - - -- Isaac Connor Thu, 21 May 2015 15:54:47 -0400 - -zoneminder (1.29.0-trusty-33) trusty; urgency=medium - - * update zmaudit some more - - -- Isaac Connor Thu, 14 May 2015 13:44:35 -0400 - -zoneminder (1.29.0-trusty-32) trusty; urgency=medium - - * merge zmaudit_updates1 - - -- Isaac Connor Wed, 13 May 2015 15:51:56 -0400 - -zoneminder (1.29.0-trusty-31) trusty; urgency=medium - - * Merge some fixes and zmaudit improvements - - -- Isaac Connor Wed, 13 May 2015 15:16:19 -0400 - -zoneminder (1.29.0-trusty-27) trusty; urgency=medium - - * fflush logs, merge master. - - -- Isaac Connor Mon, 30 Mar 2015 19:28:05 -0400 - -zoneminder (1.29.0-vivid-26) vivid; urgency=medium - - * logging improvements, merge RedData changes - - -- Isaac Connor Wed, 04 Mar 2015 16:39:19 -0500 - -zoneminder (1.29.0-vivid-25) vivid; urgency=medium - - * some change to ReadData - - -- Isaac Connor Mon, 02 Mar 2015 12:57:16 -0500 - -zoneminder (1.29.0-utopic-24) utopic; urgency=medium - - * merge local_raw - - -- Isaac Connor Mon, 23 Feb 2015 17:49:36 -0500 - -zoneminder (1.29.0-trusty-23) trusty; urgency=medium - - * more onvif merges, fix to zmfilter - - -- Isaac Connor Sat, 21 Feb 2015 16:17:01 -0500 - -zoneminder (1.29.0-trusty-22) trusty; urgency=medium - - * updates from master: merge onvif, default to classic if skin not defined. - - -- Isaac Connor Thu, 19 Feb 2015 18:14:58 -0500 - -zoneminder (1.29.0-utopic-21) utopic; urgency=medium - - * updates from master, improve monitor probing and support TRENDnet cameras - - -- Isaac Connor Tue, 17 Feb 2015 14:18:52 -0500 - -zoneminder (1.29.0-wheezy-20) wheezy; urgency=medium - - * zmaudit.pl improvements - double check db before deleting fs event - - -- Isaac Connor Wed, 04 Feb 2015 11:09:22 -0500 - -zoneminder (1.29.0-utopic-18) utopic; urgency=medium - - * RTSP Timeout fixes - - -- Isaac Connor Wed, 28 Jan 2015 13:49:16 -0500 - -zoneminder (1.29.0-utopic-17) utopic; urgency=medium - - * Merge master, use new split-up debian build - - -- Isaac Connor Mon, 26 Jan 2015 11:21:07 -0500 - -zoneminder (1.28.0+nmu1) testing; urgency=medium - - * Non-maintainer upload - * Split the debian package into several packages - * Switch to native source format - - -- Emmanuel Papin Thu, 15 Jan 2015 20:00:08 +0100 - -zoneminder (1.28.0-0.2) testing; urgency=medium - - * Non-maintainer upload. - * Upstream release for debian jessie - * Package dependencies updated - * debhelper version upgraded - * Standards-Version upgraded - * Use debhelper commands instead of standard commands - * Install man pages in /usr/share/man (patch added) - * Switch to quilt - * Switch to systemd - * Some lintian fixes - - -- Emmanuel Papin Wed, 26 Nov 2014 00:26:01 +0100 - -zoneminder (1.28.0-0.1) stable; urgency=medium - - * Release - - -- Isaac Connor Fri, 17 Oct 2014 09:27:22 -0400 - -zoneminder (1.27.99+1-testing-SNAPSHOT2014072901) testing; urgency=medium - - * improve error messages - * Make zmupdate re-run the most recent patch so that people running the daily builds get their db updates - - -- Isaac Connor Tue, 29 Jul 2014 14:50:20 -0400 - -zoneminder (1.27.0+1-testing-v4ltomonitor-1) testing; urgency=high - - * Snapshot release - - - -- Isaac Connor Wed, 09 Jul 2014 21:35:29 -0400 diff --git a/distros/ubuntu1410/compat b/distros/ubuntu1410/compat deleted file mode 100644 index ec635144f..000000000 --- a/distros/ubuntu1410/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/distros/ubuntu1410/control b/distros/ubuntu1410/control deleted file mode 100644 index 5a873f133..000000000 --- a/distros/ubuntu1410/control +++ /dev/null @@ -1,122 +0,0 @@ -Source: zoneminder -Section: net -Priority: optional -Maintainer: Isaac Connor -Build-Depends: debhelper (>= 9), po-debconf (>= 1.0), autoconf, automake, libphp-serialization-perl, libgnutls-dev, libmysqlclient-dev | libmariadbclient-dev, libdbd-mysql-perl, libdate-manip-perl, libwww-perl, libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev, libpcre3-dev, libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libv4l-dev (>= 0.8.3), libbz2-dev, libtool, libsys-mmap-perl, libavdevice-dev, libdevice-serialport-perl, libarchive-zip-perl, libmime-lite-perl, dh-autoreconf, libvlccore-dev, libvlc-dev, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libgcrypt11-dev | libgcrypt20-dev, libpolkit-gobject-1-dev, libdbi-perl, libnet-sftp-foreign-perl, libexpect-perl, libmime-tools-perl, libx264-dev, libmp4v2-dev, libpcre3-dev -Standards-Version: 3.9.6 - -Package: zoneminder -Section: metapackages -Architecture: all -Depends: ${misc:Depends}, - libzoneminder-perl (>= ${source:Version}), - zoneminder-database (>= ${source:Version}), - zoneminder-core (>= ${binary:Version}), - zoneminder-ui-base (>= ${source:Version}), - zoneminder-ui-classic (>= ${source:Version}), - zoneminder-ui-mobile (>= ${source:Version}), - zoneminder-ui-xml (>= ${source:Version}) -Description: Video camera security and surveillance solution (metapackage) - ZoneMinder is intended for use in single or multi-camera video security - applications, including commercial or home CCTV, theft prevention and child - or family member or home monitoring and other care scenarios. It - supports capture, analysis, recording, and monitoring of video data coming - from one or more video or network cameras attached to a Linux system. - ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom - cameras using a variety of protocols. It is suitable for use as a home - video security system and for commercial or professional video security - and surveillance. It can also be integrated into a home automation system - via X.10 or other protocols. - -Package: libzoneminder-perl -Section: perl -Architecture: all -Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl, - libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, libjson-maybexs-perl, - libsys-mmap-perl, liburi-encode-perl, libwww-perl -Description: Perl libraries for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the libraries for the perl scripts, it can be used to - write custom interfaces as well. - -Package: zoneminder-database -Section: database -Architecture: all -Depends: ${misc:Depends}, debconf, dbconfig-common, - mysql-client | mariadb-client -Recommends: mysql-server | mariadb-server -Description: Database management package for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the sql files and maintenance scripts to perform all the - database operations (installation, upgrade or removal) on a local or a remote - server. - -Package: zoneminder-core -Section: video -Architecture: any -Depends: libzoneminder-perl (= ${source:Version}), - zoneminder-database (= ${source:Version}), ${shlibs:Depends}, ${misc:Depends}, - ${perl:Depends}, libarchive-tar-perl, libarchive-zip-perl, libdate-manip-perl, - libdbi-perl, libmodule-load-conditional-perl, libmime-lite-perl, - libmime-tools-perl, libnet-sftp-foreign-perl, libphp-serialization-perl, - debconf, ffmpeg | libav-tools, rsyslog | system-log-daemon, zip, - policykit-1, apache2, libmp4v2-2, libpcre++0 -Description: Core binaries and perl scripts for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the executable compiled binaries which do the main video - processing work and the perl scripts which perform helper and/or external - interface tasks. - -Package: zoneminder-core-dbg -Priority: extra -Section: debug -Architecture: any -Depends: zoneminder-core (= ${binary:Version}), ${misc:Depends} -Description: Debugging symbols for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the debugging symbols for the executable compiled - binaries. - -Package: zoneminder-ui-base -Section: web -Architecture: any -Depends: zoneminder-core (= ${binary:Version}), ${shlibs:Depends}, - ${misc:Depends}, debconf, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, - php5, php5-mysql | php5-mysqlnd, php5-gd -Description: Essential files for ZoneMinder's web user interface - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the essential web files and maintenance scripts to set up - a basic web environment. - -Package: zoneminder-ui-classic -Section: web -Architecture: all -Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} -Description: Classic web user interface for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the classic web user interface. - -Package: zoneminder-ui-mobile -Section: web -Architecture: all -Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} -Description: Mobile web user interface for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides the web user interface for mobile devices. - -Package: zoneminder-ui-xml -Section: web -Architecture: all -Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} -Description: XML interface for ZoneMinder - ZoneMinder is a video camera security and surveillance solution. - . - This package provides a XML interface mainly intended for use with the eyeZm - iPhone Application, but can be used with any other custom programs as well. diff --git a/distros/ubuntu1410/copyright b/distros/ubuntu1410/copyright deleted file mode 100644 index a177502a0..000000000 --- a/distros/ubuntu1410/copyright +++ /dev/null @@ -1,22 +0,0 @@ -Copyright: - -Copyright 2002 Philip Coombes - -License: - -This package is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2 of the License, or (at your -option) any later version. - -This package is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public -License along with this package; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -On Debian GNU/Linux systems, the text of the GPL can be found in -/usr/share/common-licenses/GPL. diff --git a/distros/ubuntu1410/docs b/distros/ubuntu1410/docs deleted file mode 100644 index b43bf86b5..000000000 --- a/distros/ubuntu1410/docs +++ /dev/null @@ -1 +0,0 @@ -README.md diff --git a/distros/ubuntu1410/libzoneminder-perl.install b/distros/ubuntu1410/libzoneminder-perl.install deleted file mode 100644 index 792ffc15e..000000000 --- a/distros/ubuntu1410/libzoneminder-perl.install +++ /dev/null @@ -1,4 +0,0 @@ -usr/share/perl5/ZoneMinder -usr/share/perl5/ZoneMinder.pm -debian/tmp/usr/share/man/man3/ZoneMinder.3pm -debian/tmp/usr/share/man/man3/ZoneMinder::* diff --git a/distros/ubuntu1410/patches/series b/distros/ubuntu1410/patches/series deleted file mode 100644 index e69de29bb..000000000 diff --git a/distros/ubuntu1410/po/POTFILES.in b/distros/ubuntu1410/po/POTFILES.in deleted file mode 100644 index 5b155907e..000000000 --- a/distros/ubuntu1410/po/POTFILES.in +++ /dev/null @@ -1,3 +0,0 @@ -[type: gettext/rfc822deb] zoneminder-core.templates -[type: gettext/rfc822deb] zoneminder-database.templates -[type: gettext/rfc822deb] zoneminder-ui-base.templates diff --git a/distros/ubuntu1410/po/fr.po b/distros/ubuntu1410/po/fr.po deleted file mode 100644 index 85ced7fd2..000000000 --- a/distros/ubuntu1410/po/fr.po +++ /dev/null @@ -1,252 +0,0 @@ -# debconf french translation file for ZoneMinder. -# Copyright (C) 2001-2008 Philip Coombes -# This file is distributed under the same license as the zoneminder package. -# First author: Emmanuel Papin , 2014. -# -msgid "" -msgstr "" -"Project-Id-Version: zoneminder\n" -"Report-Msgid-Bugs-To: zoneminder@packages.debian.org\n" -"POT-Creation-Date: 2014-12-16 12:34+0100\n" -"PO-Revision-Date: 2014-12-07 00:40+0100\n" -"Last-Translator: Emmanuel Papin \n" -"Language-Team: French \n" -"Language: fr\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "Delete this non empty directory?" -msgstr "Supprimer ce répertoire non vide ?" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"A purge of the ZoneMinder package is performed but the directory '/var/cache/" -"zoneminder' is not empty so it will not be deleted." -msgstr "" -"Une purge du paquet ZoneMinder est en cours mais le répertoire '/var/cache/" -"zoneminder' n'est pas vide et sera donc conservé." - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"Please consider that this directory is designed to contain data resulting " -"from event detection. Therefore, \"proof of evidence\" could be lost!\"" -msgstr "" -"Veuillez considérer que ce répertoire est conçu pour contenir des données " -"résultants de la détection d'événements. Par conséquent, des preuves " -"pourraient être perdues !" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"If you are not sure of your decision, please do not delete this directory " -"but perform a manual checkup." -msgstr "" -"Si vous n'êtes pas sûr de votre décision, veuillez conserver ce répertoire " -"et effectuer une vérification manuelle." - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "Deletion confirmed?" -msgstr "Supression confirmée ?" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "" -"You have allowed the deletion of directory '/var/cache/zoneminder' although " -"it may contain critical data." -msgstr "" -"Vous avez autorisé la suppression du répertoire '/var/cache/zoneminder' bien " -"qu'il puisse contenir des données critiques." - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "local" -msgstr "local" - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "remote" -msgstr "distant" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "Database location:" -msgstr "Emplacement de la base de donnée :" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"A database server is required to run ZoneMinder. The database can be " -"installed either locally or remotely on a machine of your network." -msgstr "" -"Un serveur de base de données est requis pour ZoneMinder. La base de donnée " -"peut être installée localement ou à distance sur une machine de votre réseau." - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"If you choose a remote location, you will have to select the 'tcp/ip' " -"connection method and enter the hostname or ip address of the remote machine " -"in the next configuration screens." -msgstr "" -"Si vous choisissez un emplacement distant, vous devrez sélectionner la " -"méthode de connexion 'tcp/ip' et entrer le nom réseau ou l'adresse ip de la " -"machine distante dans les écrans de configuration suivants." - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "No local database server is available:" -msgstr "Aucun serveur local de base de données n'est disponible :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"Currently ZoneMinder supports mysql or mariadb database server but none of " -"them appears to be installed on this machine." -msgstr "" -"Actuellement ZoneMinder supporte les serveurs de base de données mysql et " -"mariadb mais aucun d'entre eux n'est installé sur cette machine." - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"In order to complete ZoneMinder's installation, after ending of this " -"assistant, please install a compatible database server and then restart the " -"assistant by invoking:" -msgstr "" -"Afin de compléter l'installation de ZoneMinder, après la fermeture de cet " -"assitant, veuillez installer un serveur de base de données compatible et " -"ensuite redémarrez l'assistant en invoquant :" - -#. Type: error -#. Description -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure zoneminder" -msgstr "$ sudo dpkg-reconfigure zoneminder" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "Remote database servers are not allowed:" -msgstr "Les serveurs de base de données distants ne sont pas autorisés :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"The current configuration of dbconfig-common does not allow installation of " -"a database on remote servers." -msgstr "" -"La configuration actuelle de dbconfig-common ne permet pas l'installation de " -"bases de données sur des serveurs distants." - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"In order to reconfigure dbconfig-common, please invoke the following command " -"after ending of this assistant:" -msgstr "" -"Afin de reconfigurer dbconfig-common, veuillez invoquer la commande suivante " -"après la fermeture de cet assitant :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure dbconfig-common" -msgstr "$ sudo dpkg-reconfigure dbconfig-common" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"Then, to complete ZoneMinder's installation, please restart this assistant " -"by invoking:" -msgstr "" -"Ensuite, pour compléter l'installation de ZoneMinder, veuillez redémarrer " -"cet assistant en invoquant :" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "New password for the ZoneMinder 'admin' user:" -msgstr "Nouveau mot de passe pour le compte 'admin' de ZoneMinder :" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "Please enter the password of the default administrative user." -msgstr "Veuillez entrer le mot de passe du compte administrateur par défaut." - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "" -"While not mandatory, it is highly recommended that you set a custom password " -"for the administrative 'admin' user." -msgstr "" -"Bien que cela ne soit pas obligatoire, il est fortement recommandé de " -"fournir un mot de passe personnalisé pour le compte administrateur 'admin'." - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "If this field is left blank, the password will not be changed." -msgstr "Si le champ est laissé vide, le mot de passe ne sera pas changé." - -#. Type: password -#. Description -#: ../zoneminder-database.templates:5001 -msgid "Repeat password for the ZoneMinder 'admin' user:" -msgstr "Répéter le mot de passe pour le compte 'admin' de ZoneMinder :" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "Password input error" -msgstr "Erreur de mot de passe" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "The two passwords you entered were not the same. Please try again." -msgstr "" -"Les deux mots de passe saisis ne sont pas les mêmes. Veuillez essayer à " -"nouveau." - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "Web server to reconfigure automatically:" -msgstr "Serveur web à reconfigurer automatiquement :" - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "" -"Please choose the web server that should be automatically configured for " -"ZoneMinder's web portal access." -msgstr "" -"Veuillez choisir le serveur web à reconfigurer automatiquement pour l'accès " -"au portail web de ZoneMinder." diff --git a/distros/ubuntu1410/po/templates.pot b/distros/ubuntu1410/po/templates.pot deleted file mode 100644 index 941a4094e..000000000 --- a/distros/ubuntu1410/po/templates.pot +++ /dev/null @@ -1,222 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: zoneminder\n" -"Report-Msgid-Bugs-To: zoneminder@packages.debian.org\n" -"POT-Creation-Date: 2014-12-16 12:34+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "Delete this non empty directory?" -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"A purge of the ZoneMinder package is performed but the directory '/var/cache/" -"zoneminder' is not empty so it will not be deleted." -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"Please consider that this directory is designed to contain data resulting " -"from event detection. Therefore, \"proof of evidence\" could be lost!\"" -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:1001 -msgid "" -"If you are not sure of your decision, please do not delete this directory " -"but perform a manual checkup." -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "Deletion confirmed?" -msgstr "" - -#. Type: boolean -#. Description -#: ../zoneminder-core.templates:2001 -msgid "" -"You have allowed the deletion of directory '/var/cache/zoneminder' although " -"it may contain critical data." -msgstr "" - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "local" -msgstr "" - -#. Type: select -#. Choices -#: ../zoneminder-database.templates:1001 -msgid "remote" -msgstr "" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "Database location:" -msgstr "" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"A database server is required to run ZoneMinder. The database can be " -"installed either locally or remotely on a machine of your network." -msgstr "" - -#. Type: select -#. Description -#: ../zoneminder-database.templates:1002 -msgid "" -"If you choose a remote location, you will have to select the 'tcp/ip' " -"connection method and enter the hostname or ip address of the remote machine " -"in the next configuration screens." -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "No local database server is available:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"Currently ZoneMinder supports mysql or mariadb database server but none of " -"them appears to be installed on this machine." -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 -msgid "" -"In order to complete ZoneMinder's installation, after ending of this " -"assistant, please install a compatible database server and then restart the " -"assistant by invoking:" -msgstr "" - -#. Type: error -#. Description -#. Type: error -#. Description -#: ../zoneminder-database.templates:2001 ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure zoneminder" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "Remote database servers are not allowed:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"The current configuration of dbconfig-common does not allow installation of " -"a database on remote servers." -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"In order to reconfigure dbconfig-common, please invoke the following command " -"after ending of this assistant:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "$ sudo dpkg-reconfigure dbconfig-common" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:3001 -msgid "" -"Then, to complete ZoneMinder's installation, please restart this assistant " -"by invoking:" -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "New password for the ZoneMinder 'admin' user:" -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "Please enter the password of the default administrative user." -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "" -"While not mandatory, it is highly recommended that you set a custom password " -"for the administrative 'admin' user." -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:4001 -msgid "If this field is left blank, the password will not be changed." -msgstr "" - -#. Type: password -#. Description -#: ../zoneminder-database.templates:5001 -msgid "Repeat password for the ZoneMinder 'admin' user:" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "Password input error" -msgstr "" - -#. Type: error -#. Description -#: ../zoneminder-database.templates:6001 -msgid "The two passwords you entered were not the same. Please try again." -msgstr "" - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "Web server to reconfigure automatically:" -msgstr "" - -#. Type: multiselect -#. Description -#: ../zoneminder-ui-base.templates:1001 -msgid "" -"Please choose the web server that should be automatically configured for " -"ZoneMinder's web portal access." -msgstr "" diff --git a/distros/ubuntu1410/rules b/distros/ubuntu1410/rules deleted file mode 100755 index 49d3549f1..000000000 --- a/distros/ubuntu1410/rules +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- -# Sample debian/rules that uses debhelper. -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -# These are used for cross-compiling and for saving the configure script -# from having to guess our platform (since we know it already) -DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) -DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) - -CFLAGS = -Wall -CPPFLAGS = -D__STDC_CONSTANT_MACROS -CXXFLAGS = -DHAVE_LIBCRYPTO - -ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) -DEBOPT = --enable-debug -CFLAGS += -g -CXXFLAGS += -g -else -DEBOPT = -endif - -ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) -CFLAGS += -O0 -else -CFLAGS += -O2 -endif - -# These are used to get the most recent version of the original sources from github -UURL = $(shell git config --get remote.origin.url) -BRANCH = $(shell git rev-parse --abbrev-ref HEAD) -HEAD = $(shell git rev-parse HEAD) -PKD = $(abspath $(dir $(MAKEFILE_LIST))) -PKG = $(word 2,$(shell dpkg-parsechangelog -l$(PKD)/changelog | grep ^Source)) -VER ?= $(shell dpkg-parsechangelog -l$(PKD)/changelog | perl -ne 'print $$1 if m{^Version:\s+(?:\d+:)?(\d.*)(?:\-|\+nmu\d+.*)};') -DTYPE = -TARBALL = ../$(PKG)_$(VER)$(DTYPE).orig.tar.xz - -%: - dh $@ --with autoreconf - -override_dh_auto_configure: - CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" dh_auto_configure -- \ - --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) \ - --sysconfdir=/etc/zm --prefix=/usr --mandir=\$${prefix}/share/man \ - --infodir=\$${prefix}/share/info --with-mysql=/usr \ - --with-mariadb=/usr --with-webdir=/usr/share/zoneminder \ - --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin \ - --with-webuser=www-data --with-webgroup=www-data \ - --enable-mmap=yes $(DEBOPT) - -override_dh_clean: - # Add here commands to clean up after the build process. - [ ! -f Makefile ] || $(MAKE) distclean - dh_clean src/zm_config_defines.h - # - # Delete remaining auto-generated Makefile if Makefile.in exists - find $(CURDIR)/ -type f -name "Makefile" | while read file; do \ - [ -f $$file.in ] && rm -f $$file; \ - done || true - # - # Delete remaining auto-generated Makefile.in if Makefile.am exists - find $(CURDIR)/ -type f -name "Makefile.in" | while read filein; do \ - fileam=`echo $$filein | sed 's/\(.*\)\.in/\1\.am/'`; \ - [ -f $$fileam ] && rm -f $$filein; \ - done || true - -override_dh_install: - dh_install --fail-missing - # - # NOTE: This is a short-term kludge; hopefully changes in the next - # upstream version will render this unnecessary. - rm -rf debian/zoneminder/usr/share/zoneminder/events - rm -rf debian/zoneminder/usr/share/zoneminder/images - rm -rf debian/zoneminder/usr/share/zoneminder/temp - # The link stuff for these folders has been moved to - # zoneminder-core.links file - # - # This is a slightly lesser kludge; moving the cgi stuff to - # /usr/share/zoneminder/cgi-bin breaks one set of behavior, - # having it just in /usr/lib/cgi-bin breaks another bit of - # behavior. - # The link stuff for /usr/share/zoneminder/cgi-bin has been moved to - # zoneminder-ui-base.links file - -override_dh_installinit: - dh_installinit --package=zoneminder-core --name=zoneminder - -override_dh_systemd_start: - dh_systemd_start --package=zoneminder-core --name=zoneminder \ - --restart-after-upgrade - -override_dh_systemd_enable: - dh_systemd_enable --package=zoneminder-core --name=zoneminder - -override_dh_fixperms: - dh_fixperms - # - # As requested by the Debian Webapps Policy Manual §3.2.1 - chown root:www-data debian/zoneminder-core/etc/zm/zm.conf - chmod 640 debian/zoneminder-core/etc/zm/zm.conf - -override_dh_auto_test: - # do not run tests... - -.PHONY: override_dh_strip -override_dh_strip: - dh_strip --dbg-package=zoneminder-core-dbg - -# Inspired by https://wiki.debian.org/onlyjob/get-orig-source -.PHONY: get-orig-source -get-orig-source: $(TARBALL) $(info I: $(PKG)_$(VER)$(DTYPE)) - @ - -$(TARBALL): - $(if $(wildcard $(PKG)-$(VER)),$(error folder '$(PKG)-$(VER)' exists, aborting...)) - @echo "# Cloning origin repository..."; \ - if ! git clone $(UURL) $(PKG)-$(VER); then \ - $(RM) -r $(PKG)-$(VER); \ - echo "failed to clone repository, aborting..."; \ - false; \ - fi - @if [ $(BRANCH) != "master" ]; then \ - cd $(PKG)-$(VER); \ - echo "# Not on master branch, fetching origin branch '$(BRANCH)'..."; \ - git fetch origin $(BRANCH):$(BRANCH) || false; \ - echo "# Switching to branch '$(BRANCH)'..."; \ - git checkout $(BRANCH) || false; \ - fi - @echo "# Checking local source..." - @if [ $$(cd $(PKG)-$(VER) && git rev-parse HEAD) = $(HEAD) ]; then \ - echo "even with origin, ok"; \ - true; \ - else \ - echo "not even with origin, aborting..."; \ - false; \ - fi - @echo "# Setting times..." - @cd $(PKG)-$(VER) \ - && for F in $$(git ls-tree -r --name-only HEAD | sed -e "s/\s/\*/g"); do \ - touch --no-dereference -d "$$(git log -1 --format="%ai" -- $$F)" "$$F"; \ - done - @echo "# Cleaning-up..." - cd $(PKG)-$(VER) && $(RM) -r .git - @echo "# Packing file '$(TARBALL)'..." - @find -L "$(PKG)-$(VER)" -xdev -type f -print | sort \ - | XZ_OPT="-6v" tar -caf "$(TARBALL)" -T- --owner=root --group=root --mode=a+rX \ - && $(RM) -r "$(PKG)-$(VER)" diff --git a/distros/ubuntu1410/source/format b/distros/ubuntu1410/source/format deleted file mode 100644 index 89ae9db8f..000000000 --- a/distros/ubuntu1410/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (native) diff --git a/distros/ubuntu1410/source/local-options b/distros/ubuntu1410/source/local-options deleted file mode 100644 index e69de29bb..000000000 diff --git a/distros/ubuntu1410/source/options b/distros/ubuntu1410/source/options deleted file mode 100644 index 8bd61fce6..000000000 --- a/distros/ubuntu1410/source/options +++ /dev/null @@ -1 +0,0 @@ -extend-diff-ignore = "(^|/)(config\.sub|config\.guess|Makefile|aclocal.m4|compile|config.h.in|configure|depcomp|install-sh|missing)$" diff --git a/distros/ubuntu1410/zoneminder-core.config b/distros/ubuntu1410/zoneminder-core.config deleted file mode 100644 index 2a15a599e..000000000 --- a/distros/ubuntu1410/zoneminder-core.config +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# config maintainer script for zoneminder-core package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.dirs b/distros/ubuntu1410/zoneminder-core.dirs deleted file mode 100644 index 350c32aff..000000000 --- a/distros/ubuntu1410/zoneminder-core.dirs +++ /dev/null @@ -1,4 +0,0 @@ -var/log/zm -var/cache/zoneminder/events -var/cache/zoneminder/images -var/cache/zoneminder/temp diff --git a/distros/ubuntu1410/zoneminder-core.install b/distros/ubuntu1410/zoneminder-core.install deleted file mode 100644 index afd9ada95..000000000 --- a/distros/ubuntu1410/zoneminder-core.install +++ /dev/null @@ -1,4 +0,0 @@ -etc/zm -usr/bin -usr/share/polkit-1/actions -usr/share/polkit-1/rules.d diff --git a/distros/ubuntu1410/zoneminder-core.links b/distros/ubuntu1410/zoneminder-core.links deleted file mode 100644 index 5560a100a..000000000 --- a/distros/ubuntu1410/zoneminder-core.links +++ /dev/null @@ -1,3 +0,0 @@ -var/cache/zoneminder/events usr/share/zoneminder/events -var/cache/zoneminder/images usr/share/zoneminder/images -var/cache/zoneminder/temp usr/share/zoneminder/temp diff --git a/distros/ubuntu1410/zoneminder-core.postinst b/distros/ubuntu1410/zoneminder-core.postinst deleted file mode 100644 index da2b444fe..000000000 --- a/distros/ubuntu1410/zoneminder-core.postinst +++ /dev/null @@ -1,80 +0,0 @@ -#! /bin/sh -# postinst maintainer script for zoneminder-core package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -# Source the config file -CONFIGFILE=/etc/zm/zm.conf -. $CONFIGFILE - -# Do this when the package is installed, upgraded or reconfigured -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Retrieve data from dbconfig (inputs from user) - . /etc/dbconfig-common/zoneminder.conf - - # ZoneMinder config file handling - # Inspired by: http://manpages.debian.org/cgi-bin/man.cgi?query=debconf-devel&sektion=7 - - # Backup the config file - cp -a -f $CONFIGFILE ${CONFIGFILE}.postinst.bak - - # Redeclare variables if missing in config file - test -z $dbc_dbserver || grep -Eq "^ *ZM_DB_HOST=" $CONFIGFILE \ - || echo "ZM_DB_HOST=" >> ${CONFIGFILE}.postinst.bak - test -z $dbc_dbname || grep -Eq "^ *ZM_DB_NAME=" $CONFIGFILE \ - || echo "ZM_DB_NAME=" >> ${CONFIGFILE}.postinst.bak - test -z $dbc_dbuser || grep -Eq "^ *ZM_DB_USER=" $CONFIGFILE \ - || echo "ZM_DB_USER=" >> ${CONFIGFILE}.postinst.bak - test -z $dbc_dbpass || grep -Eq "^ *ZM_DB_PASS=" $CONFIGFILE \ - || echo "ZM_DB_PASS=" >> ${CONFIGFILE}.postinst.bak - - # Prevent ZM_DB_HOST to be empty if user selected the 'unix socket' method - if test -z $dbc_dbserver; then - dbc_dbserver_override="localhost" - else - dbc_dbserver_override=$dbc_dbserver - fi - - # Update variables in config file - sed -i "s/^ *ZM_DB_HOST=.*/ZM_DB_HOST=$dbc_dbserver_override/" \ - ${CONFIGFILE}.postinst.bak - test -z $dbc_dbname || sed -i "s/^ *ZM_DB_NAME=.*/ZM_DB_NAME=$dbc_dbname/" \ - ${CONFIGFILE}.postinst.bak - test -z $dbc_dbuser || sed -i "s/^ *ZM_DB_USER=.*/ZM_DB_USER=$dbc_dbuser/" \ - ${CONFIGFILE}.postinst.bak - test -z $dbc_dbpass || sed -i "s/^ *ZM_DB_PASS=.*/ZM_DB_PASS=$dbc_dbpass/" \ - ${CONFIGFILE}.postinst.bak - - # Clean-up backup file - mv -f ${CONFIGFILE}.postinst.bak $CONFIGFILE - - - # Set some file permissions - chown $ZM_WEB_USER:$ZM_WEB_GROUP /var/log/zm - if [ -z "$2" ]; then - chown $ZM_WEB_USER:$ZM_WEB_GROUP -R /var/cache/zoneminder - fi - # As requested by the Debian Webapps Policy Manual §3.2.1 - chown root:${ZM_WEB_GROUP} $CONFIGFILE - chmod 640 $CONFIGFILE -fi - -# Do this every time the package is installed or upgraded -# Test for database presence to avoid failure of zmupdate.pl -if [ "$dbc_install" = "true" ] && [ "$1" = "configure" ]; then - - # Ensure zoneminder is stopped - deb-systemd-invoke stop zoneminder.service || exit $? - - # Run the ZoneMinder update tool - zmupdate.pl --nointeractive - -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.postrm b/distros/ubuntu1410/zoneminder-core.postrm deleted file mode 100644 index d75e75e8b..000000000 --- a/distros/ubuntu1410/zoneminder-core.postrm +++ /dev/null @@ -1,37 +0,0 @@ -#! /bin/sh -# postrm maintainer script for zoneminder-core package - -set -e - -# Source the debconf stuff -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -if [ "$1" = "purge" ]; then - - # Ask the user if we have to remove the cache directory even if not empty - if [ -d /var/cache/zoneminder ] \ - && [ ! $(find /var/cache/zoneminder -maxdepth 0 -type d -empty 2>/dev/null) ]; then - RET="" - db_input high zoneminder/ask_delete || true - db_go || true - db_get zoneminder/ask_delete - if [ "$RET" = "true" ]; then - RET="" - db_input high zoneminder/ask_delete_again || true - db_go || true - db_get zoneminder/ask_delete_again - if [ "$RET" = "true" ]; then - rm -rf /var/cache/zoneminder - fi - fi - fi -fi - -#DEBHELPER# - -# postrm rm may freeze without that -db_stop - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.preinst b/distros/ubuntu1410/zoneminder-core.preinst deleted file mode 100644 index 3ed1ef661..000000000 --- a/distros/ubuntu1410/zoneminder-core.preinst +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# preinst maintainer script for zoneminder-core package - -set -e - -abort=false -if [ -L /usr/share/zoneminder/events ]; then - l=$(readlink /usr/share/zoneminder/events) - if [ "$l" != "/var/cache/zoneminder/events" ]; then - abort=true - fi -fi -if [ -L /usr/share/zoneminder/images ]; then - l=$(readlink /usr/share/zoneminder/images ) - if [ "$l" != "/var/cache/zoneminder/images" ]; then - abort=true - fi -fi - -if [ "$abort" = "true" ]; then - cat >&2 << EOF -Aborting installation of zoneminder due to non-default symlinks in -/usr/share/zoneminder for the images and/or events directory, which could -result in loss of data. Please move your data in each of these directories to -/var/cache/zoneminder before installing zoneminder from the package. -EOF - exit 1 - -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-core.templates b/distros/ubuntu1410/zoneminder-core.templates deleted file mode 100644 index 35fdefd7a..000000000 --- a/distros/ubuntu1410/zoneminder-core.templates +++ /dev/null @@ -1,19 +0,0 @@ -Template: zoneminder/ask_delete -Type: boolean -Default: false -_Description: Delete this non empty directory? - A purge of the ZoneMinder package is performed but the directory - '/var/cache/zoneminder' is not empty so it will not be deleted. - . - Please consider that this directory is designed to contain data resulting from - event detection. Therefore, "proof of evidence" could be lost!" - . - If you are not sure of your decision, please do not delete this directory but - perform a manual checkup. - -Template: zoneminder/ask_delete_again -Type: boolean -Default: false -_Description: Deletion confirmed? - You have allowed the deletion of directory '/var/cache/zoneminder' although - it may contain critical data. diff --git a/distros/ubuntu1410/zoneminder-core.zoneminder.init b/distros/ubuntu1410/zoneminder-core.zoneminder.init deleted file mode 100644 index d3354c1d8..000000000 --- a/distros/ubuntu1410/zoneminder-core.zoneminder.init +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: zoneminder -# Required-Start: $network $remote_fs $syslog -# Required-Stop: $network $remote_fs $syslog -# Should-Start: mysql -# Should-Stop: mysql -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Control ZoneMinder as a Service -### END INIT INFO -# description: Control ZoneMinder as a Service -# chkconfig: 2345 20 20 - -# Source function library. -#. /etc/rc.d/init.d/functions - -prog=ZoneMinder -ZM_PATH_BIN="/usr/bin" -RUNDIR=/var/run/zm -TMPDIR=/tmp/zm -command="$ZM_PATH_BIN/zmpkg.pl" - -start() { - echo -n "Starting $prog: " - mkdir -p $RUNDIR && chown www-data:www-data $RUNDIR - mkdir -p $TMPDIR && chown www-data:www-data $TMPDIR - $command start - RETVAL=$? - [ $RETVAL = 0 ] && echo success - [ $RETVAL != 0 ] && echo failure - echo - [ $RETVAL = 0 ] && touch /var/lock/zm - return $RETVAL -} -stop() { - echo -n "Stopping $prog: " - # - # Why is this status check being done? - # as $command stop returns 1 if zoneminder - # is stopped, which will result in - # this returning 1, which will stuff - # dpkg when it tries to stop zoneminder before - # uninstalling . . . - # - result=`$command status` - if [ ! "$result" = "running" ]; then - echo "Zoneminder already stopped" - echo - RETVAL=0 - else - $command stop - RETVAL=$? - [ $RETVAL = 0 ] && echo success - [ $RETVAL != 0 ] && echo failure - echo - [ $RETVAL = 0 ] && rm -f /var/lock/zm - fi -} -status() { - result=`$command status` - if [ "$result" = "running" ]; then - echo "ZoneMinder is running" - RETVAL=0 - else - echo "ZoneMinder is stopped" - RETVAL=1 - fi -} - -case "$1" in -'start') - start - ;; -'stop') - stop - ;; -'restart' | 'force-reload') - stop - start - ;; -'status') - status - ;; -*) - echo "Usage: $0 { start | stop | restart | status }" - RETVAL=1 - ;; -esac -exit $RETVAL diff --git a/distros/ubuntu1410/zoneminder-core.zoneminder.service b/distros/ubuntu1410/zoneminder-core.zoneminder.service deleted file mode 100644 index d82270024..000000000 --- a/distros/ubuntu1410/zoneminder-core.zoneminder.service +++ /dev/null @@ -1,19 +0,0 @@ -# ZoneMinder systemd unit file -# This file is intended to work with debian distributions - -[Unit] -Description=ZoneMinder CCTV recording and security system -After=network.target mysql.service apache2.service -Requires=apache2.service -Wants=mysql.service - -[Service] -User=www-data -Type=forking -ExecStart=/usr/bin/zmpkg.pl start -ExecReload=/usr/bin/zmpkg.pl restart -ExecStop=/usr/bin/zmpkg.pl stop -PIDFile=/var/run/zm/zm.pid - -[Install] -WantedBy=multi-user.target diff --git a/distros/ubuntu1410/zoneminder-core.zoneminder.tmpfile b/distros/ubuntu1410/zoneminder-core.zoneminder.tmpfile deleted file mode 100644 index 6ea70bf35..000000000 --- a/distros/ubuntu1410/zoneminder-core.zoneminder.tmpfile +++ /dev/null @@ -1 +0,0 @@ -d /var/run/zm 0755 www-data www-data diff --git a/distros/ubuntu1410/zoneminder-database.config b/distros/ubuntu1410/zoneminder-database.config deleted file mode 100644 index f6a84d36d..000000000 --- a/distros/ubuntu1410/zoneminder-database.config +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/sh -# config maintainer script for zoneminder - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -# Set the first version in which dbconfig-common was introduced in the package -dbc_first_version="1.28.0" - -CONFIGFILE=/etc/zm/zm.conf -if [ -e $CONFIGFILE ]; then - # Source the config file if exists - . $CONFIGFILE -elif [ -e ${CONFIGFILE}.dpkg-new ]; then - # If no config file, source the config file which is going to be installed - # by the core package - . ${CONFIGFILE}.dpkg-new -else - # If no config file is going to be installed, set some default values - ZM_DB_HOST= - ZM_DB_NAME="zm" - ZM_DB_USER="zmuser" -fi - -# Set some variables for the dbconfig-common stuff -dbc_dbserver="$ZM_DB_HOST" -dbc_dbname="$ZM_DB_NAME" -dbc_dbuser="$ZM_DB_USER" - -if [ -f /usr/share/dbconfig-common/dpkg/config ]; then - - # Default use dbconfig-common - dbc_install="true" - - # Currently we only support mysql database - dbc_dbtypes="mysql" - - # Set authentication method to password - dbc_authmethod_user="password" - - # Source the dbconfig-common stuff - . /usr/share/dbconfig-common/dpkg/config -fi - -# Do this when the package is installed, upgraded or reconfigured -# Most of answers are cached so the questions will not be asked again -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Ask the user if the database shall be installed locally or remotely - db_input high zoneminder/debconf_dblocation || true - db_go || true - db_get zoneminder/debconf_dblocation - - if [ "$RET" = "local" ]; then - if [ ! -e "/usr/sbin/mysqld" ]; then - # Display a message and exit if the user want a local database but - # no database server is available - db_input high zoneminder/debconf_dblocalmissingerror || true - db_go || true - exit 0 - else - # Set the database server to localhost - dbc_dbserver="localhost" - fi - else - # Source the dbconfig main configuration file - if [ -f /etc/dbconfig-common/config ]; then - . /etc/dbconfig-common/config - fi - if [ "$dbc_remote_questions_default" = "false" ]; then - # Display a message and exit if the dbconfig configuration does not - # allow installation of remote databases - # Note: To overcome this issue, we could think to override the - # default setting by using dbc_remote_questions_default='true' in - # maintainer scripts but unfortunately this does not work due to - # current dbconfig design - # More information here: - # https://bugs.launchpad.net/ubuntu/+source/dbconfig-common/+bug/1065331 - db_input high zoneminder/debconf_dbconfigerror || true - db_go || true - exit 0 - fi - fi - - # Ask the user for all database settings - dbc_go zoneminder $@ - - # Ask the user for the password of the database administrator if the user - # has not yet answered to this question. - # This situation may occur if the user skipped the database creation step - # when reconfiguring the package. - RET="" - db_get zoneminder/mysql/admin-pass - if [ -z "$RET" ]; then - db_input high zoneminder/mysql/admin-pass || true - db_go || true - fi - - # Do this only when not upgrading the package (no old version in argument) - if [ -z "$2" ]; then - # Ask for the password of 'admin' user - while :; do - RET="" - db_input high zoneminder/admin_password || true - db_go || true - db_get zoneminder/admin_password - # If password isn't empty we ask for password verification - if [ -z "$RET" ]; then - db_fset zoneminder/admin_password seen false - db_fset zoneminder/admin_password_again seen false - break - fi - ROOT_PW="$RET" - db_input high zoneminder/admin_password_again || true - db_go || true - db_get zoneminder/admin_password_again - if [ "$RET" = "$ROOT_PW" ]; then - ROOT_PW="" - break - fi - db_fset zoneminder/password_mismatch seen false - db_input critical zoneminder/password_mismatch || true - db_set zoneminder/admin_password "" - db_set zoneminder/admin_password_again "" - db_go || true - done - else - # If we are upgrading the package, set an empty password to disable - # password update in ZoneMinder database - db_set zoneminder/admin_password "" - fi - # Set the seen flag to not ask this question again if no password is - # provided - db_fset zoneminder/admin_password seen true - -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.dirs b/distros/ubuntu1410/zoneminder-database.dirs deleted file mode 100644 index b37463a9e..000000000 --- a/distros/ubuntu1410/zoneminder-database.dirs +++ /dev/null @@ -1,3 +0,0 @@ -usr/share/zoneminder/db -usr/share/dbconfig-common/data/zoneminder/install -usr/share/dbconfig-common/data/zoneminder/upgrade/mysql diff --git a/distros/ubuntu1410/zoneminder-database.install b/distros/ubuntu1410/zoneminder-database.install deleted file mode 100644 index 756c5bbfa..000000000 --- a/distros/ubuntu1410/zoneminder-database.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/db diff --git a/distros/ubuntu1410/zoneminder-database.postinst b/distros/ubuntu1410/zoneminder-database.postinst deleted file mode 100644 index 41d4e5b5b..000000000 --- a/distros/ubuntu1410/zoneminder-database.postinst +++ /dev/null @@ -1,79 +0,0 @@ -#! /bin/sh -# postinst maintainer script for zoneminder-db package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -mysql_update() { - - # Source the dbconfig stuff - . /usr/share/dbconfig-common/internal/mysql - - # Update the password of the hard-coded default 'admin' account - test -z $ADMIN_PASSWORD || dbc_mysql_exec_command "UPDATE Users SET Password = password('$ADMIN_PASSWORD') WHERE Username = 'admin';" || true - - # Update the database version - dbc_mysql_exec_command "UPDATE Config SET Value = '$DB_VERSION' WHERE Name = 'ZM_DYN_DB_VERSION';" || true -} - -if [ -f /usr/share/dbconfig-common/dpkg/postinst ]; then - - # Set the first version in which dbconfig-common was introduced in the package - dbc_first_version="1.28.0" - - # Set the database type - dbc_dbtypes="mysql" - - # Source the dbconfig-common stuff - . /usr/share/dbconfig-common/dpkg/postinst -fi - -# Do this when the package is installed, upgraded or reconfigured -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Install sql database create file for dbconfig - # (needed at first package installation) - if [ ! -f /usr/share/dbconfig-common/data/zoneminder/install/mysql ]; then - install -m 644 /usr/share/zoneminder/db/zm_create.sql \ - /usr/share/dbconfig-common/data/zoneminder/install/mysql - # Remove unneeded sql requests - # dbconfig will create the underlying database - sed -i "/^ *CREATE DATABASE /d" \ - /usr/share/dbconfig-common/data/zoneminder/install/mysql - sed -i "/^ *USE /d" \ - /usr/share/dbconfig-common/data/zoneminder/install/mysql - fi - - # Symlink sql update files for dbconfig (needed when upgrading the package) - for sqlfile in /usr/share/zoneminder/db/zm_update-*.sql; do - lnk=`echo $sqlfile | sed "s/^\/usr\/share\/zoneminder\/db\/zm_update-\(.*\)\.sql/\1/"` - if [ ! -L /usr/share/dbconfig-common/data/zoneminder/upgrade/mysql/$lnk ]; then - ln -sf $sqlfile \ - /usr/share/dbconfig-common/data/zoneminder/upgrade/mysql/$lnk - fi - done || true - - # Create the underlying database and populate it - # dbconfig will take care of applying any updates which are newer than the - # previously installed version - dbc_go zoneminder $@ - - # Get the password of ZoneMinder user 'admin' from debconf - db_get zoneminder/admin_password - ADMIN_PASSWORD=$RET - - # Remove the password from debconf database - test -z $ADMIN_PASSWORD || db_reset zoneminder/admin_password || true - - # Get the lastest database version from dbconfig upgrade folder - DB_VERSION=$(ls -rv /usr/share/dbconfig-common/data/zoneminder/upgrade/$dbc_dbtypes | head -1) - - # Update the default admin account and database version - mysql_update -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.postrm b/distros/ubuntu1410/zoneminder-database.postrm deleted file mode 100644 index 231f01ad7..000000000 --- a/distros/ubuntu1410/zoneminder-database.postrm +++ /dev/null @@ -1,34 +0,0 @@ -#! /bin/sh -# postrm maintainer script for zoneminder-db package - -set -e - -# Source the debconf stuff -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -# Source the dbconfig stuff -if [ -f /usr/share/dbconfig-common/dpkg/postrm ]; then - . /usr/share/dbconfig-common/dpkg/postrm - # Ask the user what do to with dbconfig when removing the package - dbc_go zoneminder $@ -fi - -if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then - # Remove dbconfig stuff added in postinst script - rm -rf /usr/share/dbconfig-common/data/zoneminder - # No need to manually remove the zm database, dbconfig take care of this -fi - -if [ "$1" = "purge" ]; then - # Delete a potential remaining file used in postinst script - rm -f /etc/zm/zm.conf.postinst.bak -fi - -#DEBHELPER# - -# postrm rm may freeze without that -db_stop - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.prerm b/distros/ubuntu1410/zoneminder-database.prerm deleted file mode 100644 index 31786116a..000000000 --- a/distros/ubuntu1410/zoneminder-database.prerm +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# prerm script for zoneminder-db package - -set -e - -# Source the debconf stuff if file exists -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -# If dbconfig-common is installed and has been used by zoneminder -if [ -f /usr/share/dbconfig-common/dpkg/prerm ] \ - && [ -f /etc/dbconfig-common/zoneminder.conf ]; then - # Source the dbconfig stuff - . /usr/share/dbconfig-common/dpkg/prerm - # Ask the user what do to with dbconfig before removing the package - dbc_go zoneminder $@ -fi - -# #DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-database.templates b/distros/ubuntu1410/zoneminder-database.templates deleted file mode 100644 index 4de4342f6..000000000 --- a/distros/ubuntu1410/zoneminder-database.templates +++ /dev/null @@ -1,58 +0,0 @@ -Template: zoneminder/debconf_dblocation -Type: select -__Choices: local, remote -Default: local -_Description: Database location: - A database server is required to run ZoneMinder. The database can be installed - either locally or remotely on a machine of your network. - . - If you choose a remote location, you will have to select the 'tcp/ip' - connection method and enter the hostname or ip address of the remote machine - in the next configuration screens. - -Template: zoneminder/debconf_dblocalmissingerror -Type: error -_Description: No local database server is available: - Currently ZoneMinder supports mysql or mariadb database server but none of them - appears to be installed on this machine. - . - In order to complete ZoneMinder's installation, after ending of this assistant, - please install a compatible database server and then restart the assistant by - invoking: - . - $ sudo dpkg-reconfigure zoneminder - -Template: zoneminder/debconf_dbconfigerror -Type: error -_Description: Remote database servers are not allowed: - The current configuration of dbconfig-common does not allow installation of - a database on remote servers. - . - In order to reconfigure dbconfig-common, please invoke the following command - after ending of this assistant: - . - $ sudo dpkg-reconfigure dbconfig-common - . - Then, to complete ZoneMinder's installation, please restart this assistant by - invoking: - . - $ sudo dpkg-reconfigure zoneminder - -Template: zoneminder/admin_password -Type: password -_Description: New password for the ZoneMinder 'admin' user: - Please enter the password of the default administrative user. - . - While not mandatory, it is highly recommended that you set a custom password - for the administrative 'admin' user. - . - If this field is left blank, the password will not be changed. - -Template: zoneminder/admin_password_again -Type: password -_Description: Repeat password for the ZoneMinder 'admin' user: - -Template: zoneminder/password_mismatch -Type: error -_Description: Password input error - The two passwords you entered were not the same. Please try again. diff --git a/distros/ubuntu1410/zoneminder-ui-base.config b/distros/ubuntu1410/zoneminder-ui-base.config deleted file mode 100644 index 2660208a8..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.config +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# config maintainer script for zoneminder-ui-base package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -# Do this when the package is installed, upgraded or reconfigured -# Most of answers are cached so the questions will not be asked again -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Ask the user for the web server(s) to configure - db_input high zoneminder/webserver || true - db_go || true -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-ui-base.install b/distros/ubuntu1410/zoneminder-ui-base.install deleted file mode 100644 index f72b569be..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.install +++ /dev/null @@ -1,11 +0,0 @@ -debian/apache.conf etc/zm -usr/lib/cgi-bin -usr/share/zoneminder/ajax -usr/share/zoneminder/css -usr/share/zoneminder/graphics -usr/share/zoneminder/includes -usr/share/zoneminder/index.php -usr/share/zoneminder/js -usr/share/zoneminder/lang -usr/share/zoneminder/tools -usr/share/zoneminder/views diff --git a/distros/ubuntu1410/zoneminder-ui-base.links b/distros/ubuntu1410/zoneminder-ui-base.links deleted file mode 100644 index b00a147d6..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.links +++ /dev/null @@ -1 +0,0 @@ -usr/lib/cgi-bin usr/share/zoneminder/cgi-bin diff --git a/distros/ubuntu1410/zoneminder-ui-base.postinst b/distros/ubuntu1410/zoneminder-ui-base.postinst deleted file mode 100644 index a5bce3c98..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.postinst +++ /dev/null @@ -1,48 +0,0 @@ -#! /bin/sh -# postinst maintainer script for zoneminder-ui-base package - -set -e - -# Source the debconf stuff -. /usr/share/debconf/confmodule - -apache_install() { - - mkdir -p /etc/apache2/conf-available - ln -sf ../../zm/apache.conf /etc/apache2/conf-available/zoneminder.conf - - COMMON_STATE=$(dpkg-query -f '${Status}' -W 'apache2.2-common' 2>/dev/null | awk '{print $3}' || true) - - if [ -e /usr/share/apache2/apache2-maintscript-helper ] ; then - . /usr/share/apache2/apache2-maintscript-helper - apache2_invoke enconf zoneminder - elif [ "$COMMON_STATE" = "installed" ] || [ "$COMMON_STATE" = "unpacked" ] ; then - [ -d /etc/apache2/conf.d/ ] && [ ! -L /etc/apache2/conf.d/zoneminder.conf ] && ln -s ../conf-available/zoneminder.conf /etc/apache2/conf.d/zoneminder.conf - fi - - # Enable CGI script module in apache (not enabled by default on jessie) - a2enmod cgi >/dev/null 2>&1 - - # Reload the web server - deb-systemd-invoke reload apache2.service || true -} - -# Do this when the package is installed, upgraded or reconfigured -if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then - - # Configure the web server - db_get zoneminder/webserver - webservers="$RET" - - for webserver in $webservers; do - webserver=${webserver%,} - # Currently we only support apache2 - if [ "$webserver" = "apache2" ] ; then - apache_install $1 - fi - done -fi - -#DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-ui-base.postrm b/distros/ubuntu1410/zoneminder-ui-base.postrm deleted file mode 100644 index 441bb5218..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.postrm +++ /dev/null @@ -1,41 +0,0 @@ -#! /bin/sh -# postrm maintainer script for zoneminder-ui-base package - -set -e - -# Source the debconf stuff -if [ -f /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule -fi - -apache_remove() { - COMMON_STATE=$(dpkg-query -f '${Status}' -W 'apache2.2-common' 2>/dev/null | awk '{print $3}' || true) - if [ -e /usr/share/apache2/apache2-maintscript-helper ] ; then - . /usr/share/apache2/apache2-maintscript-helper - apache2_invoke disconf zoneminder - elif [ "$COMMON_STATE" = "installed" ] || [ "$COMMON_STATE" = "unpacked" ] ; then - rm -f /etc/apache2/conf.d/zoneminder.conf - fi - rm -f /etc/apache2/conf-available/zoneminder.conf - # Reload the web server - deb-systemd-invoke reload apache2.service || true -} - -if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then - # Deconfigure the web server - db_get zoneminder/webserver - for webserver in $RET; do - webserver=${webserver%,} - # Currently we only support apache2 - if [ "$webserver" = "apache2" ] ; then - apache_remove $1 - fi - done -fi - -#DEBHELPER# - -# postrm rm may freeze without that -db_stop - -exit 0 diff --git a/distros/ubuntu1410/zoneminder-ui-base.templates b/distros/ubuntu1410/zoneminder-ui-base.templates deleted file mode 100644 index 31e70277f..000000000 --- a/distros/ubuntu1410/zoneminder-ui-base.templates +++ /dev/null @@ -1,7 +0,0 @@ -Template: zoneminder/webserver -Type: multiselect -Choices: apache2 -Default: apache2 -_Description: Web server to reconfigure automatically: - Please choose the web server that should be automatically configured for - ZoneMinder's web portal access. diff --git a/distros/ubuntu1410/zoneminder-ui-classic.install b/distros/ubuntu1410/zoneminder-ui-classic.install deleted file mode 100644 index 9532d9dc9..000000000 --- a/distros/ubuntu1410/zoneminder-ui-classic.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/skins/classic diff --git a/distros/ubuntu1410/zoneminder-ui-mobile.install b/distros/ubuntu1410/zoneminder-ui-mobile.install deleted file mode 100644 index 464bb74eb..000000000 --- a/distros/ubuntu1410/zoneminder-ui-mobile.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/skins/mobile diff --git a/distros/ubuntu1410/zoneminder-ui-xml.install b/distros/ubuntu1410/zoneminder-ui-xml.install deleted file mode 100644 index 6617707f8..000000000 --- a/distros/ubuntu1410/zoneminder-ui-xml.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/zoneminder/skins/xml diff --git a/distros/ubuntu1604/control b/distros/ubuntu1604/control index 617b3e852..00a8bd9ac 100644 --- a/distros/ubuntu1604/control +++ b/distros/ubuntu1604/control @@ -33,6 +33,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa ,libssl-dev ,libcrypt-eksblowfish-perl ,libdata-entropy-perl + ,libvncserver-dev # Unbundled (dh_linktree): ,libjs-jquery ,libjs-mootools @@ -82,6 +83,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libssl | libssl1.0.0 | libssl1.1 ,libcrypt-eksblowfish-perl ,libdata-entropy-perl + ,libvncclient1 Recommends: ${misc:Recommends} ,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm ,mysql-server | mariadb-server | virtual-mysql-server diff --git a/distros/ubuntu2004/NEWS b/distros/ubuntu2004/NEWS new file mode 100644 index 000000000..6200726cf --- /dev/null +++ b/distros/ubuntu2004/NEWS @@ -0,0 +1,10 @@ +zoneminder (1.28.1-1) unstable; urgency=low + + This version is no longer automatically initialize or upgrade database. + See README.Debian for details. + + Changed installation paths (please correct your web server configuration): + /usr/share/zoneminder --> /usr/share/zoneminder/www + /usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin + + -- Dmitry Smirnov Tue, 31 Mar 2015 15:12:17 +1100 diff --git a/distros/ubuntu1204/README.Debian b/distros/ubuntu2004/README.Debian similarity index 64% rename from distros/ubuntu1204/README.Debian rename to distros/ubuntu2004/README.Debian index 2ba809fe4..4fe3464d2 100644 --- a/distros/ubuntu1204/README.Debian +++ b/distros/ubuntu2004/README.Debian @@ -8,7 +8,7 @@ Initializing database OR cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf - echo 'grant lock tables,alter,create,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + echo 'grant lock tables,alter,create,index,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\ | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql Hint: generate secure password with `pwgen` and update "/etc/zm/zm.conf" @@ -23,8 +23,7 @@ configuration file: Upgrading database ------------------ -Prior to 1.28.1 database upgrade was performed automatically. -"zoneminder" service will refuse to start with outdated database. +The database is updated automatically on installation. You should not need to take this step. Assuming that database is on "localhost" then the following command can be used to upgrade "zm" database: @@ -45,17 +44,11 @@ The following command prints the current version of zoneminder database: Enabling service ---------------- -By default Zoneminder service is not starting automatically and need to be -manually activated once database is configured: - -On systemd: +By default Zoneminder service is not automatically started and needs to be +manually enabled once database is configured: sudo systemctl enable zoneminder.service -On SysV: - - sudo update-rc.d zoneminder enable - Web server set-up ----------------- @@ -82,10 +75,10 @@ Common configuration steps for Apache2: ## nginx / fcgiwrap -Nginx needs "php5-fpm" package to support PHP and "fcgiwrap" package +Nginx needs "php-fpm" package to support PHP and "fcgiwrap" package for binary "cgi-bin" applications: - sudo apt-get install php5-fpm fcgiwrap + sudo apt-get install php-fpm fcgiwrap To enable a URL alias that makes Zoneminder available from @@ -119,32 +112,9 @@ site configuration. Changing the location for images and events ------------------------------------------- -Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This -package modifies that by changing /usr/share/zoneminder/images and -/usr/share/zoneminder/events to symlinks to directories under -/var/cache/zoneminder. - -There are numerous places these could be put and ways to do it. But, at the -moment, if you change this, an upgrade will fail with a warning about these -locations having changed (the reason for this was that previously, an upgrade -would silently revert the changes and cause event loss - refer -bug #608793). - -If you do want to change the location, here are a couple of suggestions. -(thanks to vagrant@freegeek.org): - -These lines in fstab could allow you to bind-mount an alternate location - - /dev/sdX1 /otherdrive ext3 defaults 0 2 - /otherdrive/zoneminder/images /var/cache/zoneminder/images bind defaults 0 2 - /otherdrive/zoneminder/events /var/cache/zoneminder/events bind defaults 0 2 - - or if you have a separate partition for each: - - /dev/sdX1 /var/cache/zoneminder/images ext3 defaults 0 2 - /dev/sdX2 /var/cache/zoneminder/events ext3 defaults 0 2 - - -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100 +ZoneMinder is now able to be configured to use an alternative location for storing +events and images at compile time. This package makes use of that, so symlinks in +/usr/share/zoneminder/www are no longer necessary. Access to /dev/video* --------------------- diff --git a/distros/ubuntu1204/TODO.Debian b/distros/ubuntu2004/TODO.Debian similarity index 100% rename from distros/ubuntu1204/TODO.Debian rename to distros/ubuntu2004/TODO.Debian diff --git a/distros/ubuntu2004/changelog b/distros/ubuntu2004/changelog new file mode 100644 index 000000000..616f75178 --- /dev/null +++ b/distros/ubuntu2004/changelog @@ -0,0 +1,3 @@ +zoneminder (1.31.39~20180223.27-stretch-1) unstable; urgency=low + * + -- Isaac Connor Fri, 23 Feb 2018 14:15:59 -0500 diff --git a/distros/ubuntu1204/clean b/distros/ubuntu2004/clean similarity index 100% rename from distros/ubuntu1204/clean rename to distros/ubuntu2004/clean diff --git a/distros/ubuntu1204/compat b/distros/ubuntu2004/compat similarity index 100% rename from distros/ubuntu1204/compat rename to distros/ubuntu2004/compat diff --git a/distros/ubuntu1204/conf/apache2/zoneminder.conf b/distros/ubuntu2004/conf/apache2/zoneminder.conf similarity index 95% rename from distros/ubuntu1204/conf/apache2/zoneminder.conf rename to distros/ubuntu2004/conf/apache2/zoneminder.conf index 8e2957cbf..e3164d36c 100644 --- a/distros/ubuntu1204/conf/apache2/zoneminder.conf +++ b/distros/ubuntu2004/conf/apache2/zoneminder.conf @@ -6,7 +6,8 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" Require all granted -# Order matters. This Alias must come first + +# Order matters. This alias must come first. Alias /zm/cache /var/cache/zoneminder/cache Options -Indexes +FollowSymLinks @@ -24,7 +25,6 @@ Alias /zm/cache /var/cache/zoneminder/cache Alias /zm /usr/share/zoneminder/www - php_flag register_globals off Options -Indexes +FollowSymLinks DirectoryIndex index.php diff --git a/distros/ubuntu1204/control b/distros/ubuntu2004/control similarity index 80% rename from distros/ubuntu1204/control rename to distros/ubuntu2004/control index e5688a421..4e7c53503 100644 --- a/distros/ubuntu1204/control +++ b/distros/ubuntu2004/control @@ -3,34 +3,39 @@ Section: net Priority: optional Maintainer: Isaac Connor Uploaders: Isaac Connor -Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh-linktree +Build-Depends: debhelper, dh-systemd, python3-sphinx, dh-linktree, dh-systemd, dh-apache2 ,cmake ,libx264-dev, libmp4v2-dev - ,libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libavdevice-dev - ,libavresample-dev + ,libavdevice-dev + ,libavcodec-dev + ,libavformat-dev + ,libavutil-dev + ,libswresample-dev + ,libswscale-dev + ,ffmpeg + ,net-tools ,libbz2-dev - ,libgcrypt-dev + ,libgcrypt20-dev ,libcurl4-gnutls-dev - ,libgnutls-openssl-dev - ,libjpeg8-dev|libjpeg9-dev|libjpeg62-turbo-dev, - ,libmysqlclient-dev + ,libturbojpeg0-dev + ,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat ,libpcre3-dev ,libpolkit-gobject-1-dev - ,libv4l-dev (>= 0.8.3) [!hurd-any] + ,libv4l-dev [!hurd-any] ,libvlc-dev ,libdate-manip-perl ,libdbd-mysql-perl ,libphp-serialization-perl ,libsys-mmap-perl [!hurd-any] ,libwww-perl - ,libdata-uuid-perl + ,libdata-uuid-perl ,libssl-dev ,libcrypt-eksblowfish-perl ,libdata-entropy-perl # Unbundled (dh_linktree): ,libjs-jquery ,libjs-mootools -Standards-Version: 3.9.4 +Standards-Version: 3.9.8 Homepage: http://www.zoneminder.com/ Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/zoneminder.git Vcs-Git: git://anonscm.debian.org/collab-maint/zoneminder.git @@ -39,16 +44,15 @@ Package: zoneminder Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,javascript-common - ,libmp4v2-2, libx264-142 - ,libav-tools|ffmpeg - ,libdate-manip-perl + ,libmp4v2-2, libx264-155 + ,libswscale5 + ,libswresample4 + ,ffmpeg + ,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl ,libdbd-mysql-perl - ,libmime-lite-perl - ,libmime-tools-perl ,libphp-serialization-perl ,libmodule-load-conditional-perl ,libnet-sftp-foreign-perl -# ,libzoneminder-perl (= ${source:Version}) ,libarchive-zip-perl ,libdbd-mysql-perl ,libdevice-serialport-perl @@ -56,27 +60,31 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libjson-maybexs-perl ,libsys-mmap-perl [!hurd-any] ,liburi-encode-perl - ,libwww-perl + ,libwww-perl, liburi-perl + ,libdata-dump-perl ,libdatetime-perl - ,libdata-uuid-perl + ,libclass-std-fast-perl + ,libsoap-wsdl-perl + ,libio-socket-multicast-perl + ,libdigest-sha-perl + ,libsys-cpu-perl, libsys-meminfo-perl + ,libdata-uuid-perl ,libnumber-bytes-human-perl ,libfile-slurp-perl - ,mysql-client | virtual-mysql-client + ,mysql-client | mariadb-client | virtual-mysql-client ,perl-modules - ,php5-mysql, php5-gd, php5-apcu, php-apc + ,php-mysql, php-gd, php-apcu, php-apc | php-apcu-bc, php-json ,policykit-1 ,rsyslog | system-log-daemon ,zip - ,libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl - ,libio-socket-multicast-perl, libdigest-sha-perl - ,libsys-cpu-perl, libsys-meminfo-perl - ,libssl | libssl1.0.0 + ,libpcre3 ,libcrypt-eksblowfish-perl ,libdata-entropy-perl Recommends: ${misc:Recommends} - ,libapache2-mod-php5 | php5-fpm - ,mysql-server | virtual-mysql-server + ,libapache2-mod-php | php-fpm + ,mysql-server | mariadb-server | virtual-mysql-server ,zoneminder-doc (>= ${source:Version}) + ,ffmpeg Suggests: fcgiwrap, logrotate Description: video camera security and surveillance solution ZoneMinder is intended for use in single or multi-camera video security @@ -122,7 +130,7 @@ Package: zoneminder-doc Section: doc Architecture: all Multi-Arch: foreign -Depends: ${misc:Depends}, ${sphinxdoc:Depends} +Depends: ${misc:Depends}, ${sphinxdoc:Depends}, python-sphinx-rtd-theme | python3-sphinx-rtd-theme Suggests: www-browser Description: ZoneMinder documentation ZoneMinder is intended for use in single or multi-camera video security diff --git a/distros/ubuntu1204/copyright b/distros/ubuntu2004/copyright similarity index 100% rename from distros/ubuntu1204/copyright rename to distros/ubuntu2004/copyright diff --git a/distros/ubuntu1204/examples/nginx.conf b/distros/ubuntu2004/examples/nginx.conf similarity index 100% rename from distros/ubuntu1204/examples/nginx.conf rename to distros/ubuntu2004/examples/nginx.conf diff --git a/distros/ubuntu1204/gbp.conf b/distros/ubuntu2004/gbp.conf similarity index 100% rename from distros/ubuntu1204/gbp.conf rename to distros/ubuntu2004/gbp.conf diff --git a/distros/ubuntu1204/libzoneminder-perl.install b/distros/ubuntu2004/libzoneminder-perl.install similarity index 100% rename from distros/ubuntu1204/libzoneminder-perl.install rename to distros/ubuntu2004/libzoneminder-perl.install diff --git a/distros/ubuntu1204/patches/series b/distros/ubuntu2004/patches/series similarity index 100% rename from distros/ubuntu1204/patches/series rename to distros/ubuntu2004/patches/series diff --git a/distros/ubuntu1204/rules b/distros/ubuntu2004/rules similarity index 71% rename from distros/ubuntu1204/rules rename to distros/ubuntu2004/rules index 657697fcf..c671a1b03 100755 --- a/distros/ubuntu1204/rules +++ b/distros/ubuntu2004/rules @@ -13,26 +13,26 @@ endif %: dh $@ --parallel --buildsystem=cmake --builddirectory=dbuild \ - --with sphinxdoc,apache2,linktree + --with systemd,sphinxdoc,apache2,linktree override_dh_auto_configure: - dh_auto_configure -- $(ARGS) \ - -DCMAKE_VERBOSE_MAKEFILE=ON \ - -DCMAKE_BUILD_TYPE=Release \ - -DZM_CONFIG_DIR="/etc/zm" \ - -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ - -DZM_RUNDIR="/var/run/zm" \ - -DZM_SOCKDIR="/var/run/zm" \ - -DZM_TMPDIR="/tmp/zm" \ - -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ - -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ + dh_auto_configure -- $(ARGS) \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DZM_CONFIG_DIR="/etc/zm" \ + -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ + -DZM_RUNDIR="/var/run/zm" \ + -DZM_SOCKDIR="/var/run/zm" \ + -DZM_TMPDIR="/tmp/zm" \ + -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ + -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ - -DZM_PATH_SHUTDOWN="/sbin/shutdown" \ - -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" + -DZM_PATH_SHUTDOWN="/sbin/shutdown" \ + -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" override_dh_clean: dh_clean $(MANPAGES1) - $(RM) -r docs/_build docs/installationguide + $(RM) -r docs/_build build-indep: #$(MAKE) -C docs text @@ -67,8 +67,11 @@ override_dh_fixperms: chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf -override_dh_installinit: - dh_installinit --no-start +override_dh_systemd_start: + dh_systemd_start --no-start + +override_dh_systemd_enable: + dh_systemd_enable --no-enable override_dh_apache2: dh_apache2 --noenable diff --git a/distros/ubuntu1204/source/format b/distros/ubuntu2004/source/format similarity index 100% rename from distros/ubuntu1204/source/format rename to distros/ubuntu2004/source/format diff --git a/distros/ubuntu1204/source/lintian-overrides b/distros/ubuntu2004/source/lintian-overrides similarity index 100% rename from distros/ubuntu1204/source/lintian-overrides rename to distros/ubuntu2004/source/lintian-overrides diff --git a/distros/ubuntu1204/zoneminder-doc.doc-base b/distros/ubuntu2004/zoneminder-doc.doc-base similarity index 100% rename from distros/ubuntu1204/zoneminder-doc.doc-base rename to distros/ubuntu2004/zoneminder-doc.doc-base diff --git a/distros/ubuntu1204/zoneminder-doc.install b/distros/ubuntu2004/zoneminder-doc.install similarity index 100% rename from distros/ubuntu1204/zoneminder-doc.install rename to distros/ubuntu2004/zoneminder-doc.install diff --git a/distros/ubuntu1204/zoneminder-doc.links b/distros/ubuntu2004/zoneminder-doc.links similarity index 100% rename from distros/ubuntu1204/zoneminder-doc.links rename to distros/ubuntu2004/zoneminder-doc.links diff --git a/distros/ubuntu1204/zoneminder.apache2 b/distros/ubuntu2004/zoneminder.apache2 similarity index 100% rename from distros/ubuntu1204/zoneminder.apache2 rename to distros/ubuntu2004/zoneminder.apache2 diff --git a/distros/ubuntu1204/zoneminder.bug-presubj b/distros/ubuntu2004/zoneminder.bug-presubj similarity index 100% rename from distros/ubuntu1204/zoneminder.bug-presubj rename to distros/ubuntu2004/zoneminder.bug-presubj diff --git a/distros/ubuntu1204/zoneminder.dirs b/distros/ubuntu2004/zoneminder.dirs similarity index 95% rename from distros/ubuntu1204/zoneminder.dirs rename to distros/ubuntu2004/zoneminder.dirs index 79b2c66af..6db3d5a95 100644 --- a/distros/ubuntu1204/zoneminder.dirs +++ b/distros/ubuntu2004/zoneminder.dirs @@ -5,5 +5,5 @@ var/cache/zoneminder/images var/cache/zoneminder/temp var/cache/zoneminder/cache usr/share/zoneminder/db -etc/zm +etc/zm/ etc/zm/conf.d diff --git a/distros/ubuntu1204/zoneminder.docs b/distros/ubuntu2004/zoneminder.docs similarity index 100% rename from distros/ubuntu1204/zoneminder.docs rename to distros/ubuntu2004/zoneminder.docs diff --git a/distros/ubuntu1204/zoneminder.examples b/distros/ubuntu2004/zoneminder.examples similarity index 100% rename from distros/ubuntu1204/zoneminder.examples rename to distros/ubuntu2004/zoneminder.examples diff --git a/distros/ubuntu1204/zoneminder.install b/distros/ubuntu2004/zoneminder.install similarity index 100% rename from distros/ubuntu1204/zoneminder.install rename to distros/ubuntu2004/zoneminder.install diff --git a/distros/ubuntu2004/zoneminder.links b/distros/ubuntu2004/zoneminder.links new file mode 100644 index 000000000..b7258c3c4 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.links @@ -0,0 +1 @@ +/var/tmp /usr/share/zoneminder/www/api/app/tmp diff --git a/distros/ubuntu1204/zoneminder.linktrees b/distros/ubuntu2004/zoneminder.linktrees similarity index 100% rename from distros/ubuntu1204/zoneminder.linktrees rename to distros/ubuntu2004/zoneminder.linktrees diff --git a/distros/ubuntu1204/zoneminder.lintian-overrides b/distros/ubuntu2004/zoneminder.lintian-overrides similarity index 100% rename from distros/ubuntu1204/zoneminder.lintian-overrides rename to distros/ubuntu2004/zoneminder.lintian-overrides diff --git a/distros/ubuntu1204/zoneminder.logrotate b/distros/ubuntu2004/zoneminder.logrotate similarity index 90% rename from distros/ubuntu1204/zoneminder.logrotate rename to distros/ubuntu2004/zoneminder.logrotate index 3195d0fb2..6162e9c4d 100644 --- a/distros/ubuntu1204/zoneminder.logrotate +++ b/distros/ubuntu2004/zoneminder.logrotate @@ -1,4 +1,4 @@ -/var/log/zm/*log { +/var/log/zm/*.log { missingok notifempty sharedscripts diff --git a/distros/ubuntu1204/zoneminder.maintscript b/distros/ubuntu2004/zoneminder.maintscript similarity index 100% rename from distros/ubuntu1204/zoneminder.maintscript rename to distros/ubuntu2004/zoneminder.maintscript diff --git a/distros/ubuntu1204/zoneminder.manpages b/distros/ubuntu2004/zoneminder.manpages similarity index 100% rename from distros/ubuntu1204/zoneminder.manpages rename to distros/ubuntu2004/zoneminder.manpages diff --git a/distros/ubuntu2004/zoneminder.postinst b/distros/ubuntu2004/zoneminder.postinst new file mode 100644 index 000000000..d3983950b --- /dev/null +++ b/distros/ubuntu2004/zoneminder.postinst @@ -0,0 +1,90 @@ +#! /bin/sh + +set +e + +if [ "$1" = "configure" ]; then + + . /etc/zm/zm.conf + for CONFFILE in /etc/zm/conf.d/*.conf; do + . "$CONFFILE" + done + + # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group + chown www-data:root /var/log/zm + chown www-data:www-data /var/lib/zm + if [ -z "$2" ]; then + chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* + fi + if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ] && [ "$(command -v a2enmod)" != "" ]; then + echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi." + a2enmod cgi + fi + + if [ "$ZM_DB_HOST" = "localhost" ]; then + + if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ] || [ -e "/etc/init.d/mysql" ]; then + # Ensure zoneminder is stopped + deb-systemd-invoke stop zoneminder.service || exit $? + + # + # Get mysql started if it isn't running + # + + if [ -e "/lib/systemd/system/mariadb.service" ]; then + DBSERVICE="mariadb.service" + else + DBSERVICE="mysql.service" + fi + echo "Detected db service is $DBSERVICE" + if systemctl is-failed --quiet $DBSERVICE; then + echo "$DBSERVICE is in a failed state; it will not be started." + echo "If you have already resolved the problem preventing $DBSERVICE from running," + echo "run sudo systemctl restart $DBSERVICE then run sudo dpkg-reconfigure zoneminder." + exit 1 + fi + + if ! systemctl is-active --quiet mysql.service mariadb.service; then + # Due to /etc/init.d service autogeneration, mysql.service always returns the status of mariadb.service + # However, mariadb.service will not return the status of mysql.service. + deb-systemd-invoke start $DBSERVICE + fi + + # Make sure systemctl status exit code is 0; i.e. the DB is running + if systemctl is-active --quiet "$DBSERVICE"; then + mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload + # test if database if already present... + if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then + echo "Creating zm db" + cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf + if [ $? -ne 0 ]; then + echo "Error creating db." + exit 1; + fi + # This creates the user. + echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + else + echo "Updating permissions" + echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + fi + + zmupdate.pl --nointeractive + zmupdate.pl --nointeractive -f + + # Add any new PTZ control configurations to the database (will not overwrite) + zmcamtool.pl --import >/dev/null 2>&1 + echo "Done Updating; starting ZoneMinder." + else + echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.' + fi + else + echo 'MySQL/MariaDB not found; assuming remote server.' + fi + + else + echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)." + fi + deb-systemd-invoke restart zoneminder.service + +fi + +#DEBHELPER# diff --git a/distros/ubuntu1204/zoneminder.postrm b/distros/ubuntu2004/zoneminder.postrm similarity index 100% rename from distros/ubuntu1204/zoneminder.postrm rename to distros/ubuntu2004/zoneminder.postrm diff --git a/distros/ubuntu2004/zoneminder.preinst b/distros/ubuntu2004/zoneminder.preinst new file mode 100644 index 000000000..6088c3ea9 --- /dev/null +++ b/distros/ubuntu2004/zoneminder.preinst @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +## Remove obsolete symlink which is in the way of dh_apache2: +ol="/etc/apache2/conf-available/zoneminder.conf" +if [ -h "${ol}" ]; then + [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" +fi + +#DEBHELPER# diff --git a/distros/ubuntu1204/zoneminder.service b/distros/ubuntu2004/zoneminder.service similarity index 95% rename from distros/ubuntu1204/zoneminder.service rename to distros/ubuntu2004/zoneminder.service index ac719b733..cb2d6791e 100644 --- a/distros/ubuntu1204/zoneminder.service +++ b/distros/ubuntu2004/zoneminder.service @@ -13,7 +13,7 @@ Type=forking ExecStart=/usr/bin/zmpkg.pl start ExecReload=/usr/bin/zmpkg.pl restart ExecStop=/usr/bin/zmpkg.pl stop -PIDFile=/var/run/zm/zm.pid +PIDFile=/run/zm/zm.pid Restart=always RestartSec=10 Environment=TZ=:/etc/localtime diff --git a/distros/ubuntu2004/zoneminder.tmpfile b/distros/ubuntu2004/zoneminder.tmpfile new file mode 100644 index 000000000..cbfdec1de --- /dev/null +++ b/distros/ubuntu2004/zoneminder.tmpfile @@ -0,0 +1,4 @@ +d /run/zm 0755 www-data www-data +d /tmp/zm 0755 www-data www-data +d /var/tmp/zm 0755 www-data www-data +d /var/cache/zoneminder/cache 0755 www-data www-data diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst index 491ee1eb8..7b714e51a 100644 --- a/docs/installationguide/ubuntu.rst +++ b/docs/installationguide/ubuntu.rst @@ -42,7 +42,7 @@ guide you with a quick search. :: - add-apt-repository ppa:iconnor/zoneminder-1.32 + add-apt-repository ppa:iconnor/zoneminder-1.34 Update repo and upgrade. diff --git a/docs/userguide/options/options_system.rst b/docs/userguide/options/options_system.rst index bd871a1e8..33c59cf1c 100644 --- a/docs/userguide/options/options_system.rst +++ b/docs/userguide/options/options_system.rst @@ -14,13 +14,13 @@ LANG_DEFAULT - ZoneMinder allows the web interface to use languages other than E OPT_USE_AUTH - ZoneMinder can run in two modes. The simplest is an entirely unauthenticated mode where anyone can access ZoneMinder and perform all tasks. This is most suitable for installations where the web server access is limited in other ways. The other mode enables user accounts with varying sets of permissions. Users must login or authenticate to access ZoneMinder and are limited by their defined permissions. Authenticated mode alone should not be relied up for securing Internet connected ZoneMinder. -AUTH_TYPE - ZoneMinder can use two methods to authenticate users when running in authenticated mode. The first is a builtin method where ZoneMinder provides facilities for users to log in and maintains track of their identity. The second method allows interworking with other methods such as http basic authentication which passes an independently authentication 'remote' user via http. In this case ZoneMinder would use the supplied user without additional authentication provided such a user is configured ion ZoneMinder. +AUTH_TYPE - ZoneMinder can use two methods to authenticate users when running in authenticated mode. The first is a builtin method where ZoneMinder provides facilities for users to log in and maintains track of their identity. The second method allows interworking with other methods such as http basic authentication which passes an independently authenticated 'remote' user via http. In this case ZoneMinder would use the supplied user without additional authentication provided such a user is configured in ZoneMinder. AUTH_RELAY - When ZoneMinder is running in authenticated mode it can pass user details between the web pages and the back end processes. There are two methods for doing this. This first is to use a time limited hashed string which contains no direct username or password details, the second method is to pass the username and passwords around in plaintext. This method is not recommend except where you do not have the md5 libraries available on your system or you have a completely isolated system with no external access. You can also switch off authentication relaying if your system is isolated in other ways. -AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is necessary to generate hashed strings containing encrypted sensitive information such as usernames and password. Although these string are reasonably secure the addition of a random secret increases security substantially. Note that if you are using the new token based APIs, then this field is mandatory with ZM 1.34 and above +AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is necessary to generate hashed strings containing encrypted sensitive information such as usernames and passwords. Although these strings are reasonably secure the addition of a random secret increases security substantially. Note that if you are using the new token based APIs, then this field is mandatory with ZM 1.34 and above. -AUTH_HASH_IPS - When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help. It is recommended you keep this off if you use mobile apps like zmNinja over mobile carrier networks - several APNs change the IP very frequently which may result in authentication failure. +AUTH_HASH_IPS - When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help. It is recommended you keep this off if you use mobile apps like zmNinja over mobile carrier networks - several APNs change the IP very frequently which may result in authentication failures. AUTH_HASH_TTL - Time before ZM auth will expire (does not apply to API tokens). The default has traditionally been 2 hours. A new hash will automatically be regenerated at half this value. @@ -34,11 +34,11 @@ OPT_USE_API - A global setting to enable/disable ZoneMinder APIs. If you are usi OPT_USE_LEGACY_AUTH - Starting version 1.34.0, ZoneMinder uses a more secure Authentication mechanism using JWT tokens. Older versions used a less secure MD5 based auth hash. It is recommended you turn this off after you are sure you don't need it. If you are using a 3rd party app that relies on the older API auth mechanisms, you will have to update that app if you turn this off. Note that zmNinja 1.3.057 onwards supports the new token system. -OPT_USE_EVENT_NOTIFICATION - zmeventnotification is a 3rd party event notification server that is used to get notifications for alarms detected by ZoneMinder in real time. zmNinja requires this server for push notifications to mobile phones. This option only enables the server if its already installed. Please visit the `Event Notification Server project site `__ for installation instructions. +OPT_USE_EVENT_NOTIFICATION - zmeventnotification is a 3rd party event notification server that is used to get notifications for alarms detected by ZoneMinder in real time. zmNinja requires this server for push notifications to mobile phones. This option only enables the server if it is already installed. Please visit the `Event Notification Server project site `__ for installation instructions. -OPT_USE_GOOG_RECAPTCHA - This option allows you to include a google reCaptcha validation at login. This means in addition to providing a valid usernane and password, you will also have to pass the reCaptcha test. Please note that enabling this option results in the zoneminder login page reach out to google servers for captcha validation. Also please note that enabling this option may break 3rd party clients if they rely on web based logins (Note that zmNinja now uses the API based token method and will not be affected if reCAPTCHA is enabled). If you enable this, you also need to specify your site and secret key (please refer to context help in the ZoneMinder system screen) +OPT_USE_GOOG_RECAPTCHA - This option allows you to include a google reCaptcha validation at login. This means in addition to providing a valid username and password, you will also have to pass the reCaptcha test. Please note that enabling this option results in the zoneminder login page reaching out to google servers for captcha validation. Also please note that enabling this option may break 3rd party clients if they rely on web based logins (Note that zmNinja now uses the API based token method and will not be affected if reCAPTCHA is enabled). If you enable this, you also need to specify your site and secret key (please refer to context help in the ZoneMinder system screen). -SYSTEM_SHUTDOWN - this option decides if it is allowed to shutdown the full system via the ZM UI. The system will need to have sudo installed and the following added to /etc/sudoers: +SYSTEM_SHUTDOWN - this option puts a poweroff icon in the header of the ZM UI for users with System privilege accessi. This icon will allow the user to shutdown the full system via the ZM UI. The system will need to have sudo installed and the following added to /etc/sudoers: :: @@ -46,9 +46,9 @@ SYSTEM_SHUTDOWN - this option decides if it is allowed to shutdown the full syst to perform the shutdown or reboot -OPT_FAST_DELETE - Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if your are trying to do a lot of events at once. **NOTE**: It is recommended that you keep this option OFF, unless you are running on an old or low-powered system. +OPT_FAST_DELETE - Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if youxr are trying to do a lot of events at once. **NOTE**: It is recommended that you keep this option OFF, unless you are running on an old or low-powered system. -FILTER_RELOAD_DELAY - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often in seconds the filters are reloaded from the database to get the latest versions or new filters. If you don't change filters very often this value can be set to a large value. +FILTER_RELOAD_DELAY - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often in seconds the filters are reloaded from the database to get the latest versions or new filters. If you don't change filters very often this value can be set to a large value. As of 1.34.0 filters should be automatically reloaded when saving a filter so this setting should have little effect. FILTER_EXECUTE_INTERVAL - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often the filters are executed on the saved event in the database. If you want a rapid response to new events this should be a smaller value, however this may increase the overall load on the system and affect performance of other elements. @@ -58,9 +58,9 @@ STATUS_UPDATE_INTERVAL - The zmstats daemon performs various db queries related WATCH_CHECK_INTERVAL - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines how often the daemons are checked. -WATCH_MAX_DELAY - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines the maximum delay to allow since the last captured frame. The daemon will be restarted if it has not captured any images after this period though the actual restart may take slightly longer in conjunction with the check interval value above. +WATCH_MAX_DELAY - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines the maximum delay to allow since the last captured frame. The daemon will be restarted if it has not captured any images after this period though the actual restart may take slightly longer in conjunction with the check interval value above. Please note that some cameras can take up to 30 seconds to get a valid image, so this setting should be larger than that. -RUN_AUDIT - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. It is recommended you keep this **OFF** in most systems. +RUN_AUDIT - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. It is recommended you keep this **OFF** in most systems and run it manually if needed after a system crash. AUDIT_CHECK_INTERVAL - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. The default check interval of 900 seconds (15 minutes) is fine for most systems however if you have a very large number of events the process of scanning the database and filesystem may take a long time and impact performance. In this case you may prefer to make this interval much larger to reduce the impact on your system. This option determines how often these checks are performed. @@ -70,11 +70,11 @@ OPT_CONTROL - ZoneMinder includes limited support for controllable cameras. A nu OPT_TRIGGERS - ZoneMinder can interact with external systems which prompt or cancel alarms. This is done via the zmtrigger.pl script. This option indicates whether you want to use these external triggers. Most people will say no here. -CHECK_FOR_UPDATES - From ZoneMinder version 1.17.0 onwards new versions are expected to be more frequent. To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable +CHECK_FOR_UPDATES - To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable. TELEMETRY_DATA - Enable collection of usage information of the local system and send it to the ZoneMinder development team. This data will be used to determine things like who and where our customers are, how big their systems are, the underlying hardware and operating system, etc. This is being done for the sole purpose of creating a better product for our target audience. This script is intended to be completely transparent to the end user, and can be disabled from the web console under Options. For more details on what information we collect, please refer to Zoneminder's privacy statement (available in the contextual help of TELEMETRY_DATA on your installation). -UPDATE_CHECK_PROXY - If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of ``http://:/`` +UPDATE_CHECK_PROXY - If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of ``http://:/``. SHM_KEY - ZoneMinder uses shared memory to speed up communication between modules. To identify the right area to use shared memory keys are used. This option controls what the base key is, each monitor will have it's Id or'ed with this to get the actual key used. You will not normally need to change this value unless it clashes with another instance of ZoneMinder on the same machine. Only the first four hex digits are used, the lower four will be masked out and ignored. diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm index 59d9e3550..4f14e787a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/DCS5020L.pm @@ -46,243 +46,187 @@ use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); -sub new -{ - my $class = shift; - my $id = shift; - my $self = ZoneMinder::Control->new( $id ); - bless( $self, $class ); - srand( time() ); - return $self; +sub open { + my $self = shift; + + $self->loadMonitor(); + + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent('ZoneMinder Control Agent/' . ZoneMinder::Base::ZM_VERSION); + $self->{state} = 'open'; } -our $AUTOLOAD; - -sub AUTOLOAD -{ - my $self = shift; - my $class = ref($self) || croak( "$self not object" ); - my $name = $AUTOLOAD; - $name =~ s/.*://; - if ( exists($self->{$name}) ) - { - return( $self->{$name} ); - } - Fatal( "Can't access $name member of object of class $class" ); +sub close { + my $self = shift; + $self->{state} = 'closed'; } -sub open -{ - my $self = shift; +sub sendCmd { + my $self = shift; + my $cmd = shift; + my $cgi = shift; - $self->loadMonitor(); + my $result = undef; - use LWP::UserAgent; - $self->{ua} = LWP::UserAgent->new; - $self->{ua}->agent( "ZoneMinder Control Agent/" . ZoneMinder::Base::ZM_VERSION ); - $self->{state} = 'open'; + printMsg($cmd, 'Tx'); + + my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" ); + $req->content($cmd); + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) { + $result = !undef; + } else { + Error("Error check failed: '".$res->status_line()."'"); + } + + return $result; } -sub close -{ - my $self = shift; - $self->{state} = 'closed'; +sub move { + my $self = shift; + my $dir = shift; + my $panStep = shift; + my $tiltStep = shift; + my $cmd = "PanSingleMoveDegree=$panStep&TiltSingleMoveDegree=$tiltStep&PanTiltSingleMove=$dir"; + $self->sendCmd($cmd, 'pantiltcontrol'); } -sub printMsg -{ - my $self = shift; - my $msg = shift; - my $msg_len = length($msg); - - Debug( $msg."[".$msg_len."]" ); +sub moveRel { + my $self = shift; + my $params = shift; + my $panStep = $self->getParam($params, 'panstep', 0); + my $tiltStep = $self->getParam($params, 'tiltstep', 0); + my $dir = shift; + $self->move( $dir, $panStep, $tiltStep ); } -sub sendCmd -{ - my $self = shift; - my $cmd = shift; - my $cgi = shift; - - my $result = undef; - - printMsg( $cmd, "Tx" ); - - my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" ); - $req->content($cmd); - my $res = $self->{ua}->request($req); - - if ( $res->is_success ) - { - $result = !undef; - } - else - { - Error( "Error check failed: '".$res->status_line()."'" ); - } - - return( $result ); +sub moveRelUpLeft { + my $self = shift; + my $params = shift; + $self->moveRel($params, 0); } -sub move -{ - my $self = shift; - my $dir = shift; - my $panStep = shift; - my $tiltStep = shift; - my $cmd = "PanSingleMoveDegree=$panStep&TiltSingleMoveDegree=$tiltStep&PanTiltSingleMove=$dir"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub moveRelUp { + my $self = shift; + my $params = shift; + $self->moveRel($params, 1); } -sub moveRel -{ - my $self = shift; - my $params = shift; - my $panStep = $self->getParam($params, 'panstep', 0); - my $tiltStep = $self->getParam($params, 'tiltstep', 0); - my $dir = shift; - $self->move( $dir, $panStep, $tiltStep ); +sub moveRelUpRight { + my $self = shift; + my $params = shift; + $self->moveRel($params, 2); } -sub moveRelUpLeft -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 0 ); +sub moveRelLeft { + my $self = shift; + my $params = shift; + $self->moveRel($params, 3); } -sub moveRelUp -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 1 ); +sub moveRelRight { + my $self = shift; + my $params = shift; + $self->moveRel($params, 5); } -sub moveRelUpRight -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 2 ); +sub moveRelDownLeft { + my $self = shift; + my $params = shift; + $self->moveRel($params, 6); } -sub moveRelLeft -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 3 ); +sub moveRelDown { + my $self = shift; + my $params = shift; + $self->moveRel($params, 7); } -sub moveRelRight -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 5 ); -} - -sub moveRelDownLeft -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 6 ); -} - -sub moveRelDown -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 7 ); -} - -sub moveRelDownRight -{ - my $self = shift; - my $params = shift; - $self->moveRel( $params, 8 ); +sub moveRelDownRight { + my $self = shift; + my $params = shift; + $self->moveRel($params, 8); } # moves the camera to center on the point that the user clicked on in the video image. # This isn't extremely accurate but good enough for most purposes -sub moveMap -{ - # if the camera moves too much or too little, try increasing or decreasing this value - my $f = 11; +sub moveMap { + # if the camera moves too much or too little, try increasing or decreasing this value + my $f = 11; - my $self = shift; - my $params = shift; - my $xcoord = $self->getParam( $params, 'xcoord' ); - my $ycoord = $self->getParam( $params, 'ycoord' ); + my $self = shift; + my $params = shift; + my $xcoord = $self->getParam( $params, 'xcoord' ); + my $ycoord = $self->getParam( $params, 'ycoord' ); - my $hor = $xcoord * 100 / $self->{Monitor}->{Width}; - my $ver = $ycoord * 100 / $self->{Monitor}->{Height}; - - my $direction; - my $horSteps; - my $verSteps; - if ($hor < 50 && $ver < 50) { - # up left - $horSteps = (50 - $hor) / $f; - $verSteps = (50 - $ver) / $f; - $direction = 0; - } elsif ($hor >= 50 && $ver < 50) { - # up right - $horSteps = ($hor - 50) / $f; - $verSteps = (50 - $ver) / $f; - $direction = 2; - } elsif ($hor < 50 && $ver >= 50) { - # down left - $horSteps = (50 - $hor) / $f; - $verSteps = ($ver - 50) / $f; - $direction = 6; - } elsif ($hor >= 50 && $ver >= 50) { - # down right - $horSteps = ($hor - 50) / $f; - $verSteps = ($ver - 50) / $f; - $direction = 8; - } - my $v = int($verSteps + .5); - my $h = int($horSteps + .5); - Debug( "Move Map to $xcoord,$ycoord, hor=$h, ver=$v with direction $direction" ); - $self->move( $direction, $h, $v ); + my $hor = $xcoord * 100 / $self->{Monitor}->{Width}; + my $ver = $ycoord * 100 / $self->{Monitor}->{Height}; + + my $direction; + my $horSteps; + my $verSteps; + if ($hor < 50 && $ver < 50) { + # up left + $horSteps = (50 - $hor) / $f; + $verSteps = (50 - $ver) / $f; + $direction = 0; + } elsif ($hor >= 50 && $ver < 50) { + # up right + $horSteps = ($hor - 50) / $f; + $verSteps = (50 - $ver) / $f; + $direction = 2; + } elsif ($hor < 50 && $ver >= 50) { + # down left + $horSteps = (50 - $hor) / $f; + $verSteps = ($ver - 50) / $f; + $direction = 6; + } elsif ($hor >= 50 && $ver >= 50) { + # down right + $horSteps = ($hor - 50) / $f; + $verSteps = ($ver - 50) / $f; + $direction = 8; + } + my $v = int($verSteps + .5); + my $h = int($horSteps + .5); + Debug("Move Map to $xcoord,$ycoord, hor=$h, ver=$v with direction $direction"); + $self->move($direction, $h, $v); } -sub presetClear -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Clear Preset $preset" ); - my $cmd = "ClearPosition=$preset"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub presetClear { + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Clear Preset $preset" ); + my $cmd = "ClearPosition=$preset"; + $self->sendCmd( $cmd, 'pantiltcontrol' ); } -sub presetSet -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Set Preset $preset" ); - my $cmd = "SetCurrentPosition=$preset&SetName=preset_$preset"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub presetSet { + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Set Preset $preset" ); + my $cmd = "SetCurrentPosition=$preset&SetName=preset_$preset"; + $self->sendCmd( $cmd, 'pantiltcontrol' ); } -sub presetGoto -{ - my $self = shift; - my $params = shift; - my $preset = $self->getParam( $params, 'preset' ); - Debug( "Goto Preset $preset" ); - my $cmd = "PanTiltPresetPositionMove=$preset"; - $self->sendCmd( $cmd, 'pantiltcontrol' ); +sub presetGoto { + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Goto Preset $preset" ); + my $cmd = "PanTiltPresetPositionMove=$preset"; + $self->sendCmd( $cmd, 'pantiltcontrol' ); } -sub presetHome -{ - my $self = shift; - Debug( "Home Preset" ); - $self->move( 4, 0, 0 ); +sub presetHome { + my $self = shift; + Debug( "Home Preset" ); + $self->move( 4, 0, 0 ); } - # IR Controls # # wake = IR on @@ -290,40 +234,36 @@ sub presetHome # reset = IR auto sub setDayNightMode { - my $self = shift; - my $mode = shift; - my $cmd = "DayNightMode=$mode&ConfigReboot=No"; - $self->sendCmd( $cmd, 'daynight' ); + my $self = shift; + my $mode = shift; + my $cmd = "DayNightMode=$mode&ConfigReboot=No"; + $self->sendCmd($cmd, 'daynight'); } -sub wake -{ - my $self = shift; - Debug( "Wake - IR on" ); - $self->setDayNightMode(2); +sub wake { + my $self = shift; + Debug('Wake - IR on'); + $self->setDayNightMode(2); } -sub sleep -{ - my $self = shift; - Debug( "Sleep - IR off" ); - $self->setDayNightMode(3); +sub sleep { + my $self = shift; + Debug('Sleep - IR off'); + $self->setDayNightMode(3); } -sub reset -{ - my $self = shift; - Debug( "Reset - IR auto" ); - $self->setDayNightMode(0); +sub reset { + my $self = shift; + Debug('Reset - IR auto'); + $self->setDayNightMode(0); } 1; __END__ -# Below is stub documentation for your module. You'd better edit it! =head1 NAME -ZoneMinder::Database - Perl extension for DCS-5020L +ZoneMinder::Control::DCS5020L - Perl extension for DCS-5020L =head1 SYNOPSIS @@ -351,6 +291,20 @@ Art Scheel ascheel (at) gmail =head1 COPYRIGHT AND LICENSE -LGPLv3 +Copyright (C) 2018 ZoneMinder LLC + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. =cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in index 53da1659b..13cae4d4b 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in @@ -301,7 +301,7 @@ sub zmMemVerify { } return !undef; -} +} # end sub zmMemVerify sub zmMemRead { my $monitor = shift; @@ -375,10 +375,8 @@ sub zmMemInvalidate { my $mem_key = zmMemKey($monitor); if ( $mem_key ) { zmMemDetach($monitor); - } else { - Warning('no memkey in zmMemInvalidate'); } -} +} # end sub zmMemInvalidate sub zmMemTidy { zmMemClean(); @@ -504,10 +502,10 @@ sub zmHasAlarmed { my $last_event_id = shift; my ( $state, $last_event ) = zmMemRead($monitor, - ['shared_data:state' ,'shared_data:last_event'] + ['shared_data:state', 'shared_data:last_event'] ); - if ( $state == STATE_ALARM || $state == STATE_ALERT ) { + if ( $state == STATE_ALARM or $state == STATE_ALERT ) { return $last_event; } elsif( $last_event != $last_event_id ) { return $last_event; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm b/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm index b9a8b6a1c..cb7920913 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm @@ -51,7 +51,7 @@ our %EXPORT_TAGS = ( ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; -our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); +our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } ); our @EXPORT = @EXPORT_OK; @@ -77,17 +77,17 @@ sub zmMemAttach { my ( $monitor, $size ) = @_; if ( !$size ) { - Error("No size passed to zmMemAttach for monitor $$monitor{Id}"); + Error('No size passed to zmMemAttach for monitor '.$$monitor{Id}); return undef; } if ( defined($monitor->{MMapAddr}) ) { - Debug("zmMemAttach already attached at $monitor->{MMapAddr}"); + Debug("zmMemAttach already attached at $monitor->{MMapAddr} for $$monitor{Id}"); return !undef; } my $mmap_file = $Config{ZM_PATH_MAP}.'/zm.mmap.'.$monitor->{Id}; if ( ! -e $mmap_file ) { - Error("Memory map file '$mmap_file' does not exist. zmc might not be running."); + Error("Memory map file '$mmap_file' does not exist in zmMemAttach. zmc might not be running."); return undef; } my $mmap_file_size = -s $mmap_file; @@ -119,18 +119,24 @@ sub zmMemDetach { if ( $monitor->{MMap} ) { if ( ! munmap(${$monitor->{MMap}}) ) { - Warn( "Unable to munmap for monitor $$monitor{Id}\n"); + Warn("Unable to munmap for monitor $$monitor{Id}"); } delete $monitor->{MMap}; + } else { + Warn("No MMap for $$monitor{Id}"); } if ( $monitor->{MMapAddr} ) { delete $monitor->{MMapAddr}; + } else { + Warn("No MMapAddr in $$monitor{Id}"); } if ( $monitor->{MMapHandle} ) { close($monitor->{MMapHandle}); delete $monitor->{MMapHandle}; + } else { + Warn("No MMapHandle in $$monitor{Id}"); } -} +} # end sub zmMemDetach sub zmMemGet { my $monitor = shift; @@ -162,7 +168,7 @@ sub zmMemPut { } sub zmMemClean { - Debug("Removing memory map files"); + Debug('Removing memory map files'); my $mapPath = $Config{ZM_PATH_MAP}.'/zm.mmap.*'; foreach my $mapFile( glob( $mapPath ) ) { ( $mapFile ) = $mapFile =~ /^(.+)$/; diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 18b08cdf1..5b478316d 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -829,14 +829,20 @@ sub sendEmail { Data => $body ); ### Add the attachments + my $total_size = 0; foreach my $attachment ( @attachments ) { - Info("Attaching '$attachment->{path}'"); + my $size = -s $attachment->{path}; + $total_size += $size; + Info("Attaching '$attachment->{path}' which is $size bytes"); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, Disposition => 'attachment' ); } + if ( $total_size > 10*1024*1024 ) { + Warning('Emails larger than 10Mb will often not be delivered! This one is '.int($total_size/(1024*1024)).'Mb'); + } ### Send the Message if ( $Config{ZM_SSMTP_MAIL} ) { my $ssmtp_location = $Config{ZM_SSMTP_PATH}; @@ -860,20 +866,27 @@ sub sendEmail { } } else { my $mail = MIME::Entity->build( - From => $Config{ZM_FROM_EMAIL}, - To => $$filter{EmailTo}, - Subject => $subject, - Type => (($body=~/ $body - ); + From => $Config{ZM_FROM_EMAIL}, + To => $$filter{EmailTo}, + Subject => $subject, + Type => (($body=~/ $body + ); + my $total_size = 0; foreach my $attachment ( @attachments ) { - Info("Attaching '$attachment->{path}'"); + my $size = -s $attachment->{path}; + $total_size += $size; + Info("Attaching '$attachment->{path}' which is $size bytes"); + $mail->attach( - Path => $attachment->{path}, - Type => $attachment->{type}, - Encoding => 'base64' - ); + Path => $attachment->{path}, + Type => $attachment->{type}, + Encoding => 'base64' + ); + } # end foreach attachment + if ( $total_size > 10*1024*1024 ) { + Warning('Emails larger than 10Mb will often not be delivered! This one is '.int($total_size/(1024*1024)).'Mb'); } $mail->smtpsend(Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL}); } diff --git a/scripts/zmtelemetry.pl.in b/scripts/zmtelemetry.pl.in index 3a6e81320..a3debabdb 100644 --- a/scripts/zmtelemetry.pl.in +++ b/scripts/zmtelemetry.pl.in @@ -45,6 +45,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $help = 0; my $force = 0; +my $show = 0; # Interval between version checks my $interval; my $version; @@ -52,6 +53,7 @@ my $version; GetOptions( force => \$force, help => \$help, + show => \$show, interval => \$interval, version => \$version ); @@ -59,6 +61,14 @@ if ( $version ) { print( ZoneMinder::Base::ZM_VERSION . "\n"); exit(0); } +if ($show) { + my %telemetry; + my $dbh = zmDbConnect(); + collectData($dbh, \%telemetry); + my $result = jsonEncode(\%telemetry); + print ($result); + exit(0); +} if ( $help ) { pod2usage(-exitstatus => -1); } @@ -89,21 +99,9 @@ while( 1 ) { my $dbh = zmDbConnect(); # Build the telemetry hash # We should keep *BSD systems in mind when calling system commands + my %telemetry; - $telemetry{uuid} = getUUID($dbh); - @telemetry{qw(city region country latitude longitude)} = getGeo(); - $telemetry{timestamp} = strftime('%Y-%m-%dT%H:%M:%S%z', localtime()); - $telemetry{monitor_count} = countQuery($dbh,'Monitors'); - $telemetry{event_count} = countQuery($dbh,'Events'); - $telemetry{architecture} = runSysCmd('uname -p'); - ($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro(); - $telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION; - $telemetry{system_memory} = totalmem(); - $telemetry{processor_count} = cpu_count(); - $telemetry{monitors} = getMonitorRef($dbh); - - Info('Sending data to ZoneMinder Telemetry server.'); - + collectData($dbh,\%telemetry); my $result = jsonEncode(\%telemetry); if ( sendData($result) ) { @@ -126,6 +124,24 @@ print 'ZoneMinder Telemetry Agent exiting at '.strftime('%y/%m/%d %H:%M:%S', loc # SUBROUTINES # ############### +# collect data to send +sub collectData { + my $dbh = shift; + my $telemetry = shift; + $telemetry->{uuid} = getUUID($dbh); + ($telemetry->{city},$telemetry->{region},$telemetry->{country},$telemetry->{latitude},$telemetry->{longitude})=getGeo(); + $telemetry->{timestamp} = strftime('%Y-%m-%dT%H:%M:%S%z', localtime()); + $telemetry->{monitor_count} = countQuery($dbh,'Monitors'); + $telemetry->{event_count} = countQuery($dbh,'Events'); + $telemetry->{architecture} = runSysCmd('uname -p'); + ($telemetry->{kernel}, $telemetry->{distro}, $telemetry->{version}) = getDistro(); + $telemetry->{zm_version} = ZoneMinder::Base::ZM_VERSION; + $telemetry->{system_memory} = totalmem(); + $telemetry->{processor_count} = cpu_count(); + $telemetry->{use_event_server} = $Config{ZM_OPT_USE_EVENTNOTIFICATION}; + $telemetry->{monitors} = getMonitorRef($dbh); +} + # Find, verify, then run the supplied system command sub runSysCmd { my $msg = shift; @@ -365,7 +381,7 @@ zmtelemetry.pl - Send usage information to the ZoneMinder development team =head1 SYNOPSIS - zmtelemetry.pl [--force] [--help] [--interval=seconds] [--version] + zmtelemetry.pl [--force] [--help] [--show] [--interval=seconds] [--version] =head1 DESCRIPTION @@ -382,6 +398,7 @@ console under Options. --force Force the script to upload it's data instead of waiting for the defined interval since last upload. --help Display usage information + --show Displays telemetry data that is sent to zoneminder --interval Override the default configured interval since last upload. The value should be given in seconds, but can be an expression such as 24*60*60. diff --git a/scripts/zmtrigger.pl.in b/scripts/zmtrigger.pl.in index c56107276..1ee392c1e 100644 --- a/scripts/zmtrigger.pl.in +++ b/scripts/zmtrigger.pl.in @@ -356,11 +356,16 @@ sub handleMessage { my $connection = shift; my $message = shift; + # CUA - Axis camera send the message quoted with" + # CUA - Also Axis camera cannot save the plus sign which + $message =~ s/^\"//g; + $message =~ s/\"$//g; + my ( $id, $action, $score, $cause, $text, $showtext ) = split( /\|/, $message ); - $score = 0 if ( !defined($score) ); - $cause = '' if ( !defined($cause) ); - $text = '' if ( !defined($text) ); + $score = 0 if !defined($score); + $cause = '' if !defined($cause); + $text = '' if !defined($text); my $monitor = $monitors{$id}; if ( !$monitor ) { @@ -372,7 +377,7 @@ sub handleMessage { next if !zmMemVerify($monitor); Debug("Handling action '$action'"); - if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ ) { + if ( $action =~ /^(enable|disable)(?:[\+ ](\d+))?$/ ) { my $state = $1; my $delay = $2; if ( $state eq 'enable' ) { diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index b0b63757a..3662b6655 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -847,9 +847,9 @@ if ( $version ) { } $cascade = !undef; } - if ( $cascade || $version eq "1.24.4" ) { + if ( $cascade || $version eq '1.24.4' ) { # Patch the database - patchDB( $dbh, "1.24.4" ); + patchDB($dbh, '1.24.4'); # Copy the FTP specific values to the new general config my $fetchSql = "select * from Config where Name like 'ZM_UPLOAD_FTP_%'"; @@ -863,12 +863,12 @@ if ( $version ) { } $cascade = !undef; } - if ( $cascade || $version lt "1.26.0" ) { - my $sth = $dbh->prepare_cached( 'select * from Monitors LIMIT 0,1' ); + if ( $cascade || $version lt '1.26.0' ) { + my $sth = $dbh->prepare_cached('SELECT * FROM Monitors LIMIT 0,1'); die "Error: " . $dbh->errstr . "\n" unless ($sth); die "Error: " . $sth->errstr . "\n" unless ($sth->execute); - my $columns = $sth->{'NAME'}; + my $columns = $sth->{NAME}; if ( ! grep(/^Colours$/, @$columns ) ) { $dbh->do(q{alter table Monitors add column `Colours` tinyint(3) unsigned NOT NULL default '1' after `Height`;}); } # end if @@ -898,28 +898,31 @@ if ( $version ) { die "Should have found upgrade scripts at $updateDir\n"; } # end if + my $sql = "UPDATE `Config` SET `Value` = ? WHERE `Name` = 'ZM_DYN_DB_VERSION'"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + foreach my $patch ( @files ) { my ( $v ) = $patch =~ /^zm_update\-([\d\.]+)\.sql$/; #PP make sure we use version compare - if ( version->parse('v' . $v) > version->parse('v' . $version) ) { - print( "Upgrading DB to $v from $version\n" ); - patchDB( $dbh, $v ); - my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); + if ( version->parse('v'.$v) > version->parse('v'.$version) ) { + print("Upgrading DB to $v from $version\n"); + if ( patchDB($dbh, $v) ) { + my $res = $sth->execute($version) or die( "Can't execute: ".$sth->errstr() ); + } #patchDB_using_do( $dbh, $version, $updateDir.'/'.$patch ); } # end if newer version } # end foreach patchfile + + $sth->finish(); $cascade = !undef; } # end if if ( $cascade ) { - my $installed_version = ZM_VERSION; - my $sql = 'update Config set Value = ? where Name = ?'; + # This is basically here so that we don't need zm-update-blah.sql files for versions without db changes + my $sql = 'UPDATE `Config` SET `Value` = ? WHERE `Name` = ?'; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( "$installed_version", 'ZM_DYN_DB_VERSION' ) or die( "Can't execute: ".$sth->errstr() ); - $res = $sth->execute( "$installed_version", 'ZM_DYN_CURR_VERSION' ) or die( "Can't execute: ".$sth->errstr() ); + $sth->execute(ZM_VERSION, 'ZM_DYN_DB_VERSION') or die( "Can't execute: ".$sth->errstr() ); + $sth->execute(ZM_VERSION, 'ZM_DYN_CURR_VERSION') or die( "Can't execute: ".$sth->errstr() ); $sth->finish(); } else { zmDbDisconnect(); @@ -930,41 +933,42 @@ if ( $version ) { #my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); #my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() ); #$sth->finish(); - print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" ); -} + print("\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n"); +} # end if version + zmDbDisconnect(); -exit( 0 ); +exit(0); sub patchDB_using_do { my ( $dbh, $version, $file ) = @_; - open( my $fh, '<', $file ) or die "Unable to open $file $!"; + open(my $fh, '<', $file) or die "Unable to open $file $!"; $/ = undef; my $sql = <$fh>; close $fh; if ( $sql ) { - $dbh->{'AutoCommit'} = 0; + $dbh->{AutoCommit} = 0; $dbh->do($sql); if ( $dbh->errstr() ) { $dbh->rollback(); - die "Error: " . $dbh->errstr(). ". Rolled back.\n"; + die 'Error: '.$dbh->errstr().". Rolled back.\n"; } # end if error - my $sql = "update Config set Value = ? where Name = 'ZM_DYN_DB_VERSION'"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); + my $sql = 'UPDATE `Config` SET `Value` = ? WHERE `Name` = \'ZM_DYN_DB_VERSION\''; + my $sth = $dbh->prepare_cached($sql) or die "Can't prepare '$sql': ".$dbh->errstr(); + my $res = $sth->execute($version) or die 'Can\'t execute: '.$sth->errstr(); $sth->finish(); - $dbh->{'AutoCommit'} = 1; + $dbh->{AutoCommit} = 1; } else { Warning("Empty db update file at $file"); } -} +} # end sub patchDB_using_do + sub patchDB { my $dbh = shift; my $version = shift; - my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); my $command = 'mysql'; if ( defined($portOrSocket) ) { @@ -988,39 +992,38 @@ sub patchDB { } $command .= '/zm_update-'.$version.'.sql'; - print( "Executing '$command'\n" ) if ( logDebugging() ); + print("Executing '$command'\n") if logDebugging(); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { - chomp( $output ); - print( "Output: $output\n" ); + chomp($output); + print("Output: $output\n"); } if ( $status ) { - die( "Command '$command' exited with status: $status\n" ); + die("Command '$command' exited with status: $status\n"); } - print( "\nDatabase successfully upgraded to version $version.\n" ); - -} + print("\nDatabase successfully upgraded to version $version.\n"); +} # end sub patchDB sub migratePasswords { - print ("Migratings passwords, if any...\n"); - my $sql = "select * from Users"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); - while( my $user = $sth->fetchrow_hashref() ) { - my $scheme = substr($user->{Password}, 0, 1); - if ($scheme eq "*") { - print ("-->".$user->{Username}. " password will be migrated\n"); - my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8)); - my $settings = '$2a$10$'.$salt; - my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings); - my $new_pass_hash = "-ZM-".$pass_hash; - $sql = "UPDATE Users SET PASSWORD=? WHERE Username=?"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute($new_pass_hash, $user->{Username}) or die( "Can't execute: ".$sth->errstr() ); - } + print ("Migratings passwords, if any...\n"); + my $sql = 'SELECT * FROM `Users`'; + my $sth = $dbh->prepare_cached($sql) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die("Can't execute: ".$sth->errstr()); + while( my $user = $sth->fetchrow_hashref() ) { + my $scheme = substr($user->{Password}, 0, 1); + if ($scheme eq '*') { + print ('-->'.$user->{Username}." password will be migrated\n"); + my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8)); + my $settings = '$2a$10$'.$salt; + my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings); + my $new_pass_hash = '-ZM-'.$pass_hash; + $sql = 'UPDATE Users SET `Password`=? WHERE `Username`=?'; + my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($new_pass_hash, $user->{Username}) or die("Can't execute: ".$sth->errstr()); } -} + } +} # end sub migratePasswords sub migratePaths { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a75b37bc1..417af5289 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,7 @@ set(ZM_BIN_SRC_FILES zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp + zm_libvnc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp diff --git a/src/jwt-cpp/include/jwt-cpp/jwt.h b/src/jwt-cpp/include/jwt-cpp/jwt_cpp.h similarity index 100% rename from src/jwt-cpp/include/jwt-cpp/jwt.h rename to src/jwt-cpp/include/jwt-cpp/jwt_cpp.h diff --git a/src/libbcrypt/src/bcrypt.c b/src/libbcrypt/src/bcrypt.c index c32b51b0b..db16d27b3 100644 --- a/src/libbcrypt/src/bcrypt.c +++ b/src/libbcrypt/src/bcrypt.c @@ -22,7 +22,7 @@ #endif #include -#ifdef _WIN32 || _WIN64 +#if defined(_WIN32) || defined(_WIN64) // On windows we need to generate random bytes differently. typedef __int64 ssize_t; #define BCRYPT_HASHSIZE 60 @@ -117,7 +117,7 @@ int bcrypt_gensalt(int factor, char salt[BCRYPT_HASHSIZE]) char *aux; // Note: Windows does not have /dev/urandom sadly. -#ifdef _WIN32 || _WIN64 +#if defined(_WIN32) || defined(_WIN64) HCRYPTPROV p; ULONG i; diff --git a/src/zm_camera.h b/src/zm_camera.h index eb1c6e018..acee2decf 100644 --- a/src/zm_camera.h +++ b/src/zm_camera.h @@ -36,7 +36,7 @@ class Camera; // class Camera { protected: - typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; + typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC, VNC_SRC } SourceType; unsigned int monitor_id; Monitor * monitor; // Null on instantiation, set as soon as possible. @@ -87,6 +87,7 @@ public: bool IsFfmpeg() const { return type == FFMPEG_SRC; } bool IsLibvlc() const { return type == LIBVLC_SRC; } bool IscURL() const { return type == CURL_SRC; } + bool IsVNC() const { return type == VNC_SRC; } unsigned int Width() const { return width; } unsigned int Height() const { return height; } unsigned int Colours() const { return colours; } diff --git a/src/zm_crypt.cpp b/src/zm_crypt.cpp index 6b78e169b..8e42b3b3c 100644 --- a/src/zm_crypt.cpp +++ b/src/zm_crypt.cpp @@ -1,12 +1,81 @@ #include "zm.h" #include "zm_crypt.h" #include "BCrypt.hpp" -#include "jwt.h" +#if HAVE_LIBJWT +#include +#else +#include "jwt_cpp.h" +#endif #include +#if HAVE_LIBCRYPTO #include +#elif HAVE_GNUTLS_GNUTLS_H +#include +#include +#endif #include // returns username if valid, "" if not +#if HAVE_LIBJWT +std::pair verifyToken(std::string jwt_token_str, std::string key) { + std::string username = ""; + unsigned int token_issued_at = 0; + int err = 0; + jwt_t *jwt = nullptr; + + err = jwt_new(&jwt); + if( err ) { + Error("Unable to Allocate JWT object"); + return std::make_pair("", 0); + } + + err = jwt_set_alg(jwt, JWT_ALG_HS256, (const unsigned char*)key.c_str(), key.length()); + if( err ) { + jwt_free(jwt); + Error("Error setting Algorithm for JWT decode"); + return std::make_pair("", 0); + } + + err = jwt_decode(&jwt, jwt_token_str.c_str(), nullptr, 0); + if( err ) { + jwt_free(jwt); + Error("Could not decode JWT"); + return std::make_pair("", 0); + } + + const char *c_type = jwt_get_grant(jwt, (const char*)"type"); + if ( !c_type ) { + jwt_free(jwt); + Error("Missing token type. This should not happen"); + return std::make_pair("", 0); + } else if ( std::string(c_type) != "access" ) { + jwt_free(jwt); + Error("Only access tokens are allowed. Please do not use refresh tokens"); + return std::make_pair("", 0); + } + + const char *c_username = jwt_get_grant(jwt, (const char*)"user"); + if( !c_username ) { + jwt_free(jwt); + Error("User not found in claim"); + return std::make_pair("", 0); + } + + username = std::string(c_username); + Debug(1, "Got %s as user claim from token", username.c_str()); + + token_issued_at = (unsigned int)jwt_get_grant_int(jwt, "iat"); + if ( errno == ENOENT ) { + jwt_free(jwt); + Error("IAT not found in claim. This should not happen"); + return std::make_pair("", 0); + } + + Debug(1, "Got IAT token=%u", token_issued_at); + jwt_free(jwt); + return std::make_pair(username, token_issued_at); +} +#else // HAVE_LIBJWT std::pair verifyToken(std::string jwt_token_str, std::string key) { std::string username = ""; unsigned int token_issued_at = 0; @@ -58,6 +127,7 @@ std::pair verifyToken(std::string jwt_token_str, std } return std::make_pair(username, token_issued_at); } +#endif // HAVE_LIBJWT bool verifyPassword(const char *username, const char *input_password, const char *db_password_hash) { bool password_correct = false; @@ -70,10 +140,16 @@ bool verifyPassword(const char *username, const char *input_password, const char // MYSQL PASSWORD Debug(1, "%s is using an MD5 encoded password", username); - SHA_CTX ctx1, ctx2; + #ifndef SHA_DIGEST_LENGTH + #define SHA_DIGEST_LENGTH 20 + #endif + unsigned char digest_interim[SHA_DIGEST_LENGTH]; unsigned char digest_final[SHA_DIGEST_LENGTH]; - + +#if HAVE_LIBCRYPTO + SHA_CTX ctx1, ctx2; + //get first iteration SHA1_Init(&ctx1); SHA1_Update(&ctx1, input_password, strlen(input_password)); @@ -83,6 +159,15 @@ bool verifyPassword(const char *username, const char *input_password, const char SHA1_Init(&ctx2); SHA1_Update(&ctx2, digest_interim,SHA_DIGEST_LENGTH); SHA1_Final (digest_final, &ctx2); +#elif HAVE_GNUTLS_GNUTLS_H + //get first iteration + gnutls_hash_fast(GNUTLS_DIG_SHA1, input_password, strlen(input_password), digest_interim); + //2nd iteration + gnutls_hash_fast(GNUTLS_DIG_SHA1, digest_interim, SHA_DIGEST_LENGTH, digest_final); +#else + Error("Authentication Error. ZoneMinder not built with GnuTLS or Openssl"); + return false; +#endif char final_hash[SHA_DIGEST_LENGTH * 2 +2]; final_hash[0] = '*'; diff --git a/src/zm_curl_camera.cpp b/src/zm_curl_camera.cpp index 88b44a454..3d9a92058 100644 --- a/src/zm_curl_camera.cpp +++ b/src/zm_curl_camera.cpp @@ -15,8 +15,9 @@ // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// +// +#include #include "zm.h" #include "zm_curl_camera.h" @@ -25,6 +26,18 @@ #if HAVE_LIBCURL +/* Func ptrs for libcurl functions */ +static void *curl_lib = nullptr; +static CURLcode (*curl_global_init_f)(long) = nullptr; +static void (*curl_global_cleanup_f)(void) = nullptr; +static const char* (*curl_easy_strerror_f)(CURLcode) = nullptr; +static char* (*curl_version_f)(void) = nullptr; +static CURL* (*curl_easy_init_f)(void) = nullptr; +static CURLcode (*curl_easy_getinfo_f)(CURL* , CURLINFO, ...) = nullptr; +static CURLcode (*curl_easy_perform_f)(CURL*) = nullptr; +static CURLcode (*curl_easy_setopt_f)(CURL*, CURLoption, ...) = nullptr; +static void (*curl_easy_cleanup_f)(CURL*) = nullptr; + #define CURL_MAXRETRY 5 #define CURL_BUFFER_INITIAL_SIZE 65536 @@ -33,6 +46,31 @@ const char* content_type_match = "Content-Type:"; size_t content_length_match_len; size_t content_type_match_len; +void bind_libcurl_symbols() { + + if(curl_lib) + return; + + curl_lib = dlopen("libcurl.so", RTLD_LAZY | RTLD_GLOBAL); + if(!curl_lib) + curl_lib = dlopen("libcurl-gnutls.so.4", RTLD_LAZY | RTLD_GLOBAL); + if (!curl_lib) { + Error("Could not load libcurl: %s", dlerror()); + return; + } + + // Load up all required symbols here + *(void**) (&curl_global_init_f) = dlsym(curl_lib, "curl_global_init"); + *(void**) (&curl_global_cleanup_f) = dlsym(curl_lib, "curl_global_cleanup"); + *(void**) (&curl_easy_strerror_f) = dlsym(curl_lib, "curl_easy_strerror"); + *(void**) (&curl_version_f) = dlsym(curl_lib, "curl_version"); + *(void**) (&curl_easy_init_f) = dlsym(curl_lib, "curl_easy_init"); + *(void**) (&curl_easy_getinfo_f) = dlsym(curl_lib, "curl_easy_getinfo"); + *(void**) (&curl_easy_perform_f) = dlsym(curl_lib, "curl_easy_perform"); + *(void**) (&curl_easy_setopt_f) = dlsym(curl_lib, "curl_easy_setopt"); + *(void**) (&curl_easy_cleanup_f) = dlsym(curl_lib, "curl_easy_cleanup"); +} + cURLCamera::cURLCamera( int p_id, const std::string &p_path, const std::string &p_user, const std::string &p_pass, unsigned int p_width, unsigned int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ), mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET ) @@ -55,35 +93,42 @@ void cURLCamera::Initialise() { content_type_match_len = strlen(content_type_match); databuffer.expand(CURL_BUFFER_INITIAL_SIZE); - + + bind_libcurl_symbols(); /* cURL initialization */ - CURLcode cRet = curl_global_init(CURL_GLOBAL_ALL); + CURLcode cRet = (*curl_global_init_f)(CURL_GLOBAL_ALL); if(cRet != CURLE_OK) { - Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet)); + Error("libcurl initialization failed: ", (*curl_easy_strerror_f)(cRet)); + dlclose(curl_lib); + return; } - Debug(2,"libcurl version: %s",curl_version()); + Debug(2,"libcurl version: %s", (*curl_version_f)()); /* Create the shared data mutex */ int nRet = pthread_mutex_init(&shareddata_mutex, NULL); if(nRet != 0) { - Fatal("Shared data mutex creation failed: %s",strerror(nRet)); + Error("Shared data mutex creation failed: %s",strerror(nRet)); + return; } /* Create the data available condition variable */ nRet = pthread_cond_init(&data_available_cond, NULL); if(nRet != 0) { - Fatal("Data available condition variable creation failed: %s",strerror(nRet)); + Error("Data available condition variable creation failed: %s",strerror(nRet)); + return; } /* Create the request complete condition variable */ nRet = pthread_cond_init(&request_complete_cond, NULL); if(nRet != 0) { - Fatal("Request complete condition variable creation failed: %s",strerror(nRet)); + Error("Request complete condition variable creation failed: %s",strerror(nRet)); + return; } /* Create the thread */ nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this); if(nRet != 0) { - Fatal("Thread creation failed: %s",strerror(nRet)); + Error("Thread creation failed: %s",strerror(nRet)); + return; } } @@ -102,8 +147,10 @@ void cURLCamera::Terminate() { pthread_mutex_destroy(&shareddata_mutex); /* cURL cleanup */ - curl_global_cleanup(); + (*curl_global_cleanup_f)(); + if(curl_lib) + dlclose(curl_lib); } int cURLCamera::PrimeCapture() { @@ -287,7 +334,8 @@ int cURLCamera::Capture( ZMPacket &zm_packet ) { } } else { /* Failed to match content-type */ - Fatal("Unable to match Content-Type. Check URL, username and password"); + Error("Unable to match Content-Type. Check URL, username and password"); + return -21; } /* mode */ } /* frameComplete loop */ @@ -370,60 +418,88 @@ void* cURLCamera::thread_func() { long tRet; double dSize; - c = curl_easy_init(); + c = (*curl_easy_init_f)(); if(c == NULL) { - Fatal("Failed getting easy handle from libcurl"); + dlclose(curl_lib); + Error("Failed getting easy handle from libcurl"); + tRet = -51; + return (void*)tRet; } CURLcode cRet; /* Set URL */ - cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str()); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet)); + cRet = (*curl_easy_setopt_f)(c, CURLOPT_URL, mPath.c_str()); + if(cRet != CURLE_OK) { + Error("Failed setting libcurl URL: %s", *(curl_easy_strerror_f)(cRet)); + tRet = -52; + return (void*)tRet; + } /* Header callback */ - cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet)); - + cRet = (*curl_easy_setopt_f)(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher); + if(cRet != CURLE_OK) { + Error("Failed setting libcurl header callback function: %s", (*curl_easy_strerror_f)(cRet)); + tRet = -53; + return (void*)tRet; + } + + cRet = (*curl_easy_setopt_f)(c, CURLOPT_HEADERDATA, this); + if(cRet != CURLE_OK) { + Error("Failed setting libcurl header callback object: %s", (*curl_easy_strerror_f)(cRet)); + tRet = -54; + return (void*)tRet; + } /* Data callback */ - cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet)); + cRet = (*curl_easy_setopt_f)(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher); + if(cRet != CURLE_OK) { + Error("Failed setting libcurl data callback function: %s", (*curl_easy_strerror_f)(cRet)); + tRet = -55; + return (void*)tRet; + } + cRet = (*curl_easy_setopt_f)(c, CURLOPT_WRITEDATA, this); + if(cRet != CURLE_OK) { + Error("Failed setting libcurl data callback object: %s", (*curl_easy_strerror_f)(cRet)); + tRet = -56; + return (void*)tRet; + } /* Progress callback */ - cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0); - if(cRet != CURLE_OK) - Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet)); - + cRet = (*curl_easy_setopt_f)(c, CURLOPT_NOPROGRESS, 0); + if(cRet != CURLE_OK) { + Error("Failed enabling libcurl progress callback function: %s", (*curl_easy_strerror_f)(cRet)); + tRet = -57; + return (void*)tRet; + } + + cRet = (*curl_easy_setopt_f)(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher); + if(cRet != CURLE_OK) { + Error("Failed setting libcurl progress callback function: %s", (*curl_easy_strerror_f)(cRet)); + tRet = -58; + return (void*)tRet; + } + + cRet = (*curl_easy_setopt_f)(c, CURLOPT_PROGRESSDATA, this); + if(cRet != CURLE_OK) { + Error("Failed setting libcurl progress callback object: %s", (*curl_easy_strerror_f)(cRet)); + tRet = -59; + return (void*)tRet; + } /* Set username and password */ if(!mUser.empty()) { - cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str()); + cRet = (*curl_easy_setopt_f)(c, CURLOPT_USERNAME, mUser.c_str()); if(cRet != CURLE_OK) - Error("Failed setting username: %s", curl_easy_strerror(cRet)); + Error("Failed setting username: %s", (*curl_easy_strerror_f)(cRet)); } if(!mPass.empty()) { - cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str()); + cRet = (*curl_easy_setopt_f)(c, CURLOPT_PASSWORD, mPass.c_str()); if(cRet != CURLE_OK) - Error("Failed setting password: %s", curl_easy_strerror(cRet)); + Error("Failed setting password: %s", (*curl_easy_strerror_f)(cRet)); } /* Authenication preference */ - cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + cRet = (*curl_easy_setopt_f)(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY); if(cRet != CURLE_OK) - Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet)); + Warning("Failed setting libcurl acceptable http authenication methods: %s", (*curl_easy_strerror_f)(cRet)); /* Work loop */ @@ -431,14 +507,14 @@ void* cURLCamera::thread_func() { tRet = 0; while(!bTerminate) { /* Do the work */ - cRet = curl_easy_perform(c); + cRet = (*curl_easy_perform_f)(c); if(mode == MODE_SINGLE) { if(cRet != CURLE_OK) { break; } /* Attempt to get the size of the file */ - cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize); + cRet = (*curl_easy_getinfo_f)(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize); if(cRet != CURLE_OK) { break; } @@ -448,12 +524,16 @@ void* cURLCamera::thread_func() { if(dSize > 0) { single_offsets.push_back(dSize); } else { - Fatal("Unable to get the size of the image"); + Error("Unable to get the size of the image"); + tRet = -60; + return (void*)tRet; } /* Signal the request complete condition variable */ tRet = pthread_cond_signal(&request_complete_cond); if(tRet != 0) { Error("Failed signaling request completed condition variable: %s",strerror(tRet)); + tRet = -61; + return (void*)tRet; } /* Unlock */ unlock(); @@ -469,7 +549,7 @@ void* cURLCamera::thread_func() { break; } else if (cRet != CURLE_OK) { /* Some error */ - Error("cURL Request failed: %s",curl_easy_strerror(cRet)); + Error("cURL Request failed: %s",(*curl_easy_strerror_f)(cRet)); if(attempt < CURL_MAXRETRY) { Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY); /* Do a reset */ @@ -485,7 +565,7 @@ void* cURLCamera::thread_func() { } /* Cleanup */ - curl_easy_cleanup(c); + (*curl_easy_cleanup_f)(c); c = NULL; return (void*)tRet; diff --git a/src/zm_db.cpp b/src/zm_db.cpp index 77c149f03..8e6258b0b 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -40,7 +40,7 @@ bool zmDbConnect() { Error("Can't initialise database connection: %s", mysql_error(&dbconn)); return false; } - my_bool reconnect = 1; + bool reconnect = 1; if ( mysql_options(&dbconn, MYSQL_OPT_RECONNECT, &reconnect) ) Error("Can't set database auto reconnect option: %s", mysql_error(&dbconn)); if ( !staticConfig.DB_SSL_CA_CERT.empty() ) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 479ded301..2cc133f76 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -113,6 +113,23 @@ Event::Event( return; } id = mysql_insert_id(&dbconn); + // + + /* Update event record with DefaultVideo name if possible so image.php can extract frames + if needed, while recording is in progress */ + if ( monitor->GetOptVideoWriter() != 0 ) { + video_name[0] = 0; + snprintf(video_name, sizeof(video_name), "%" PRIu64 "-%s", id, "video.mp4"); + Debug(1, "Updating inserted event with DefaultVideo=%s",video_name); + snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name,id); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql); + db_mutex.unlock(); + return; + } + } else { + Debug (1, "GetOptVideoWriter() returned 0, not updating DefaultVideo"); + } db_mutex.unlock(); if ( untimedEvent ) { Warning("Event %d has zero time, setting to current", id); diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 034ff778a..c65bf7445 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -76,7 +76,7 @@ bool EventStream::loadInitialEventData(int monitor_id, time_t event_time) { curr_frame_id = 1; // curr_frame_id is 1-based if ( event_time >= event_data->start_time ) { Debug(2, "event time is after event start"); - for (unsigned int i = 0; i < event_data->frame_count; i++ ) { + for ( unsigned int i = 0; i < event_data->frame_count; i++ ) { //Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time ); if ( event_data->frames[i].timestamp >= event_time ) { curr_frame_id = i+1; @@ -100,6 +100,7 @@ bool EventStream::loadInitialEventData(uint64_t init_event_id, unsigned int init if ( init_frame_id >= event_data->frame_count ) { Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count); curr_stream_time = event_data->start_time; + curr_frame_id = 1; } else { curr_stream_time = event_data->frames[init_frame_id-1].timestamp; curr_frame_id = init_frame_id; @@ -163,7 +164,22 @@ bool EventStream::loadEventData(uint64_t event_id) { event_data->Orientation = (Monitor::Orientation)(dbrow[8] == NULL ? 0 : atoi(dbrow[8])); mysql_free_result(result); - Storage * storage = new Storage(event_data->storage_id); + if ( !monitor ) { + monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY); + } else if ( monitor->Id() != event_data->monitor_id ) { + delete monitor; + monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY); + } + if ( !monitor ) { + Fatal("Unable to load monitor id %d for streaming", event_data->monitor_id); + } + + if ( !storage ) { + storage = new Storage(event_data->storage_id); + } else if ( storage->Id() != event_data->storage_id ) { + delete storage; + storage = new Storage(event_data->storage_id); + } const char *storage_path = storage->Path(); if ( event_data->scheme == Storage::DEEP ) { @@ -205,7 +221,6 @@ bool EventStream::loadEventData(uint64_t event_id) { staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id); } - delete storage; storage = NULL; updateFrameRate((double)event_data->frame_count/event_data->duration); Debug(3, "fps set by frame_count(%d)/duration(%f)", @@ -245,7 +260,7 @@ bool EventStream::loadEventData(uint64_t event_id) { event_data->frames[i-1].timestamp = last_timestamp + ((i-last_id)*frame_delta); event_data->frames[i-1].offset = event_data->frames[i-1].timestamp - event_data->start_time; event_data->frames[i-1].in_db = false; - Debug(3,"Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)", + Debug(3, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)", i, event_data->frames[i-1].timestamp, event_data->frames[i-1].offset, @@ -261,7 +276,7 @@ bool EventStream::loadEventData(uint64_t event_id) { last_id = id; last_delta = delta; last_timestamp = event_data->frames[id-1].timestamp; - Debug(4, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)", + Debug(3, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)", id, event_data->frames[id-1].timestamp, event_data->frames[id-1].offset, @@ -276,7 +291,10 @@ bool EventStream::loadEventData(uint64_t event_id) { mysql_free_result(result); - if ( event_data->video_file[0] ) { + if ( event_data->video_file[0] || (monitor->GetOptVideoWriter() > 0) ) { + if ( !event_data->video_file[0] ) { + snprintf(event_data->video_file, sizeof(event_data->video_file), "%" PRIu64 "-%s", event_data->event_id, "video.mp4"); + } std::string filepath = std::string(event_data->path) + "/" + std::string(event_data->video_file); //char filepath[PATH_MAX]; //snprintf(filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file); @@ -304,7 +322,7 @@ bool EventStream::loadEventData(uint64_t event_id) { void EventStream::processCommand(const CmdMsg *msg) { Debug(2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0]); // Check for incoming command - switch( (MsgCommand)msg->msg_data[0] ) { + switch ( (MsgCommand)msg->msg_data[0] ) { case CMD_PAUSE : Debug(1, "Got PAUSE command"); @@ -373,16 +391,20 @@ void EventStream::processCommand(const CmdMsg *msg) { } break; case CMD_SLOWFWD : - Debug(1, "Got SLOW FWD command"); paused = true; replay_rate = ZM_RATE_BASE; step = 1; + if ( (unsigned int)curr_frame_id < event_data->frame_count ) + curr_frame_id += 1; + Debug(1, "Got SLOWFWD command new frame id %d", curr_frame_id); break; case CMD_SLOWREV : - Debug(1, "Got SLOW REV command"); paused = true; replay_rate = ZM_RATE_BASE; step = -1; + curr_frame_id -= 1; + if ( curr_frame_id < 1 ) curr_frame_id = 1; + Debug(1, "Got SLOWREV command new frame id %d", curr_frame_id); break; case CMD_FASTREV : Debug(1, "Got FAST REV command"); @@ -499,6 +521,7 @@ void EventStream::processCommand(const CmdMsg *msg) { // Do nothing, for now break; } + struct { uint64_t event_id; int progress; @@ -536,9 +559,9 @@ void EventStream::processCommand(const CmdMsg *msg) { exit(0); updateFrameRate((double)event_data->frame_count/event_data->duration); -} // void EventStream::processCommand(const CmdMsg *msg) +} // void EventStream::processCommand(const CmdMsg *msg) -void EventStream::checkEventLoaded() { +bool EventStream::checkEventLoaded() { static char sql[ZM_SQL_SML_BUFSIZ]; if ( curr_frame_id <= 0 ) { @@ -553,7 +576,7 @@ void EventStream::checkEventLoaded() { // No event change required Debug(3, "No event change required, as curr frame %d <=> event frames %d", curr_frame_id, event_data->frame_count); - return; + return false; } // Event change required. @@ -581,12 +604,12 @@ void EventStream::checkEventLoaded() { loadEventData(event_id); - Debug(2, "Current frame id = %d", curr_frame_id); if ( replay_rate < 0 ) // rewind curr_frame_id = event_data->frame_count; else curr_frame_id = 1; Debug(2, "New frame id = %d", curr_frame_id); + return true; } else { Debug(2, "No next event loaded using %s. Pausing", sql); if ( curr_frame_id <= 0 ) @@ -595,7 +618,7 @@ void EventStream::checkEventLoaded() { curr_frame_id = event_data->frame_count; paused = true; sendTextFrame("No more event data found"); - } // end if found a new event or not + } // end if found a new event or not mysql_free_result(result); forceEventChange = false; } else { @@ -606,7 +629,8 @@ void EventStream::checkEventLoaded() { curr_frame_id = event_data->frame_count; paused = true; } -} // void EventStream::checkEventLoaded() + return false; +} // void EventStream::checkEventLoaded() Image * EventStream::getImage( ) { static char filepath[PATH_MAX]; @@ -651,7 +675,8 @@ bool EventStream::sendFrame(int delta_us) { Image *send_image = prepareImage(&image); if ( !vid_stream ) { - vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height()); + vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, + send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height()); fprintf(stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType()); vid_stream->OpenStream(); } @@ -694,9 +719,10 @@ Debug(1, "Loading image"); } else if ( ffmpeg_input ) { // Get the frame from the mp4 input Debug(1,"Getting frame from ffmpeg"); - AVFrame *frame; FrameData *frame_data = &event_data->frames[curr_frame_id-1]; - frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id(), frame_data->offset ); + AVFrame *frame = ffmpeg_input->get_frame( + ffmpeg_input->get_video_stream_id(), + frame_data->offset); if ( frame ) { image = new Image(frame); //av_frame_free(&frame); @@ -761,6 +787,10 @@ Debug(1, "Loading image"); Fatal("Unexpected frame type %d", type); break; } + if ( send_image != image ) { + delete send_image; + send_image = NULL; + } delete image; image = NULL; } // end if send_raw or not @@ -834,6 +864,10 @@ void EventStream::runStream() { updateFrameRate((double)event_data->frame_count/event_data->duration); gettimeofday(&start, NULL); uint64_t start_usec = start.tv_sec * 1000000 + start.tv_usec; + uint64_t last_frame_offset = 0; + + bool in_event = true; + double time_to_event = 0; while ( !zm_terminate ) { gettimeofday(&now, NULL); @@ -845,108 +879,76 @@ void EventStream::runStream() { // commands may set send_frame to true while ( checkCommandQueue() && !zm_terminate ) { // The idea is to loop here processing all commands before proceeding. - Debug(1, "Have command queue"); } - Debug(2, "Done command queue"); // Update modified time of the socket .lock file so that we can tell which ones are stale. if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) { touch(sock_path_lock); last_comm_update = now; } - } else { - Debug(2, "Not checking command queue"); } - // Get current frame data FrameData *frame_data = &event_data->frames[curr_frame_id-1]; - //Info( "cst:%.2f", curr_stream_time ); - //Info( "cfid:%d", curr_frame_id ); - //Info( "fdt:%d", frame_data->timestamp ); if ( !paused ) { - Debug(3, "Not paused at frame %d", curr_frame_id); - - // This next bit is to determine if we are in the current event time wise - // and whether to show an image saying how long until the next event. - bool in_event = true; - double time_to_event = 0; - if ( replay_rate > 0 ) { - time_to_event = event_data->frames[0].timestamp - curr_stream_time; - if ( time_to_event > 0 ) - in_event = false; - } else if ( replay_rate < 0 ) { - time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; - if ( time_to_event > 0 ) - in_event = false; - } - Debug(1, "replay rate(%d) in_event(%d) time_to_event(%f)=curr_stream_time(%f)-frame timestamp:%f", - replay_rate, in_event, time_to_event, curr_stream_time, event_data->frames[event_data->frame_count-1].timestamp); - if ( !in_event ) { - double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; - Debug(1, "Ctual delta time = %f = %f - %f", actual_delta_time , TV_2_FLOAT(now) , last_frame_sent); - // > 1 second - if ( actual_delta_time > 1 ) { - Debug(1, "Sending time to next event frame"); - static char frame_text[64]; - snprintf(frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event); - if ( !sendTextFrame(frame_text) ) - zm_terminate = true; - } else { - Debug(1, "Not Sending time to next event frame because actual delta time is %f", actual_delta_time); - } - //else - //{ - // FIXME ICON But we are not paused. We are somehow still in the event? - double sleep_time = (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); - //double sleep_time = (replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); - //// ZM_RATE_BASE == 100, and 1x replay_rate is 100 - //double sleep_time = ((replay_rate/ZM_RATE_BASE) * STREAM_PAUSE_WAIT)/1000000; - if ( ! sleep_time ) { - sleep_time += STREAM_PAUSE_WAIT/1000000; - } - curr_stream_time += sleep_time; - Debug(2, "Sleeping (%dus) because we are not at the next event yet, adding %f", STREAM_PAUSE_WAIT, sleep_time); - usleep(STREAM_PAUSE_WAIT); - - //curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); - //} - continue; - } // end if !in_event - // Figure out if we should send this frame - Debug(3, "cur_frame_id (%d-1) mod frame_mod(%d)", curr_frame_id, frame_mod); + Debug(3, "not paused at cur_frame_id (%d-1) mod frame_mod(%d)", curr_frame_id, frame_mod); // If we are streaming and this frame is due to be sent // frame mod defaults to 1 and if we are going faster than max_fps will get multiplied by 2 // so if it is 2, then we send every other frame, if is it 4 then every fourth frame, etc. + if ( (frame_mod == 1) || (((curr_frame_id-1)%frame_mod) == 0) ) { - delta_us = (unsigned int)(frame_data->delta * 1000000); - Debug(3, "frame delta %uus ", delta_us); - // if effective > base we should speed up frame delivery - delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); - Debug(3, "delta %u = base_fps(%f)/effective fps(%f)", delta_us, base_fps, effective_fps); - // but must not exceed maxfps - delta_us = max(delta_us, 1000000 / maxfps); - Debug(3, "delta %u = base_fps(%f)/effective fps(%f) from 30fps", delta_us, base_fps, effective_fps); send_frame = true; } } else if ( step != 0 ) { - Debug(2, "Paused with step"); + Debug(2, "Paused with step %d", step); // We are paused and are just stepping forward or backward one frame step = 0; send_frame = true; } else if ( !send_frame ) { - // We are paused, not stepping and doing nothing + // We are paused, not stepping and doing nothing, meaning that comms didn't set send_frame to true double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; if ( actual_delta_time > MAX_STREAM_DELAY ) { // Send keepalive Debug(2, "Sending keepalive frame"); send_frame = true; - //} else { - //Debug(2, "Not Sending keepalive frame"); } - } // end if streaming stepping or doing nothing + } // end if streaming stepping or doing nothing + + // time_to_event > 0 means that we are not in the event + if ( time_to_event > 0 ) { + double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; + Debug(1, "Actual delta time = %f = %f - %f", actual_delta_time, TV_2_FLOAT(now), last_frame_sent); + // > 1 second + if ( actual_delta_time > 1 ) { + Debug(1, "Sending time to next event frame"); + static char frame_text[64]; + snprintf(frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event); + if ( !sendTextFrame(frame_text) ) + zm_terminate = true; + } else { + Debug(1, "Not Sending time to next event frame because actual delta time is %f", actual_delta_time); + } + //else + //{ + // FIXME ICON But we are not paused. We are somehow still in the event? + double sleep_time = (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); + //double sleep_time = (replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); + //// ZM_RATE_BASE == 100, and 1x replay_rate is 100 + //double sleep_time = ((replay_rate/ZM_RATE_BASE) * STREAM_PAUSE_WAIT)/1000000; + if ( !sleep_time ) { + sleep_time += STREAM_PAUSE_WAIT/1000000; + } + curr_stream_time += sleep_time; + time_to_event -= sleep_time; + Debug(2, "Sleeping (%dus) because we are not at the next event yet, adding %f", STREAM_PAUSE_WAIT, sleep_time); + usleep(STREAM_PAUSE_WAIT); + + //curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); + //} + continue; + } // end if !in_event if ( send_frame ) { if ( !sendFrame(delta_us) ) { @@ -958,6 +960,17 @@ void EventStream::runStream() { curr_stream_time = frame_data->timestamp; if ( !paused ) { + + // delta is since the last frame + delta_us = (unsigned int)(frame_data->delta * 1000000); + Debug(3, "frame delta %uus ", delta_us); + // if effective > base we should speed up frame delivery + delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); + Debug(3, "delta %u = base_fps(%f)/effective fps(%f)", delta_us, base_fps, effective_fps); + // but must not exceed maxfps + delta_us = max(delta_us, 1000000/maxfps); + Debug(3, "delta %u = base_fps(%f)/effective fps(%f) from 30fps", delta_us, base_fps, effective_fps); + // +/- 1? What if we are skipping frames? curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod; // sending the frame may have taken some time, so reload now @@ -965,7 +978,12 @@ void EventStream::runStream() { uint64_t now_usec = (now.tv_sec * 1000000 + now.tv_usec); // we incremented by replay_rate, so might have jumped past frame_count - if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id >= event_data->frame_count) ) { + if ( (mode == MODE_SINGLE) && ( + (curr_frame_id < 1 ) + || + ((unsigned int)curr_frame_id >= event_data->frame_count) + ) + ) { Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start"); curr_frame_id = 1; // Have to reset start_usec to now when replaying @@ -980,9 +998,21 @@ void EventStream::runStream() { // There are two ways to go about this, not sure which is correct. // you can calculate the relationship between now and the start // or calc the relationship from the last frame. I think from the start is better as it self-corrects + // + if ( last_frame_offset ) { + // We assume that we are going forward and the next frame is in the future. + delta_us = frame_data->offset * 1000000 - (now_usec-start_usec); + // - (now_usec - start_usec); + Debug(2, "New delta_us now %" PRIu64 " - start %" PRIu64 " = %d offset %" PRId64 " - elapsed = %dusec", + now_usec, start_usec, now_usec-start_usec, frame_data->offset * 1000000, delta_us); + } else { + Debug(2, "No last frame_offset, no sleep"); + delta_us = 0; + } + last_frame_offset = frame_data->offset * 1000000; - if ( send_frame && type != STREAM_MPEG ) { - if ( delta_us > 0) { + if ( send_frame && (type != STREAM_MPEG) ) { + if ( delta_us > 0 ) { if ( delta_us > MAX_SLEEP_USEC ) { Debug(1, "Limiting sleep to %d because calculated sleep is too long %d", MAX_SLEEP_USEC, delta_us); delta_us = MAX_SLEEP_USEC; @@ -1007,15 +1037,34 @@ void EventStream::runStream() { } usleep(delta_us); } - } // end if !paused - - //if ( step != 0 )// Adding 0 is cheaper than an if 0 - // curr_frame_id starts at 1 though, so we might skip the first frame? + // We are paused, so might be stepping + //if ( step != 0 )// Adding 0 is cheaper than an if 0 + // curr_frame_id starts at 1 though, so we might skip the first frame? curr_frame_id += step; - // Detects when we hit end of event and will load the next event or previous event - checkEventLoaded(); - } // end while ! zm_terminate + // Detects when we hit end of event and will load the next event or previous event + if ( checkEventLoaded() ) { + // Have change of event + + // This next bit is to determine if we are in the current event time wise + // and whether to show an image saying how long until the next event. + if ( replay_rate > 0 ) { + // This doesn't make sense unless we have hit the end of the event. + time_to_event = event_data->frames[0].timestamp - curr_stream_time; + Debug(1, "replay rate(%d) time_to_event(%f)=frame timestamp:%f - curr_stream_time(%f)", + replay_rate, time_to_event, + event_data->frames[0].timestamp, + curr_stream_time); + + } else if ( replay_rate < 0 ) { + time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; + Debug(1, "replay rate(%d) time_to_event(%f)=curr_stream_time(%f)-frame timestamp:%f", + replay_rate, time_to_event, curr_stream_time, event_data->frames[event_data->frame_count-1].timestamp); + } // end if forward or reverse + + } // end if checkEventLoaded + } // end if !paused + } // end while ! zm_terminate #if HAVE_LIBAVCODEC if ( type == STREAM_MPEG ) delete vid_stream; @@ -1024,18 +1073,12 @@ void EventStream::runStream() { closeComms(); } // void EventStream::runStream() -void EventStream::setStreamStart( uint64_t init_event_id, unsigned int init_frame_id=0 ) { +void EventStream::setStreamStart( + uint64_t init_event_id, + unsigned int init_frame_id=0) { loadInitialEventData(init_event_id, init_frame_id); - if ( !(monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY)) ) { - Fatal("Unable to load monitor id %d for streaming", event_data->monitor_id); - return; - } -} +} // end void EventStream::setStreamStart(init_event_id,init_frame_id=0) void EventStream::setStreamStart(int monitor_id, time_t event_time) { loadInitialEventData(monitor_id, event_time); - if ( !(monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY)) ) { - Fatal("Unable to load monitor id %d for streaming", monitor_id); - return; - } } diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index d0b5827e7..a482483b4 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -20,9 +20,6 @@ #ifndef ZM_EVENTSTREAM_H #define ZM_EVENTSTREAM_H -#include -#include - #include "zm_image.h" #include "zm_stream.h" #include "zm_video.h" @@ -83,35 +80,51 @@ class EventStream : public StreamBase { struct timeval start; // clock time when started the event EventData *event_data; - FFmpeg_Input *ffmpeg_input; protected: bool loadEventData( uint64_t event_id ); bool loadInitialEventData( uint64_t init_event_id, unsigned int init_frame_id ); bool loadInitialEventData( int monitor_id, time_t event_time ); - void checkEventLoaded(); + bool checkEventLoaded(); void processCommand( const CmdMsg *msg ); bool sendFrame( int delta_us ); public: - EventStream() { - mode = DEFAULT_MODE; - replay_rate = DEFAULT_RATE; - - forceEventChange = false; - - curr_frame_id = 0; - curr_stream_time = 0.0; - send_frame = false; - - event_data = 0; - + EventStream() : + mode(DEFAULT_MODE), + forceEventChange(false), + curr_frame_id(0), + curr_stream_time(0.0), + send_frame(false), + event_data(0), + storage(NULL), + ffmpeg_input(NULL), // Used when loading frames from an mp4 - input_codec_context = 0; - input_codec = 0; - - ffmpeg_input = NULL; + input_codec_context(0), + input_codec(0) + {} + ~EventStream() { + if ( event_data ) { + if ( event_data->frames ) { + delete[] event_data->frames; + event_data->frames = NULL; + } + delete event_data; + event_data = NULL; + } + if ( monitor ) { + delete monitor; + monitor = NULL; + } + if ( storage ) { + delete storage; + storage = NULL; + } + if ( ffmpeg_input ) { + delete ffmpeg_input; + ffmpeg_input = NULL; + } } void setStreamStart( uint64_t init_event_id, unsigned int init_frame_id ); void setStreamStart( int monitor_id, time_t event_time ); @@ -121,8 +134,10 @@ class EventStream : public StreamBase { void runStream(); Image *getImage(); private: - AVCodecContext *input_codec_context; - AVCodec *input_codec; + Storage *storage; + FFmpeg_Input *ffmpeg_input; + AVCodecContext *input_codec_context; + AVCodec *input_codec; }; #endif // ZM_EVENTSTREAM_H diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index fbb1ab29f..e10588830 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -559,6 +559,8 @@ int zm_send_packet_receive_frame( } return ret; } + // In this api the packet is always consumed, so return packet.bytes + return packet.size; # else int frameComplete = 0; while ( !frameComplete ) { @@ -572,8 +574,8 @@ int zm_send_packet_receive_frame( return ret; } } // end while !frameComplete + return ret; #endif - return 0; } // end int zm_send_packet_receive_frame(AVCodecContext *context, AVFrame *frame, AVPacket &packet) /* Returns < 0 on error, 0 if codec not ready, 1 on success diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index d2af4d3f7..89ca587bd 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -321,6 +321,7 @@ void zm_dump_codecpar(const AVCodecParameters *par); frame->pts \ ); +#if LIBAVUTIL_VERSION_CHECK(54, 4, 0, 74, 100) #define zm_dump_video_frame(frame,text) Debug(1, "%s: format %d %s %dx%d linesize:%dx%d pts: %" PRId64, \ text, \ frame->format, \ @@ -331,6 +332,18 @@ void zm_dump_codecpar(const AVCodecParameters *par); frame->pts \ ); +#else +#define zm_dump_video_frame(frame,text) Debug(1, "%s: format %d %s %dx%d linesize:%dx%d pts: %" PRId64, \ + text, \ + frame->format, \ + "unsupported", \ + frame->width, \ + frame->height, \ + frame->linesize[0], frame->linesize[1], \ + frame->pts \ + ); +#endif + #if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) #define zm_av_packet_unref(packet) av_packet_unref(packet) #define zm_av_packet_ref(dst, src) av_packet_ref(dst, src) diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index 1def60346..980c2e50b 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -18,12 +18,31 @@ FFmpeg_Input::~FFmpeg_Input() { Close(); } if ( streams ) { - delete streams; + for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) { + avcodec_close(streams[i].context); + streams[i].context = NULL; + } + delete[] streams; streams = NULL; } -} + if ( frame ) { + av_frame_free(&frame); + frame = NULL; + } + if ( input_format_context ) { +#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0) + av_close_input_file(input_format_context); +#else + avformat_close_input(&input_format_context); +#endif + input_format_context = NULL; + } +} // end ~FFmpeg_Input() -int FFmpeg_Input::Open( AVStream * video_in_stream, AVStream * audio_in_stream ) { +int FFmpeg_Input::Open( + const AVStream * video_in_stream, + const AVStream * audio_in_stream + ) { video_stream_id = video_in_stream->index; int max_stream_index = video_in_stream->index; @@ -104,6 +123,7 @@ int FFmpeg_Input::Open(const char *filepath) { avcodec_free_context(&streams[i].context); #endif avformat_close_input(&input_format_context); + input_format_context = NULL; return error; } } // end foreach stream @@ -139,7 +159,6 @@ int FFmpeg_Input::Close( ) { } // end int FFmpeg_Input::Close() AVFrame *FFmpeg_Input::get_frame(int stream_id) { - int frameComplete = false; AVPacket packet; av_init_packet(&packet); @@ -178,13 +197,17 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) { } ret = zm_send_packet_receive_frame(context, frame, packet); if ( ret < 0 ) { - Error("Unable to decode frame at frame %d: %s, continuing", - streams[packet.stream_index].frame_count, av_make_error_string(ret).c_str()); + Error("Unable to decode frame at frame %d: %d %s, continuing", + streams[packet.stream_index].frame_count, ret, av_make_error_string(ret).c_str()); zm_av_packet_unref(&packet); av_frame_free(&frame); continue; } else { - zm_dump_frame(frame, "resulting frame"); + if ( is_video_stream(input_format_context->streams[packet.stream_index]) ) { + zm_dump_video_frame(frame, "resulting video frame"); + } else { + zm_dump_frame(frame, "resulting frame"); + } } frameComplete = 1; @@ -244,15 +267,17 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) { last_seek_request = seek_target; // Seeking seems to typically seek to a keyframe, so then we have to decode until we get the frame we want. - if ( frame->pts <= seek_target ) { + if ( frame->pts <= seek_target ) { zm_dump_frame(frame, "pts <= seek_target"); while ( frame && (frame->pts < seek_target) ) { - if ( !get_frame(stream_id) ) + if ( !get_frame(stream_id) ) { + Warning("Got no frame. returning nothing"); return frame; + } } + zm_dump_frame(frame, "frame->pts <= seek_target, got"); return frame; } return get_frame(stream_id); - -} // end AVFrame *FFmpeg_Input::get_frame( int stream_id, struct timeval at) +} // end AVFrame *FFmpeg_Input::get_frame( int stream_id, struct timeval at) diff --git a/src/zm_ffmpeg_input.h b/src/zm_ffmpeg_input.h index 900f14d4a..41dc03008 100644 --- a/src/zm_ffmpeg_input.h +++ b/src/zm_ffmpeg_input.h @@ -20,6 +20,7 @@ class FFmpeg_Input { ~FFmpeg_Input(); int Open( const char *filename ); + int Open( const AVStream *, const AVStream * ); int Close(); AVFrame *get_frame( int stream_id=-1 ); AVFrame *get_frame( int stream_id, double at ); diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 85f9ba68b..7a788d1d7 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -48,6 +48,8 @@ static short *g_v_table; static short *g_u_table; static short *b_u_table; +struct SwsContext *sws_convert_context = NULL; + jpeg_compress_struct *Image::writejpg_ccinfo[101] = { 0 }; jpeg_compress_struct *Image::encodejpg_ccinfo[101] = { 0 }; jpeg_decompress_struct *Image::readjpg_dcinfo = 0; @@ -160,7 +162,8 @@ Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uin update_function_pointers(); } -Image::Image( const AVFrame *frame ) { +Image::Image(const AVFrame *frame) { + AVFrame *dest_frame = zm_av_frame_alloc(); text[0] = '\0'; width = frame->width; height = frame->height; @@ -191,28 +194,28 @@ void Image::Assign( const AVFrame *frame ) { #endif #if HAVE_LIBSWSCALE - struct SwsContext *mConvertContext = sws_getContext( + sws_convert_context = sws_getCachedContext( + sws_convert_context, width, height, (AVPixelFormat)frame->format, width, height, format, SWS_BICUBIC, NULL, NULL, NULL); - if ( mConvertContext == NULL ) - Fatal( "Unable to create conversion context" ); + if ( sws_convert_context == NULL ) + Fatal("Unable to create conversion context"); - if ( sws_scale(mConvertContext, frame->data, frame->linesize, 0, frame->height, dest_frame->data, dest_frame->linesize) < 0 ) - Fatal("Unable to convert raw format %u to target format %u", frame->format, format); + if ( sws_scale(sws_convert_context, frame->data, frame->linesize, 0, frame->height, + dest_frame->data, dest_frame->linesize) < 0 ) + Fatal("Unable to convert raw format %u to target format %u", frame->format, AV_PIX_FMT_RGBA); #else // HAVE_LIBSWSCALE Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras"); #endif // HAVE_LIBSWSCALE - av_frame_free( &dest_frame ); - sws_freeContext(mConvertContext); - mConvertContext = NULL; + av_frame_free(&dest_frame); update_function_pointers(); -} // end Image::Image( const AVFrame *frame ) +} // end Image::Image(const AVFrame *frame) -Image::Image( const Image &p_image ) { +Image::Image(const Image &p_image) { if ( !initialised ) Initialise(); width = p_image.width; @@ -225,7 +228,7 @@ Image::Image( const Image &p_image ) { holdbuffer = 0; AllocImgBuffer(size); (*fptr_imgbufcpy)(buffer, p_image.buffer, size); - strncpy( text, p_image.text, sizeof(text) ); + strncpy(text, p_image.text, sizeof(text)); update_function_pointers(); } @@ -235,35 +238,39 @@ Image::~Image() { /* Should be called as part of program shutdown to free everything */ void Image::Deinitialise() { - if ( initialised ) { - /* - delete[] y_table; - delete[] uv_table; - delete[] r_v_table; - delete[] g_v_table; - delete[] g_u_table; - delete[] b_u_table; + if ( !initialised ) return; + /* + delete[] y_table; + delete[] uv_table; + delete[] r_v_table; + delete[] g_v_table; + delete[] g_u_table; + delete[] b_u_table; */ - initialised = false; - if ( readjpg_dcinfo ) { - jpeg_destroy_decompress( readjpg_dcinfo ); - delete readjpg_dcinfo; - readjpg_dcinfo = 0; - } - if ( decodejpg_dcinfo ) { - jpeg_destroy_decompress( decodejpg_dcinfo ); - delete decodejpg_dcinfo; - decodejpg_dcinfo = 0; - } - for ( unsigned int quality=0; quality <= 100; quality += 1 ) { - if ( writejpg_ccinfo[quality] ) { - jpeg_destroy_compress( writejpg_ccinfo[quality] ); - delete writejpg_ccinfo[quality]; - writejpg_ccinfo[quality] = NULL; - } - } // end foreach quality + initialised = false; + if ( readjpg_dcinfo ) { + jpeg_destroy_decompress( readjpg_dcinfo ); + delete readjpg_dcinfo; + readjpg_dcinfo = 0; } -} + if ( decodejpg_dcinfo ) { + jpeg_destroy_decompress( decodejpg_dcinfo ); + delete decodejpg_dcinfo; + decodejpg_dcinfo = 0; + } + for ( unsigned int quality=0; quality <= 100; quality += 1 ) { + if ( writejpg_ccinfo[quality] ) { + jpeg_destroy_compress( writejpg_ccinfo[quality] ); + delete writejpg_ccinfo[quality]; + writejpg_ccinfo[quality] = NULL; + } + } // end foreach quality + + if ( sws_convert_context ) { + sws_freeContext(sws_convert_context); + sws_convert_context = NULL; + } +} // end void Image::Deinitialise() void Image::Initialise() { /* Assign the blend pointer to function */ @@ -667,7 +674,8 @@ void Image::Assign( const Image &image ) { return; } - if ( !buffer || image.width != width || image.height != height || image.colours != colours || image.subpixelorder != subpixelorder ) { + if ( !buffer || image.width != width || image.height != height + || image.colours != colours || image.subpixelorder != subpixelorder) { if ( holdbuffer && buffer ) { if ( new_size > allocation ) { diff --git a/src/zm_image.h b/src/zm_image.h index 5fb151a29..5ac5db525 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -122,7 +122,7 @@ protected: }; inline void DumpImgBuffer() { - DumpBuffer(buffer,buffertype); + DumpBuffer(buffer, buffertype); buffer = NULL; allocation = 0; } diff --git a/src/zm_libvlc_camera.cpp b/src/zm_libvlc_camera.cpp index a17b511c9..65cf2324c 100644 --- a/src/zm_libvlc_camera.cpp +++ b/src/zm_libvlc_camera.cpp @@ -17,12 +17,51 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include "zm.h" #include "zm_signal.h" #include "zm_libvlc_camera.h" #if HAVE_LIBVLC +static void *libvlc_lib = nullptr; +static void (*libvlc_media_player_release_f)(libvlc_media_player_t* ) = nullptr; +static void (*libvlc_media_release_f)(libvlc_media_t* ) = nullptr; +static void (*libvlc_release_f)(libvlc_instance_t* ) = nullptr; +static void (*libvlc_media_player_stop_f)(libvlc_media_player_t* ) = nullptr; +static libvlc_instance_t* (*libvlc_new_f)(int, const char* const *) = nullptr; +static void (*libvlc_log_set_f)(libvlc_instance_t*, libvlc_log_cb, void *) = nullptr; +static libvlc_media_t* (*libvlc_media_new_location_f)(libvlc_instance_t*, const char*) = nullptr; +static libvlc_media_player_t* (*libvlc_media_player_new_from_media_f)(libvlc_media_t*) = nullptr; +static void (*libvlc_video_set_format_f)(libvlc_media_player_t*, const char*, unsigned, unsigned, unsigned) = nullptr; +static void (*libvlc_video_set_callbacks_f)(libvlc_media_player_t*, libvlc_video_lock_cb, libvlc_video_unlock_cb, libvlc_video_display_cb, void*) = nullptr; +static int (*libvlc_media_player_play_f)(libvlc_media_player_t *) = nullptr; +static const char* (*libvlc_errmsg_f)(void) = nullptr; +static const char* (*libvlc_get_version_f)(void) = nullptr; +void bind_libvlc_symbols() { + if(libvlc_lib != nullptr) // Safe-check + return; + + libvlc_lib = dlopen("libvlc.so", RTLD_LAZY | RTLD_GLOBAL); + if(!libvlc_lib){ + Error("Error loading libvlc: %s", dlerror()); + return; + } + + *(void**) (&libvlc_media_player_release_f) = dlsym(libvlc_lib, "libvlc_media_player_release"); + *(void**) (&libvlc_media_release_f) = dlsym(libvlc_lib, "libvlc_media_release"); + *(void**) (&libvlc_release_f) = dlsym(libvlc_lib, "libvlc_release"); + *(void**) (&libvlc_media_player_stop_f) = dlsym(libvlc_lib, "libvlc_media_player_stop"); + *(void**) (&libvlc_new_f) = dlsym(libvlc_lib, "libvlc_new"); + *(void**) (&libvlc_log_set_f) = dlsym(libvlc_lib, "libvlc_log_set"); + *(void**) (&libvlc_media_new_location_f) = dlsym(libvlc_lib, "libvlc_media_new_location"); + *(void**) (&libvlc_media_player_new_from_media_f) = dlsym(libvlc_lib, "libvlc_media_player_new_from_media"); + *(void**) (&libvlc_video_set_format_f) = dlsym(libvlc_lib, "libvlc_video_set_format"); + *(void**) (&libvlc_video_set_callbacks_f) = dlsym(libvlc_lib, "libvlc_video_set_callbacks"); + *(void**) (&libvlc_media_player_play_f) = dlsym(libvlc_lib, "libvlc_media_player_play"); + *(void**) (&libvlc_errmsg_f) = dlsym(libvlc_lib, "libvlc_errmsg"); + *(void**) (&libvlc_get_version_f) = dlsym(libvlc_lib, "libvlc_get_version"); +} // Do all the buffer checking work here to avoid unnecessary locking void* LibvlcLockBuffer(void* opaque, void** planes) { LibvlcPrivateData* data = reinterpret_cast(opaque); @@ -125,15 +164,15 @@ LibvlcCamera::~LibvlcCamera() { Terminate(); } if ( mLibvlcMediaPlayer != NULL ) { - libvlc_media_player_release(mLibvlcMediaPlayer); + (*libvlc_media_player_release_f)(mLibvlcMediaPlayer); mLibvlcMediaPlayer = NULL; } if ( mLibvlcMedia != NULL ) { - libvlc_media_release(mLibvlcMedia); + (*libvlc_media_release_f)(mLibvlcMedia); mLibvlcMedia = NULL; } if ( mLibvlcInstance != NULL ) { - libvlc_release(mLibvlcInstance); + (*libvlc_release_f)(mLibvlcInstance); mLibvlcInstance = NULL; } if ( mOptArgV != NULL ) { @@ -142,14 +181,16 @@ LibvlcCamera::~LibvlcCamera() { } void LibvlcCamera::Initialise() { + bind_libvlc_symbols(); } void LibvlcCamera::Terminate() { - libvlc_media_player_stop(mLibvlcMediaPlayer); + (*libvlc_media_player_stop_f)(mLibvlcMediaPlayer); if ( mLibvlcData.buffer ) { zm_freealigned(mLibvlcData.buffer); mLibvlcData.buffer = NULL; } + if ( mLibvlcData.prevBuffer ) { zm_freealigned(mLibvlcData.prevBuffer); mLibvlcData.prevBuffer = NULL; @@ -158,6 +199,7 @@ void LibvlcCamera::Terminate() { int LibvlcCamera::PrimeCapture() { Info("Priming capture from %s", mPath.c_str()); + Info("Libvlc Version %s", (*libvlc_get_version_f)()); StringVector opVect = split(Options(), ","); @@ -181,28 +223,28 @@ int LibvlcCamera::PrimeCapture() { } } - mLibvlcInstance = libvlc_new(opVect.size(), (const char* const*)mOptArgV); + mLibvlcInstance = (*libvlc_new_f)(opVect.size(), (const char* const*)mOptArgV); if ( mLibvlcInstance == NULL ) { - Error("Unable to create libvlc instance due to: %s", libvlc_errmsg()); + Error("Unable to create libvlc instance due to: %s", (*libvlc_errmsg_f)()); return -1; } - libvlc_log_set(mLibvlcInstance, LibvlcCamera::log_callback, NULL); + (*libvlc_log_set_f)(mLibvlcInstance, LibvlcCamera::log_callback, NULL); - mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str()); + mLibvlcMedia = (*libvlc_media_new_location_f)(mLibvlcInstance, mPath.c_str()); if ( mLibvlcMedia == NULL ) { - Error("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg()); + Error("Unable to open input %s due to: %s", mPath.c_str(), (*libvlc_errmsg_f)()); return -1; } - mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia); + mLibvlcMediaPlayer = (*libvlc_media_player_new_from_media_f)(mLibvlcMedia); if ( mLibvlcMediaPlayer == NULL ) { - Error("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg()); + Error("Unable to create player for %s due to: %s", mPath.c_str(), (*libvlc_errmsg_f)()); return -1; } - libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp); - libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData); + (*libvlc_video_set_format_f)(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp); + (*libvlc_video_set_callbacks_f)(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData); mLibvlcData.bufferSize = width * height * mBpp; // Libvlc wants 32 byte alignment for images (should in theory do this for all image lines) @@ -211,7 +253,7 @@ int LibvlcCamera::PrimeCapture() { mLibvlcData.newImage.setValueImmediate(false); - libvlc_media_player_play(mLibvlcMediaPlayer); + (*libvlc_media_player_play_f)(mLibvlcMediaPlayer); return 0; } diff --git a/src/zm_libvnc_camera.cpp b/src/zm_libvnc_camera.cpp new file mode 100644 index 000000000..1f7053112 --- /dev/null +++ b/src/zm_libvnc_camera.cpp @@ -0,0 +1,180 @@ +#include +#include "zm.h" +#include "zm_signal.h" +#include "zm_libvnc_camera.h" +#include "zm_swscale.h" + +#if HAVE_LIBVNC + +static int TAG_0; +static int TAG_1; +static int TAG_2; +static void *libvnc_lib = nullptr; +static void *(*rfbClientGetClientData_f)(rfbClient*, void*) = nullptr; +static rfbClient *(*rfbGetClient_f)(int, int, int) = nullptr; +static void (*rfbClientSetClientData_f)(rfbClient*, void*, void*) = nullptr; +static rfbBool (*rfbInitClient_f)(rfbClient*, int*, char**) = nullptr; +static void (*rfbClientCleanup_f)(rfbClient*) = nullptr; +static int (*WaitForMessage_f)(rfbClient*, unsigned int) = nullptr; +static rfbBool (*HandleRFBServerMessage_f)(rfbClient*) = nullptr; + +void bind_libvnc_symbols() { + if(libvnc_lib != nullptr) // Safe-check + return; + + libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL); + if(!libvnc_lib){ + Error("Error loading libvlc: %s", dlerror()); + return; + } + + *(void**) (&rfbClientGetClientData_f) = dlsym(libvnc_lib, "rfbClientGetClientData"); + *(void**) (&rfbGetClient_f) = dlsym(libvnc_lib, "rfbGetClient"); + *(void**) (&rfbClientSetClientData_f) = dlsym(libvnc_lib, "rfbClientSetClientData"); + *(void**) (&rfbInitClient_f) = dlsym(libvnc_lib, "rfbInitClient"); + *(void**) (&rfbClientCleanup_f) = dlsym(libvnc_lib, "rfbClientCleanup"); + *(void**) (&WaitForMessage_f) = dlsym(libvnc_lib, "WaitForMessage"); + *(void**) (&HandleRFBServerMessage_f) = dlsym(libvnc_lib, "HandleRFBServerMessage"); +} + +static void GotFrameBufferUpdateCallback(rfbClient *rfb, int x, int y, int w, int h){ + VncPrivateData *data = (VncPrivateData *)(*rfbClientGetClientData_f)(rfb, &TAG_0); + data->buffer = rfb->frameBuffer; +} + +static char* GetPasswordCallback(rfbClient* cl){ + return strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_1)); +} + +static rfbCredential* GetCredentialsCallback(rfbClient* cl, int credentialType){ + rfbCredential *c = (rfbCredential *)malloc(sizeof(rfbCredential)); + if ( credentialType != rfbCredentialTypeUser ) { + free(c); + return NULL; + } + + c->userCredential.password = strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_1)); + c->userCredential.username = strdup((const char *)(*rfbClientGetClientData_f)(cl, &TAG_2)); + return c; +} + +VncCamera::VncCamera( + unsigned int p_monitor_id, + const std::string &host, + const std::string &port, + const std::string &user, + const std::string &pass, + int p_width, + int p_height, + int p_colours, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio ) : + Camera( + p_monitor_id, + VNC_SRC, + p_width, + p_height, + p_colours, + ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), + p_brightness, + p_contrast, + p_hue, + p_colour, + p_capture, + p_record_audio + ), + mHost(host), + mPort(port), + mUser(user), + mPass(pass) +{ + Debug(2, "Host:%s Port: %s User: %s Pass:%s", mHost.c_str(), mPort.c_str(), mUser.c_str(), mPass.c_str()); + + if ( colours == ZM_COLOUR_RGB32 ) { + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + mImgPixFmt = AV_PIX_FMT_RGBA; + mBpp = 4; + } else if ( colours == ZM_COLOUR_RGB24 ) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + mImgPixFmt = AV_PIX_FMT_RGB24; + mBpp = 3; + } else if ( colours == ZM_COLOUR_GRAY8 ) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + mImgPixFmt = AV_PIX_FMT_GRAY8; + mBpp = 1; + } else { + Panic("Unexpected colours: %d", colours); + } + + if ( capture ) + Initialise(); +} + + +VncCamera::~VncCamera() { + if( capture ) + Terminate(); +} + +void VncCamera::Initialise() { + Debug(2, "Initializing Client"); + bind_libvnc_symbols(); + mRfb = (*rfbGetClient_f)(8, 3, 4); + + (*rfbClientSetClientData_f)(mRfb, &TAG_0, &mVncData); + (*rfbClientSetClientData_f)(mRfb, &TAG_1, (void *)mPass.c_str()); + (*rfbClientSetClientData_f)(mRfb, &TAG_2, (void *)mUser.c_str()); + + mRfb->GotFrameBufferUpdate = GotFrameBufferUpdateCallback; + mRfb->GetPassword = GetPasswordCallback; + mRfb->GetCredential = GetCredentialsCallback; + + mRfb->programName = "Zoneminder VNC Monitor"; + mRfb->serverHost = strdup(mHost.c_str()); + mRfb->serverPort = atoi(mPort.c_str()); + (*rfbInitClient_f)(mRfb, 0, nullptr); + scale.init(); +} + +void VncCamera::Terminate() { + if(mRfb->frameBuffer) + free(mRfb->frameBuffer); + (*rfbClientCleanup_f)(mRfb); + return; +} + +int VncCamera::PrimeCapture() { + Info("Priming capture from %s", mHost.c_str()); + return 0; +} + +int VncCamera::PreCapture() { + Debug(2, "PreCapture"); + (*WaitForMessage_f)(mRfb, 500); + rfbBool res = (*HandleRFBServerMessage_f)(mRfb); + return res == TRUE ? 1 : -1 ; +} + +int VncCamera::Capture(Image &image) { + Debug(2, "Capturing"); + uint8_t *directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); + scale.Convert(mVncData.buffer, mRfb->si.framebufferHeight * mRfb->si.framebufferWidth * 4, directbuffer, width * height * mBpp, AV_PIX_FMT_RGBA, mImgPixFmt, mRfb->si.framebufferWidth, mRfb->si.framebufferHeight, width, height); + return 1; +} + +int VncCamera::PostCapture() { + return 0; +} + +int VncCamera::CaptureAndRecord(Image &image, timeval recording, char* event_directory) { + return 0; +} + +int VncCamera::Close() { + return 0; +} +#endif diff --git a/src/zm_libvnc_camera.h b/src/zm_libvnc_camera.h new file mode 100644 index 000000000..250cfaa2e --- /dev/null +++ b/src/zm_libvnc_camera.h @@ -0,0 +1,62 @@ + +#ifndef ZN_LIBVNC_CAMERA_H +#define ZN_LIBVNC_CAMERA_H + +#include "zm_buffer.h" +#include "zm_camera.h" +#include "zm_thread.h" +#include "zm_swscale.h" + +#if HAVE_LIBVNC +#include +// Used by vnc callbacks +struct VncPrivateData +{ + uint8_t *buffer; + uint8_t width; + uint8_t height; +}; + +class VncCamera : public Camera { +protected: + rfbClient *mRfb; + VncPrivateData mVncData; + int mBpp; + SWScale scale; + AVPixelFormat mImgPixFmt; + std::string mHost; + std::string mPort; + std::string mUser; + std::string mPass; +public: + VncCamera( + unsigned int p_monitor_id, + const std::string &host, + const std::string &port, + const std::string &user, + const std::string &pass, + int p_width, + int p_height, + int p_colours, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio ); + + ~VncCamera(); + + void Initialise(); + void Terminate(); + + int PreCapture(); + int PrimeCapture(); + int Capture( Image &image ); + int PostCapture(); + int CaptureAndRecord( Image &image, timeval recording, char* event_directory ); + int Close(); +}; + +#endif // HAVE_LIBVNC +#endif // ZN_LIBVNC_CAMERA_H diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index dddaf48b7..75b2b5043 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -55,7 +55,7 @@ static _AVPIXELFORMAT getFfPixFormatFromV4lPalette(int v4l_version, int palette) #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { - switch( palette ) { + switch ( palette ) { #if defined(V4L2_PIX_FMT_RGB444) && defined(AV_PIX_FMT_RGB444) case V4L2_PIX_FMT_RGB444 : pixFormat = AV_PIX_FMT_RGB444; @@ -743,12 +743,12 @@ void LocalCamera::Initialise() { Debug(4, " v4l2_data.fmt.type = %08x\n" - " v4l2_data.fmt.fmt.pix.width = %08x\n" - " v4l2_data.fmt.fmt.pix.height = %08x\n" + " v4l2_data.fmt.fmt.pix.width = %d\n" + " v4l2_data.fmt.fmt.pix.height = %d\n" " v4l2_data.fmt.fmt.pix.pixelformat = %08x\n" " v4l2_data.fmt.fmt.pix.field = %08x\n" - " v4l2_data.fmt.fmt.pix.bytesperline = %08x\n" - " v4l2_data.fmt.fmt.pix.sizeimage = %08x\n" + " v4l2_data.fmt.fmt.pix.bytesperline = %d\n" + " v4l2_data.fmt.fmt.pix.sizeimage = %d\n" " v4l2_data.fmt.fmt.pix.colorspace = %08x\n" " v4l2_data.fmt.fmt.pix.priv = %08x\n" , v4l2_data.fmt.type @@ -786,12 +786,12 @@ void LocalCamera::Initialise() { /* Note VIDIOC_S_FMT may change width and height. */ Debug(4, " v4l2_data.fmt.type = %08x\n" - " v4l2_data.fmt.fmt.pix.width = %08x\n" - " v4l2_data.fmt.fmt.pix.height = %08x\n" + " v4l2_data.fmt.fmt.pix.width = %d\n" + " v4l2_data.fmt.fmt.pix.height = %d\n" " v4l2_data.fmt.fmt.pix.pixelformat = %08x\n" " v4l2_data.fmt.fmt.pix.field = %08x\n" - " v4l2_data.fmt.fmt.pix.bytesperline = %08x\n" - " v4l2_data.fmt.fmt.pix.sizeimage = %08x\n" + " v4l2_data.fmt.fmt.pix.bytesperline = %d\n" + " v4l2_data.fmt.fmt.pix.sizeimage = %d\n" " v4l2_data.fmt.fmt.pix.colorspace = %08x\n" " v4l2_data.fmt.fmt.pix.priv = %08x\n" , v4l2_data.fmt.type diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 8c65ca22a..934c4e567 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -51,6 +51,9 @@ #if HAVE_LIBCURL #include "zm_curl_camera.h" #endif // HAVE_LIBCURL +#if HAVE_LIBVNC +#include "zm_libvnc_camera.h" +#endif // HAVE_LIBVNC #if ZM_MEM_MAPPED #include @@ -88,8 +91,9 @@ std::string CameraType_Strings[] = { "File", "Ffmpeg", "LibVLC", - "cURL", "NVSOCKET", + "CURL", + "VNC", }; Monitor::MonitorLink::MonitorLink(int p_id, const char *p_name) : @@ -555,6 +559,15 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) { camera = NULL; uint64_t image_size = width * height * colours; + if ( strcmp(config.event_close_mode, "time") == 0 ) + event_close_mode = CLOSE_TIME; + else if ( strcmp(config.event_close_mode, "alarm") == 0 ) + event_close_mode = CLOSE_ALARM; + else + event_close_mode = CLOSE_IDLE; + + Debug(1, "monitor purpose=%d", purpose); + mem_size = sizeof(SharedData) + sizeof(TriggerData) + sizeof(VideoStoreData) //Information to pass back to the capture process @@ -577,13 +590,11 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) { // maybe unneeded // Should maybe store this for later use - char monitor_dir[PATH_MAX] = ""; + char monitor_dir[PATH_MAX]; snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id); if ( purpose == CAPTURE ) { - if ( mkdir(monitor_dir, 0755) ) { - if ( errno != EEXIST ) { - Error("Can't mkdir %s: %s", monitor_dir, strerror(errno)); - } + if ( mkdir(monitor_dir, 0755) && ( errno != EEXIST ) ) { + Error("Can't mkdir %s: %s", monitor_dir, strerror(errno)); } } else if ( purpose == ANALYSIS ) { if ( config.record_diag_images ) { @@ -801,7 +812,6 @@ Monitor *Monitor::Load(unsigned int p_id, bool load_zones, Purpose purpose) { return monitor; } // end Monitor *Monitor::Load(unsigned int p_id, bool load_zones, Purpose purpose) - bool Monitor::connect() { Debug(3, "Connecting to monitor. Purpose is %d", purpose); #if ZM_MEM_MAPPED @@ -2071,10 +2081,10 @@ void Monitor::Reload() { ready_count = image_count+warmup_count; delete row; - } // end if row + } // end if row ReloadZones(); -} // void Monitor::Reload() +} // end void Monitor::Reload() void Monitor::ReloadZones() { Debug(1, "Reloading zones for monitor %s", name); @@ -2315,8 +2325,8 @@ int Monitor::Capture() { packet->packet.stream_index, video_stream_id, packetqueue->video_packet_count, ( event ? 1 : 0 ) ); if ( packet->packet.stream_index != video_stream_id ) { - Debug(2, "Have audio packet (%d) != videostream_id:(%d) q.vpktcount(%d) event?(%d) ", - packet->packet.stream_index, video_stream_id, packetqueue->video_packet_count, ( event ? 1 : 0 ) ); +//Debug(2, "Have audio packet (%d) != videostream_id:(%d) q.vpktcount(%d) event?(%d) ", + // packet->packet.stream_index, video_stream_id, packetqueue->video_packet_count, ( event ? 1 : 0 ) ); // Only queue if we have some video packets in there. Should push this logic into packetqueue //mutex.lock(); if ( packetqueue->video_packet_count || event ) { diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 1bfbcb467..6a6ae7bfb 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -80,6 +80,7 @@ public: LIBVLC, CURL, NVSOCKET, + VNC, } CameraType; typedef enum { diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index dd07012ae..ea342aee4 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -330,14 +330,16 @@ bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) { struct timeval frameStartTime; gettimeofday(&frameStartTime, NULL); - fputs("--ZoneMinderFrame\r\nContent-Type: image/jpeg\r\n", stdout); - fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); + fputs("--" BOUNDARY "\r\nContent-Type: image/jpeg\r\n", stdout); + fprintf(stdout, "Content-Length: %d\r\n" + "X-Timestamp: %d.%06d\r\n" + "\r\n", img_buffer_size, (int)timestamp->tv_sec, (int)timestamp->tv_usec); if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { if ( !zm_terminate ) Warning("Unable to send stream frame: %s", strerror(errno)); return false; } - fputs("\r\n\r\n", stdout); + fputs("\r\n", stdout); fflush(stdout); struct timeval frameEndTime; @@ -361,6 +363,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { if ( !config.timestamp_on_capture && timestamp ) monitor->TimestampImage(send_image, timestamp); + fputs("--" BOUNDARY "\r\n", stdout); #if HAVE_LIBAVCODEC if ( type == STREAM_MPEG ) { if ( !vid_stream ) { @@ -386,7 +389,6 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { struct timeval frameStartTime; gettimeofday(&frameStartTime, NULL); - fputs("--ZoneMinderFrame\r\n", stdout); switch( type ) { case STREAM_JPEG : send_image->EncodeJpeg(img_buffer, &img_buffer_size); @@ -404,15 +406,17 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { send_image->Zip(img_buffer, &zip_buffer_size); img_buffer_size = zip_buffer_size; #else - Error("zlib is required for zipped images. Falling back to raw image"); - type = STREAM_RAW; + Error("zlib is required for zipped images. Falling back to raw image"); + type = STREAM_RAW; #endif // HAVE_ZLIB_H break; default : Error("Unexpected frame type %d", type); return false; } - fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size); + fprintf(stdout, "Content-Length: %d\r\n" + "X-Timestamp: %d.%06d\r\n" + "\r\n", img_buffer_size, (int)timestamp->tv_sec, (int)timestamp->tv_usec); if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) { if ( !zm_terminate ) { // If the pipe was closed, we will get signalled SIGPIPE to exit, which will set zm_terminate @@ -420,7 +424,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) { } return false; } - fputs("\r\n\r\n", stdout); + fputs("\r\n", stdout); fflush(stdout); struct timeval frameEndTime; @@ -454,7 +458,7 @@ void MonitorStream::runStream() { updateFrameRate(monitor->GetFPS()); if ( type == STREAM_JPEG ) - fputs("Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n", stdout); + fputs("Content-Type: multipart/x-mixed-replace; boundary=" BOUNDARY "\r\n\r\n", stdout); // point to end which is theoretically not a valid value because all indexes are % image_buffer_count unsigned int last_read_index = monitor->image_buffer_count; @@ -520,7 +524,6 @@ void MonitorStream::runStream() { Debug(2, "Not using playback_buffer"); } // end if connkey & playback_buffer - float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs) // if MaxFPS < 0 as in 1/60 = 0.017 then we won't get another frame for 60 sec. double capture_fps = monitor->GetFPS(); double capture_max_fps = monitor->GetCaptureMaxFPS(); @@ -529,15 +532,6 @@ void MonitorStream::runStream() { capture_fps = capture_max_fps; } - if ( capture_fps < 1 ) { - max_secs_since_last_sent_frame = 10/capture_fps; - Debug(1, "Adjusting max_secs_since_last_sent_frame to %.2f from current fps %.2f", - max_secs_since_last_sent_frame, monitor->GetFPS()); - } else { - Debug(1, "Not Adjusting max_secs_since_last_sent_frame to %.2f from current fps %.2f", - max_secs_since_last_sent_frame, monitor->GetFPS()); - } - while ( !zm_terminate ) { bool got_command = false; if ( feof(stdout) ) { @@ -673,7 +667,7 @@ void MonitorStream::runStream() { if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) { if ( !paused && !delayed ) { last_read_index = monitor->shared_data->last_write_index; - Debug(2, "index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)", + Debug(2, "Sending frame index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)", index, frame_mod, frame_count, paused, delayed); // Send the next frame // @@ -688,6 +682,15 @@ void MonitorStream::runStream() { // Perhaps we should use NOW instead. last_frame_timestamp = *timestamp; //frame_sent = true; + // + if ( frame_count == 0 ) { + // Chrome will not display the first frame until it receives another. + // Firefox is fine. So just send the first frame twice. + if ( !sendFrame(snap->image, snap->timestamp) ) { + Debug(2, "sendFrame failed, quiting."); + zm_terminate = true; + } + } temp_read_index = temp_write_index; } else { @@ -756,13 +759,13 @@ void MonitorStream::runStream() { } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))); - Debug(3, "Sleeping for (%d)", sleep_time); - if ( sleep_time > MAX_SLEEP_USEC ) { // Shouldn't sleep for long because we need to check command queue, etc. sleep_time = MAX_SLEEP_USEC; + Debug(3, "Sleeping for MAX_SLEEP_USEC %dus", sleep_time); + } else { + Debug(3, "Sleeping for %dus", sleep_time); } - Debug(3, "Sleeping for %dus", sleep_time); usleep(sleep_time); if ( ttl ) { if ( (now.tv_sec - stream_start_time) > ttl ) { @@ -775,14 +778,6 @@ void MonitorStream::runStream() { last_frame_sent = now.tv_sec; Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d)", frame_mod, frame_count); - } else if ( - (!paused) - && - ( (TV_2_FLOAT(now) - last_frame_sent) > max_secs_since_last_sent_frame ) - ) { - Error("Terminating, last frame sent time %f secs more than maximum of %f", - TV_2_FLOAT(now) - last_frame_sent, max_secs_since_last_sent_frame); - break; } } // end while diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index de5a51c93..669012683 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -169,14 +169,15 @@ int RemoteCameraRtsp::PrimeCapture() { // Find the first video stream. for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - AVCodecParameters *codec_context = mFormatContext->streams[i]->codecpar; + AVMediaType codec_type = mFormatContext->streams[i]->codecpar->codec_type; #else - AVCodecContext *codec_context = mFormatContext->streams[i]->codec; + AVMediaType codec_type = mFormatContext->streams[i]->codec->codec_type; #endif + #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) - if ( codec_context->codec_type == AVMEDIA_TYPE_VIDEO ) + if ( codec_type == AVMEDIA_TYPE_VIDEO ) #else - if ( codec_context->codec_type == CODEC_TYPE_VIDEO ) + if ( codec_type == CODEC_TYPE_VIDEO ) #endif { if ( mVideoStreamId == -1 ) { @@ -187,9 +188,9 @@ int RemoteCameraRtsp::PrimeCapture() { } } else #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) - if ( codec_context->codec_type == AVMEDIA_TYPE_AUDIO ) + if ( codec_type == AVMEDIA_TYPE_AUDIO ) #else - if ( codec_context->codec_type == CODEC_TYPE_AUDIO ) + if ( codec_type == CODEC_TYPE_AUDIO ) #endif { if ( mAudioStreamId == -1 ) { @@ -198,7 +199,7 @@ int RemoteCameraRtsp::PrimeCapture() { Debug(2, "Have another audio stream."); } } else { - Debug(1, "Have unknown codec type in stream %d : %d", i, mFormatContext->streams[i]->codec->codec_type); + Debug(1, "Have unknown codec type in stream %d : %d", i, codec_type); } } // end foreach stream @@ -229,18 +230,10 @@ int RemoteCameraRtsp::PrimeCapture() { Panic("Can't open codec"); // Allocate space for the native video frame -#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - mRawFrame = av_frame_alloc(); -#else - mRawFrame = avcodec_alloc_frame(); -#endif + mRawFrame = zm_av_frame_alloc(); // Allocate space for the converted video frame -#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - mFrame = av_frame_alloc(); -#else - mFrame = avcodec_alloc_frame(); -#endif + mFrame = zm_av_frame_alloc(); if ( mRawFrame == NULL || mFrame == NULL ) Fatal("Unable to allocate frame(s)"); @@ -345,11 +338,7 @@ int RemoteCameraRtsp::Capture( ZMPacket &zm_packet ) { bytes += packet->size; // So I think this is the magic decode step. Result is a raw image? -#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0) - int len = avcodec_decode_video2(mCodecContext, mRawFrame, &frameComplete, packet); -#else - int len = avcodec_decode_video(mCodecContext, mRawFrame, &frameComplete, packet->data, packet->size); -#endif + int len = zm_send_packet_receive_frame(mCodecContext, mRawFrame, *packet); if ( len < 0 ) { Error("Error while decoding frame %d", frameCount); Hexdump(Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size()); diff --git a/src/zm_rtsp_auth.cpp b/src/zm_rtsp_auth.cpp index 81aa13c27..2f8b2c3ce 100644 --- a/src/zm_rtsp_auth.cpp +++ b/src/zm_rtsp_auth.cpp @@ -144,7 +144,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin #if HAVE_DECL_MD5 MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() }; + gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), (unsigned int)ha1Data.length() }; gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len ); #endif for ( unsigned int j = 0; j < md5len; j++ ) { @@ -159,7 +159,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin #if HAVE_DECL_MD5 MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf ); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() }; + gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), (unsigned int)ha2Data.length() }; gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len ); #endif for ( unsigned int j = 0; j < md5len; j++ ) { @@ -180,7 +180,7 @@ std::string Authenticator::computeDigestResponse(std::string &method, std::strin #if HAVE_DECL_MD5 MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() }; + gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), (unsigned int)digestData.length() }; gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len ); #endif for ( unsigned int j = 0; j < md5len; j++ ) { diff --git a/src/zm_rtsp_auth.h b/src/zm_rtsp_auth.h index 34056eee6..8e65746de 100644 --- a/src/zm_rtsp_auth.h +++ b/src/zm_rtsp_auth.h @@ -19,9 +19,6 @@ #ifndef ZM_RTSP_AUTH_H #define ZM_RTSP_AUTH_H -#if HAVE_GNUTLS_OPENSSL_H -#include -#endif #if HAVE_GNUTLS_GNUTLS_H #include #endif diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index df4f9b417..258e04d52 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -44,7 +44,12 @@ bool StreamBase::loadMonitor(int monitor_id) { Error("Unable to load monitor id %d for streaming", monitor_id); return false; } - if ( ! monitor->connect() ) { + if ( monitor->GetFunction() == Monitor::NONE ) { + Error("Monitor %d has function NONE. Will not be able to connect to it.", monitor_id); + return false; + } + + if ( !monitor->connect() ) { Error("Unable to connect to monitor id %d for streaming", monitor_id); return false; } @@ -109,7 +114,7 @@ bool StreamBase::checkCommandQueue() { return false; } -Image *StreamBase::prepareImage( Image *image ) { +Image *StreamBase::prepareImage(Image *image) { // Do not bother to scale zoomed in images, just crop them and let the browser scale // Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream. @@ -119,50 +124,51 @@ Image *StreamBase::prepareImage( Image *image ) { int mag = (scale * zoom) / ZM_SCALE_BASE; int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag; - Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag ); int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE; int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag; - Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag ); - int base_image_width = image->Width(), base_image_height = image->Height(); - Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height ); - int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE; - Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height ); - int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE; - Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height ); - int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE; - Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height ); - int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE; - Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height ); - int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE; - Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height ); - int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE; - Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height ); - int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag; - Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height ); - int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag; - Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height ); - if ( mag != ZM_SCALE_BASE ) { - if ( act_mag != ZM_SCALE_BASE ) { - Debug(3, "Magnifying by %d", mag); - if ( !image_copied ) { - static Image copy_image; - copy_image.Assign(*image); - image = ©_image; - image_copied = true; - } - image->Scale(mag); - } + Debug(3, + "Scaling by %d, zooming by %d = magnifying by %d(%d)\n" + "Last scaling by %d, zooming by %d = magnifying by %d(%d)\n" + "Base image width = %d, height = %d\n" + "Virtual image width = %d, height = %d\n" + "Last virtual image width = %d, height = %d\n" + "Actual image width = %d, height = %d\n" + "Last actual image width = %d, height = %d\n" + "Display image width = %d, height = %d\n" + "Last display image width = %d, height = %d\n" + "Send image width = %d, height = %d\n" + "Last send image width = %d, height = %d\n", + scale, zoom, mag, act_mag, + last_scale, last_zoom, last_mag, last_act_mag, + base_image_width, base_image_height, + virt_image_width, virt_image_height, + last_virt_image_width, last_virt_image_height, + act_image_width, act_image_height, + last_act_image_width, last_act_image_height, + disp_image_width, disp_image_height, + last_disp_image_width, last_disp_image_height, + send_image_width, send_image_height, + last_send_image_width, last_send_image_height + ); + + if ( ( mag != ZM_SCALE_BASE ) && (act_mag != ZM_SCALE_BASE) ) { + Debug(3, "Magnifying by %d", mag); + static Image copy_image; + copy_image.Assign(*image); + image = ©_image; + image_copied = true; + image->Scale(mag); } Debug(3, "Real image width = %d, height = %d", image->Width(), image->Height()); @@ -171,26 +177,22 @@ Image *StreamBase::prepareImage( Image *image ) { static Box last_crop; if ( mag != last_mag || x != last_x || y != last_y ) { - Debug( 3, "Got click at %d,%d x %d", x, y, mag ); - - //if ( !last_mag ) - //last_mag = mag; + Debug(3, "Got click at %d,%d x %d", x, y, mag); if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) ) last_crop = Box(); - Debug( 3, "Recalculating crop" ); // Recalculate crop parameters, as %ges int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image click_x += ( x * 100 ) / last_virt_image_width; int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image click_y += ( y * 100 ) / last_virt_image_height; - Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y ); + Debug(3, "Got adjusted click at %d%%,%d%%", click_x, click_y); // Convert the click locations to the current image pixels click_x = ( click_x * act_image_width ) / 100; click_y = ( click_y * act_image_height ) / 100; - Debug( 3, "Got readjusted click at %d,%d", click_x, click_y ); + Debug(3, "Got readjusted click at %d,%d", click_x, click_y); int lo_x = click_x - (send_image_width/2); if ( lo_x < 0 ) @@ -209,8 +211,9 @@ Image *StreamBase::prepareImage( Image *image ) { lo_y = hi_y - (send_image_height - 1); } last_crop = Box( lo_x, lo_y, hi_x, hi_y ); - } - Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() ); + } // end if ( mag != last_mag || x != last_x || y != last_y ) + + Debug(3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY()); if ( !image_copied ) { static Image copy_image; copy_image.Assign(*image); @@ -218,14 +221,15 @@ Image *StreamBase::prepareImage( Image *image ) { image_copied = true; } image->Crop(last_crop); - } + } // end if difference in image vs displayed dimensions + last_scale = scale; last_zoom = zoom; last_x = x; last_y = y; return image; -} +} // end Image *StreamBase::prepareImage(Image *image) bool StreamBase::sendTextFrame(const char *frame_text) { Debug(2, "Sending %dx%d * %d text frame '%s'", @@ -344,7 +348,7 @@ void StreamBase::openComms() { } snprintf(rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", staticConfig.PATH_SOCKS.c_str(), connkey); - strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)-1); + strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)); rem_addr.sun_family = AF_UNIX; gettimeofday(&last_comm_update, NULL); diff --git a/src/zm_stream.h b/src/zm_stream.h index eb99b74c1..c7984a1af 100644 --- a/src/zm_stream.h +++ b/src/zm_stream.h @@ -29,6 +29,7 @@ class Monitor; #define TV_2_FLOAT( tv ) ( double((tv).tv_sec) + (double((tv).tv_usec) / 1000000.0) ) +#define BOUNDARY "ZoneMinderFrame" class StreamBase { public: @@ -73,18 +74,16 @@ protected: int bitrate; unsigned short last_x, last_y; unsigned short x, y; - -protected: + bool send_analysis; + bool send_objdetect; int connkey; int sd; - char loc_sock_path[PATH_MAX]; + char loc_sock_path[108]; struct sockaddr_un loc_addr; - char rem_sock_path[PATH_MAX]; + char rem_sock_path[108]; struct sockaddr_un rem_addr; - char sock_path_lock[PATH_MAX]; + char sock_path_lock[108]; int lock_fd; - -protected: bool paused; int step; @@ -114,25 +113,30 @@ protected: virtual void processCommand( const CmdMsg *msg )=0; public: - StreamBase() { - monitor = 0; + StreamBase(): + monitor(0), + type(DEFAULT_TYPE), + format(""), + replay_rate(DEFAULT_RATE), + scale(DEFAULT_SCALE), + last_scale(DEFAULT_SCALE), + zoom(DEFAULT_ZOOM), + last_zoom(DEFAULT_ZOOM), + maxfps(DEFAULT_MAXFPS), + bitrate(DEFAULT_BITRATE), + last_x(0), + last_y(0), + x(0), + y(0), + send_analysis(false), + send_objdetect(false), + connkey(0), + sd(-1), + lock_fd(0), + paused(false), + step(0) - type = DEFAULT_TYPE; - format = ""; - replay_rate = DEFAULT_RATE; - last_scale = scale = DEFAULT_SCALE; - last_zoom = zoom = DEFAULT_ZOOM; - maxfps = DEFAULT_MAXFPS; - bitrate = DEFAULT_BITRATE; - - paused = false; - step = 0; - last_x = x = 0; - last_y = y = 0; - - connkey = 0; - sd = -1; - lock_fd = 0; + { memset( &loc_sock_path, 0, sizeof(loc_sock_path) ); memset( &loc_addr, 0, sizeof(loc_addr) ); memset( &rem_sock_path, 0, sizeof(rem_sock_path) ); diff --git a/src/zm_swscale.cpp b/src/zm_swscale.cpp index ae3a9662d..5830f402a 100644 --- a/src/zm_swscale.cpp +++ b/src/zm_swscale.cpp @@ -134,7 +134,9 @@ int SWScale::Convert( enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, - unsigned int height + unsigned int height, + unsigned int new_width, + unsigned int new_height ) { /* Parameter checking */ if(in_buffer == NULL || out_buffer == NULL) { @@ -145,7 +147,7 @@ int SWScale::Convert( // Error("Invalid input or output pixel formats"); // return -2; // } - if (!width || !height) { + if (!width || !height || !new_height || !new_width) { Error("Invalid width or height"); return -3; } @@ -180,7 +182,7 @@ int SWScale::Convert( /* Check the buffer sizes */ #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - size_t insize = av_image_get_buffer_size(in_pf, width, height,1); + size_t insize = av_image_get_buffer_size(in_pf, width, height, 1); #else size_t insize = avpicture_get_size(in_pf, width, height); #endif @@ -189,9 +191,9 @@ int SWScale::Convert( return -4; } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - size_t outsize = av_image_get_buffer_size(out_pf, width, height,1); + size_t outsize = av_image_get_buffer_size(out_pf, new_width, new_height, 1); #else - size_t outsize = avpicture_get_size(out_pf, width, height); + size_t outsize = avpicture_get_size(out_pf, new_width, new_height); #endif if ( outsize < out_buffer_size ) { @@ -200,7 +202,7 @@ int SWScale::Convert( } /* Get the context */ - swscale_ctx = sws_getCachedContext( swscale_ctx, width, height, in_pf, width, height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL ); + swscale_ctx = sws_getCachedContext(swscale_ctx, width, height, in_pf, new_width, new_height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL); if ( swscale_ctx == NULL ) { Error("Failed getting swscale context"); return -6; @@ -219,10 +221,10 @@ int SWScale::Convert( } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize, - out_buffer, out_pf, width, height, 1) <= 0) { + out_buffer, out_pf, new_width, new_height, 1) <= 0) { #else - if (avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, width, - height) <= 0) { + if (avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, new_width, + new_height) <= 0) { #endif Error("Failed filling output frame with output buffer"); return -8; @@ -237,6 +239,10 @@ int SWScale::Convert( return 0; } +int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { + return Convert(in_buffer, in_buffer_size, out_buffer, out_buffer_size, in_pf, out_pf, width, height, width, height); +} + int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { if ( img->Width() != width ) { Error("Source image width differs. Source: %d Output: %d",img->Width(), width); diff --git a/src/zm_swscale.h b/src/zm_swscale.h index 4b4226ddb..f55f7e759 100644 --- a/src/zm_swscale.h +++ b/src/zm_swscale.h @@ -10,14 +10,15 @@ class SWScale { public: SWScale(); ~SWScale(); - bool init(); + bool init(); int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size); int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size); int Convert( AVFrame *in_frame, AVFrame *out_frame ); int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); - + int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height, unsigned int new_width, unsigned int new_height); + protected: bool gotdefaults; struct SwsContext* swscale_ctx; diff --git a/src/zm_user.cpp b/src/zm_user.cpp index d11c609df..3be4e274a 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -1,21 +1,21 @@ /* * ZoneMinder regular expression class implementation, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes - * + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ +*/ #include "zm.h" #include "zm_db.h" @@ -26,10 +26,8 @@ #include #include #include +#include -#if HAVE_GNUTLS_OPENSSL_H -#include -#endif #if HAVE_GNUTLS_GNUTLS_H #include #endif @@ -38,7 +36,7 @@ #include #elif HAVE_LIBCRYPTO #include -#endif // HAVE_L || HAVE_LIBCRYPTO +#endif // HAVE_GCRYPT_H || HAVE_LIBCRYPTO #include "zm_utils.h" #include "zm_crypt.h" @@ -55,7 +53,7 @@ User::User(const MYSQL_ROW &dbrow) { id = atoi(dbrow[index++]); strncpy(username, dbrow[index++], sizeof(username)-1); strncpy(password, dbrow[index++], sizeof(password)-1); - enabled = (bool)atoi(dbrow[index++]); + enabled = static_cast(atoi(dbrow[index++])); stream = (Permission)atoi(dbrow[index++]); events = (Permission)atoi(dbrow[index++]); control = (Permission)atoi(dbrow[index++]); @@ -64,7 +62,7 @@ User::User(const MYSQL_ROW &dbrow) { char *monitor_ids_str = dbrow[index++]; if ( monitor_ids_str && *monitor_ids_str ) { StringVector ids = split(monitor_ids_str, ","); - for( StringVector::iterator i = ids.begin(); i < ids.end(); ++i ) { + for ( StringVector::iterator i = ids.begin(); i < ids.end(); ++i ) { monitor_ids.push_back(atoi((*i).c_str())); } } @@ -75,9 +73,9 @@ User::~User() { } void User::Copy(const User &u) { - id=u.id; - strncpy(username, u.username, sizeof(username)-1); - strncpy(password, u.password, sizeof(password)-1); + id = u.id; + strncpy(username, u.username, sizeof(username)); + strncpy(password, u.password, sizeof(password)); enabled = u.enabled; stream = u.stream; events = u.events; @@ -90,8 +88,9 @@ void User::Copy(const User &u) { bool User::canAccess(int monitor_id) { if ( monitor_ids.empty() ) return true; - - for ( std::vector::iterator i = monitor_ids.begin(); i != monitor_ids.end(); ++i ) { + + for ( std::vector::iterator i = monitor_ids.begin(); + i != monitor_ids.end(); ++i ) { if ( *i == monitor_id ) { return true; } @@ -106,18 +105,23 @@ User *zmLoadUser(const char *username, const char *password) { int username_length = strlen(username); char *safer_username = new char[(username_length * 2) + 1]; - // According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator. + // According to docs, size of safer_whatever must be 2*length+1 + // due to unicode conversions + null terminator. mysql_real_escape_string(&dbconn, safer_username, username, username_length); snprintf(sql, sizeof(sql), - "SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`" - " FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", safer_username); + "SELECT `Id`, `Username`, `Password`, `Enabled`," + " `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0," + " `MonitorIds`" + " FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", + safer_username); + delete[] safer_username; + safer_username = NULL; if ( mysql_query(&dbconn, sql) ) { Error("Can't run query: %s", mysql_error(&dbconn)); - exit(mysql_errno(&dbconn)); + exit(mysql_errno(&dbconn)); } - delete safer_username; MYSQL_RES *result = mysql_store_result(&dbconn); if ( !result ) { @@ -125,164 +129,158 @@ User *zmLoadUser(const char *username, const char *password) { exit(mysql_errno(&dbconn)); } - if ( mysql_num_rows(result) != 1 ) { + if ( mysql_num_rows(result) == 1 ) { + MYSQL_ROW dbrow = mysql_fetch_row(result); + User *user = new User(dbrow); mysql_free_result(result); - Warning("Unable to authenticate user %s", username); - return NULL; - } - MYSQL_ROW dbrow = mysql_fetch_row(result); - User *user = new User(dbrow); + if ( + (! password ) // relay type must be none + || + verifyPassword(username, password, user->getPassword()) ) { + Info("Authenticated user '%s'", user->getUsername()); + return user; + } + } // end if 1 result from db mysql_free_result(result); - if ( !password ) { - // relay type must be none - return user; - } - - if ( verifyPassword(username, password, user->getPassword()) ) { - Info("Authenticated user '%s'", user->getUsername()); - return user; - } - Warning("Unable to authenticate user %s", username); return NULL; -} +} // end User *zmLoadUser(const char *username, const char *password) -User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) { +User *zmLoadTokenUser(std::string jwt_token_str, bool use_remote_addr) { std::string key = config.auth_hash_secret; std::string remote_addr = ""; - - if (use_remote_addr) { - remote_addr = std::string(getenv( "REMOTE_ADDR" )); + + if ( use_remote_addr ) { + remote_addr = std::string(getenv("REMOTE_ADDR")); if ( remote_addr == "" ) { - Warning( "Can't determine remote address, using null" ); + Warning("Can't determine remote address, using null"); remote_addr = ""; } key += remote_addr; } - Debug (1,"Inside zmLoadTokenUser, formed key=%s", key.c_str()); + Debug(1, "Inside zmLoadTokenUser, formed key=%s", key.c_str()); std::pair ans = verifyToken(jwt_token_str, key); std::string username = ans.first; unsigned int iat = ans.second; - Debug (1,"retrieved user '%s' from token", username.c_str()); + Debug(1, "retrieved user '%s' from token", username.c_str()); - if (username != "") { - char sql[ZM_SQL_MED_BUFSIZ] = ""; - snprintf(sql, sizeof(sql), - "SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`, `TokenMinExpiry`" - " FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", username.c_str() ); - - if ( mysql_query(&dbconn, sql) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); - exit(mysql_errno(&dbconn)); - } - - MYSQL_RES *result = mysql_store_result(&dbconn); - if ( !result ) { - Error("Can't use query result: %s", mysql_error(&dbconn)); - exit(mysql_errno(&dbconn)); - } - int n_users = mysql_num_rows(result); - - if ( n_users != 1 ) { - mysql_free_result(result); - Error("Unable to authenticate user '%s'", username.c_str()); - return NULL; - } - - MYSQL_ROW dbrow = mysql_fetch_row(result); - User *user = new User(dbrow); - unsigned int stored_iat = strtoul(dbrow[10], NULL,0 ); - - if (stored_iat > iat ) { // admin revoked tokens - mysql_free_result(result); - Error("Token was revoked for '%s'", username.c_str()); - return NULL; - } - - Debug (1,"Got last token revoke time of: %u",stored_iat); - Debug (1,"Authenticated user '%s' via token", username.c_str()); - mysql_free_result(result); - return user; - - } - else { + if ( username == "" ) { return NULL; } -} + char sql[ZM_SQL_MED_BUFSIZ] = ""; + snprintf(sql, sizeof(sql), + "SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0," + " `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`, `TokenMinExpiry`" + " FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", username.c_str()); + + if ( mysql_query(&dbconn, sql) ) { + Error("Can't run query: %s", mysql_error(&dbconn)); + return NULL; + } + + MYSQL_RES *result = mysql_store_result(&dbconn); + if ( !result ) { + Error("Can't use query result: %s", mysql_error(&dbconn)); + return NULL; + } + + int n_users = mysql_num_rows(result); + if ( n_users != 1 ) { + mysql_free_result(result); + Error("Unable to authenticate user '%s'", username.c_str()); + return NULL; + } + + MYSQL_ROW dbrow = mysql_fetch_row(result); + User *user = new User(dbrow); + unsigned int stored_iat = strtoul(dbrow[10], NULL, 0); + + if ( stored_iat > iat ) { // admin revoked tokens + mysql_free_result(result); + Error("Token was revoked for '%s'", username.c_str()); + return NULL; + } + + Debug(1, "Authenticated user '%s' via token with last revoke time: %u", + username.c_str(), stored_iat); + mysql_free_result(result); + return user; +} // User *zmLoadTokenUser(std::string jwt_token_str, bool use_remote_addr) // Function to validate an authentication string -User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) { +User *zmLoadAuthUser(const char *auth, bool use_remote_addr) { #if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT #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 ); -#endif // HAVE_GCRYPT_H + gcry_control(GCRYCTL_DISABLE_SECMEM, 0); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); +#endif // HAVE_GCRYPT_H const char *remote_addr = ""; if ( use_remote_addr ) { - remote_addr = getenv( "REMOTE_ADDR" ); + remote_addr = getenv("REMOTE_ADDR"); if ( !remote_addr ) { - Warning( "Can't determine remote address, using null" ); + Warning("Can't determine remote address, using null"); remote_addr = ""; } } - Debug( 1, "Attempting to authenticate user from auth string '%s', remote addr(%s)", auth, remote_addr ); + Debug(1, "Attempting to authenticate user from auth string '%s', remote addr(%s)", auth, remote_addr); char sql[ZM_SQL_SML_BUFSIZ] = ""; - snprintf( sql, sizeof(sql), "SELECT `Id`, `Username`, `Password`, `Enabled`, `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0, `MonitorIds` FROM `Users` WHERE `Enabled` = 1" ); + snprintf(sql, sizeof(sql), + "SELECT `Id`, `Username`, `Password`, `Enabled`," + " `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0," + " `MonitorIds` FROM `Users` WHERE `Enabled` = 1"); - if ( mysql_query( &dbconn, sql ) ) { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't run query: %s", mysql_error(&dbconn)); + exit(mysql_errno(&dbconn)); } - MYSQL_RES *result = mysql_store_result( &dbconn ); + MYSQL_RES *result = mysql_store_result(&dbconn); if ( !result ) { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + Error("Can't use query result: %s", mysql_error(&dbconn)); + return NULL; } - int n_users = mysql_num_rows( result ); - + int n_users = mysql_num_rows(result); if ( n_users < 1 ) { - mysql_free_result( result ); - Warning( "Unable to authenticate user" ); - return( 0 ); + mysql_free_result(result); + Warning("Unable to authenticate user"); + return NULL; } // getting the time is expensive, so only do it once. - time_t now = time( 0 ); + time_t now = time(0); unsigned int hours = config.auth_hash_ttl; - - if ( ! hours ) { + if ( !hours ) { Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2."); hours = 2; } else { - Debug( 1, "AUTH_HASH_TTL is %d, time is %d", hours, now ); + Debug(1, "AUTH_HASH_TTL is %d, time is %d", hours, now); } + char auth_key[512] = ""; + char auth_md5[32+1] = ""; + size_t md5len = 16; + unsigned char md5sum[md5len]; - while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) { + const char * hex = "0123456789abcdef"; + while ( MYSQL_ROW dbrow = mysql_fetch_row(result) ) { const char *user = dbrow[1]; const char *pass = dbrow[2]; - char auth_key[512] = ""; - char auth_md5[32+1] = ""; - size_t md5len = 16; - unsigned char md5sum[md5len]; + time_t our_now = now; + for ( unsigned int i = 0; i < hours; i++, our_now -= 3600 ) { + struct tm *now_tm = localtime(&our_now); - time_t now_copy = now; - for ( unsigned int i = 0; i < hours; i++, now_copy -= 3600 ) { - struct tm *now_tm = localtime(&now_copy); - - snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d", + snprintf(auth_key, sizeof(auth_key)-1, "%s%s%s%s%d%d%d%d", config.auth_hash_secret, user, pass, @@ -290,50 +288,57 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) { now_tm->tm_hour, now_tm->tm_mday, now_tm->tm_mon, - now_tm->tm_year - ); + now_tm->tm_year); #if HAVE_DECL_MD5 - MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum ); + MD5((unsigned char *)auth_key, strlen(auth_key), md5sum); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) }; - gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len ); + gnutls_datum_t md5data = { (unsigned char *)auth_key, (unsigned int)strlen(auth_key) }; + gnutls_fingerprint(GNUTLS_DIG_MD5, &md5data, md5sum, &md5len); #endif - auth_md5[0] = '\0'; + unsigned char *md5sum_ptr = md5sum; + char *auth_md5_ptr = auth_md5; + for ( unsigned int j = 0; j < md5len; j++ ) { - sprintf( &auth_md5[2*j], "%02x", md5sum[j] ); + *auth_md5_ptr++ = hex[(*md5sum_ptr>>4)&0xf]; + *auth_md5_ptr++ = hex[(*md5sum_ptr++)&0xf]; } - Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s' == '%s'", auth_key, auth_md5, auth ); + *auth_md5_ptr = 0; - if ( !strcmp( auth, auth_md5 ) ) { + Debug(1, "Checking auth_key '%s' -> auth_md5 '%s' == '%s'", + auth_key, auth_md5, auth); + + if ( !strcmp(auth, auth_md5) ) { // We have a match - User *user = new User( dbrow ); - Debug(1, "Authenticated user '%s'", user->getUsername() ); - mysql_free_result( result ); + User *user = new User(dbrow); + Debug(1, "Authenticated user '%s'", user->getUsername()); + mysql_free_result(result); return user; + } else { + Debug(1, "No match for %s", auth); } - } // end foreach hours - } // end foreach user - Debug(1, "No match for %s", auth ); - mysql_free_result( result ); -#else // HAVE_DECL_MD5 - Error( "You need to build with gnutls or openssl installed to use hash based authentication" ); -#endif // HAVE_DECL_MD5 - Debug(1, "No user found for auth_key %s", auth ); - return 0; -} + } // end foreach hour + } // end foreach user + mysql_free_result(result); +#else // HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT + Error("You need to build with gnutls or openssl to use hash based auth"); +#endif // HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT + Debug(1, "No user found for auth_key %s", auth); + return NULL; +} // end User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) -//Function to check Username length -bool checkUser ( const char *username) { - if ( ! username ) +// Function to check Username length +bool checkUser(const char *username) { + if ( !username ) return false; if ( strlen(username) > 32 ) return false; return true; } -//Function to check password length -bool checkPass (const char *password) { + +// Function to check password length +bool checkPass(const char *password) { if ( !password ) return false; if ( strlen(password) > 64 ) diff --git a/src/zm_utils.cpp b/src/zm_utils.cpp index 46c166543..ccc76a7d6 100644 --- a/src/zm_utils.cpp +++ b/src/zm_utils.cpp @@ -403,28 +403,46 @@ void timespec_diff(struct timespec *start, struct timespec *end, struct timespec char *timeval_to_string( struct timeval tv ) { time_t nowtime; struct tm *nowtm; - static char tmbuf[64], buf[64]; + static char tmbuf[20], buf[28]; nowtime = tv.tv_sec; nowtm = localtime(&nowtime); strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", nowtm); - snprintf(buf, sizeof buf, "%s.%06ld", tmbuf, tv.tv_usec); + snprintf(buf, sizeof buf-1, "%s.%06ld", tmbuf, tv.tv_usec); return buf; } std::string UriDecode( const std::string &encoded ) { -#ifdef HAVE_LIBCURL - CURL *curl = curl_easy_init(); - int outlength; - char *cres = curl_easy_unescape(curl, encoded.c_str(), encoded.length(), &outlength); - std::string res(cres, cres + outlength); - curl_free(cres); - curl_easy_cleanup(curl); - return res; -#else -Warning("ZM Compiled without LIBCURL. UriDecoding not implemented."); - return encoded; -#endif + char a, b; + const char *src = encoded.c_str(); + std::string retbuf; + retbuf.resize(encoded.length() + 1); + char *dst = &retbuf[0]; + while (*src) { + if ((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) { + if (a >= 'a') + a -= 'a'-'A'; + if (a >= 'A') + a -= ('A' - 10); + else + a -= '0'; + if (b >= 'a') + b -= 'a'-'A'; + if (b >= 'A') + b -= ('A' - 10); + else + b -= '0'; + *dst++ = 16*a+b; + src+=3; + } else if (*src == '+') { + *dst++ = ' '; + src++; + } else { + *dst++ = *src++; + } + } + *dst++ = '\0'; + return retbuf; } void string_toupper( std::string& str) { diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 4c20daeea..64bc64029 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -123,9 +123,7 @@ bool VideoStore::open() { if ( video_in_stream ) { video_in_stream_index = video_in_stream->index; -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) video_in_ctx = avcodec_alloc_context3(NULL); - Debug(2, "copy to video_in_context"); ret = avcodec_parameters_to_context(video_in_ctx, video_in_stream->codecpar); if ( ret < 0 ) { Error("Couldn't copy params to context"); @@ -133,181 +131,167 @@ bool VideoStore::open() { } else { zm_dump_codecpar(video_in_stream->codecpar); } -#else - video_in_ctx = video_in_stream->codec; - Debug(2,"Copied video context from input stream"); - zm_dump_codec(video_in_ctx); -#endif - } else { - // FIXME delete? - Debug(2, "No input ctx"); - video_in_ctx = avcodec_alloc_context3(NULL); - video_in_stream_index = 0; - } - video_out_ctx = avcodec_alloc_context3(NULL); - if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { + video_out_ctx = avcodec_alloc_context3(NULL); + if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) - video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; #else - video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; + video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; #endif - } - if ( !video_out_ctx->codec_tag ) { - video_out_ctx->codec_tag = + } + if ( !video_out_ctx->codec_tag ) { + video_out_ctx->codec_tag = av_codec_get_tag(oc->oformat->codec_tag, video_in_ctx->codec_id); - Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag); - } - int wanted_codec = monitor->OutputCodec(); - if ( !wanted_codec ) { - // default to h264 - wanted_codec = AV_CODEC_ID_H264; - } - max_stream_index = video_out_stream->index; + Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag); + } + int wanted_codec = monitor->OutputCodec(); + if ( !wanted_codec ) { + // default to h264 + Debug(2, "Defaulting to H264"); + wanted_codec = AV_CODEC_ID_H264; + } + + //max_stream_index = video_out_stream->index; // FIXME Should check that we are set to passthrough. Might be same codec, but want privacy overlays - if ( video_in_stream && ( video_in_ctx->codec_id == wanted_codec ) ) { - // Copy params from instream to ctx + if ( video_in_ctx->codec_id == wanted_codec ) { + // Copy params from instream to ctx #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - ret = avcodec_parameters_to_context(video_out_ctx, video_in_stream->codecpar); + ret = avcodec_parameters_to_context(video_out_ctx, video_in_stream->codecpar); #else - ret = avcodec_copy_context(video_out_ctx, video_in_ctx); + ret = avcodec_copy_context(video_out_ctx, video_in_ctx); #endif - if ( ret < 0 ) { - Error("Could not initialize ctx parameteres"); - return false; - } - //video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate - video_out_ctx->time_base = video_in_ctx->time_base; - // Fix deprecated formats - switch ( video_out_ctx->pix_fmt ) { - case AV_PIX_FMT_YUVJ422P : - video_out_ctx->pix_fmt = AV_PIX_FMT_YUV422P; - break; - case AV_PIX_FMT_YUVJ444P : - video_out_ctx->pix_fmt = AV_PIX_FMT_YUV444P; - break; - case AV_PIX_FMT_YUVJ440P : - video_out_ctx->pix_fmt = AV_PIX_FMT_YUV440P; - break; - case AV_PIX_FMT_NONE : - case AV_PIX_FMT_YUVJ420P : - default: - video_out_ctx->pix_fmt = AV_PIX_FMT_YUV420P; - break; - } - // Only set orientation if doing passthrough, otherwise the frame image will be rotated - Monitor::Orientation orientation = monitor->getOrientation(); - if ( orientation ) { - Debug(3, "Have orientation"); - if ( orientation == Monitor::ROTATE_0 ) { - } else if ( orientation == Monitor::ROTATE_90 ) { - ret = av_dict_set(&video_out_stream->metadata, "rotate", "90", 0); - if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); - } else if ( orientation == Monitor::ROTATE_180 ) { - ret = av_dict_set(&video_out_stream->metadata, "rotate", "180", 0); - if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); - } else if ( orientation == Monitor::ROTATE_270 ) { - ret = av_dict_set(&video_out_stream->metadata, "rotate", "270", 0); - if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); - } else { - Warning("Unsupported Orientation(%d)", orientation); - } - } // end if orientation - } else { // Either no video in or not the desired codec - for ( unsigned int i = 0; i < sizeof(codec_data) / sizeof(*codec_data); i++ ) { - if ( codec_data[i].codec_id != monitor->OutputCodec() ) - continue; - - video_out_codec = avcodec_find_encoder_by_name(codec_data[i].codec_name); - if ( ! video_out_codec ) { - Debug(1, "Didn't find encoder for %s", codec_data[i].codec_name); - continue; - } - - video_out_ctx->pix_fmt = codec_data[i].pix_fmt; - video_out_ctx->level = 32; - - // Don't have an input stream, so need to tell it what we are sending it, or are transcoding - video_out_ctx->width = monitor->Width(); - video_out_ctx->height = monitor->Height(); - video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO; - - // Just copy them from the in, no reason to choose different - video_out_ctx->time_base = video_in_ctx->time_base; - if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) { - Debug(2,"No timebase found in video in context, defaulting to Q"); - video_out_ctx->time_base = AV_TIME_BASE_Q; - } - video_out_stream->time_base = video_in_stream ? video_in_stream->time_base : AV_TIME_BASE_Q; - - if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { - video_out_ctx->max_b_frames = 1; - if ( video_out_ctx->priv_data ) { - //av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); - //av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); - } else { - Debug(2, "Not setting priv_data"); - } - } else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO ) { - /* just for testing, we also add B frames */ - video_out_ctx->max_b_frames = 2; - } else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO ) { - /* Needed to avoid using macroblocks in which some coeffs overflow. - * This does not happen with normal video, it just happens here as - * the motion of the chroma plane does not match the luma plane. */ - video_out_ctx->mb_decision = 2; - } - - AVDictionary *opts = 0; - std::string Options = monitor->GetEncoderOptions(); - ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0); if ( ret < 0 ) { - Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str()); - } else { + Error("Could not initialize ctx parameters"); + return false; + } + //video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate + video_out_ctx->time_base = video_in_ctx->time_base; + // Fix deprecated formats + switch ( video_out_ctx->pix_fmt ) { + case AV_PIX_FMT_YUVJ422P : + video_out_ctx->pix_fmt = AV_PIX_FMT_YUV422P; + break; + case AV_PIX_FMT_YUVJ444P : + video_out_ctx->pix_fmt = AV_PIX_FMT_YUV444P; + break; + case AV_PIX_FMT_YUVJ440P : + video_out_ctx->pix_fmt = AV_PIX_FMT_YUV440P; + break; + case AV_PIX_FMT_NONE : + case AV_PIX_FMT_YUVJ420P : + default: + video_out_ctx->pix_fmt = AV_PIX_FMT_YUV420P; + break; + } + // Only set orientation if doing passthrough, otherwise the frame image will be rotated + Monitor::Orientation orientation = monitor->getOrientation(); + if ( orientation ) { + Debug(3, "Have orientation"); + if ( orientation == Monitor::ROTATE_0 ) { + } else if ( orientation == Monitor::ROTATE_90 ) { + ret = av_dict_set(&video_out_stream->metadata, "rotate", "90", 0); + if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); + } else if ( orientation == Monitor::ROTATE_180 ) { + ret = av_dict_set(&video_out_stream->metadata, "rotate", "180", 0); + if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); + } else if ( orientation == Monitor::ROTATE_270 ) { + ret = av_dict_set(&video_out_stream->metadata, "rotate", "270", 0); + if ( ret < 0 ) Warning("%s:%d: title set failed", __FILE__, __LINE__); + } else { + Warning("Unsupported Orientation(%d)", orientation); + } + } // end if orientation + } else { // Either no video in or not the desired codec + for ( unsigned int i = 0; i < sizeof(codec_data) / sizeof(*codec_data); i++ ) { + if ( codec_data[i].codec_id != monitor->OutputCodec() ) + continue; + + video_out_codec = avcodec_find_encoder_by_name(codec_data[i].codec_name); + if ( ! video_out_codec ) { + Debug(1, "Didn't find encoder for %s", codec_data[i].codec_name); + continue; + } + + video_out_ctx->pix_fmt = codec_data[i].pix_fmt; + video_out_ctx->level = 32; + + // Don't have an input stream, so need to tell it what we are sending it, or are transcoding + video_out_ctx->width = monitor->Width(); + video_out_ctx->height = monitor->Height(); + video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO; + + // Just copy them from the in, no reason to choose different + video_out_ctx->time_base = video_in_ctx->time_base; + if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) { + Debug(2,"No timebase found in video in context, defaulting to Q"); + video_out_ctx->time_base = AV_TIME_BASE_Q; + } + video_out_stream->time_base = video_in_stream ? video_in_stream->time_base : AV_TIME_BASE_Q; + + if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { + video_out_ctx->max_b_frames = 1; + if ( video_out_ctx->priv_data ) { + //av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); + //av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); + } else { + Debug(2, "Not setting priv_data"); + } + } else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO ) { + /* just for testing, we also add B frames */ + video_out_ctx->max_b_frames = 2; + } else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO ) { + /* Needed to avoid using macroblocks in which some coeffs overflow. + * This does not happen with normal video, it just happens here as + * the motion of the chroma plane does not match the luma plane. */ + video_out_ctx->mb_decision = 2; + } + + AVDictionary *opts = 0; + std::string Options = monitor->GetEncoderOptions(); + ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0); + if ( ret < 0 ) { + Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str()); + } else { + AVDictionaryEntry *e = NULL; + while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { + Debug(3, "Encoder Option %s=%s", e->key, e->value); + } + } + + if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { + Warning("Can't open video codec (%s) %s", + video_out_codec->name, + av_make_error_string(ret).c_str() + ); + video_out_codec = NULL; + } + AVDictionaryEntry *e = NULL; while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { - Debug(3, "Encoder Option %s=%s", e->key, e->value); + Warning("Encoder Option %s not recognized by ffmpeg codec", e->key); } - } + av_dict_free(&opts); + if ( video_out_codec ) break; - if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) { - Warning("Can't open video codec (%s) %s", - video_out_codec->name, - av_make_error_string(ret).c_str() - ); - video_out_codec = NULL; - } + } // end foreach codec - AVDictionaryEntry *e = NULL; - while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { - Warning("Encoder Option %s not recognized by ffmpeg codec", e->key); - } - av_dict_free(&opts); - if ( video_out_codec ) break; - - } // end foreach codec - - if ( !video_out_codec ) { - Error("Can't open video codec!"); + if ( !video_out_codec ) { + Error("Can't open video codec!"); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - // We allocate and copy in newer ffmpeg, so need to free it - avcodec_free_context(&video_out_ctx); + // We allocate and copy in newer ffmpeg, so need to free it + avcodec_free_context(&video_out_ctx); #endif - video_out_ctx = NULL; + video_out_ctx = NULL; - return false; - } // end if can't open codec + return false; + } // end if can't open codec - Debug(2, "Sucess opening codec"); + Debug(2, "Sucess opening codec"); - } // end if copying or transcoding - - if ( !video_out_ctx->codec_tag ) { - video_out_ctx->codec_tag = - av_codec_get_tag(oc->oformat->codec_tag, video_out_ctx->codec_id); - Debug(2, "No codec_tag, setting to h264 ? "); - } + } // end if copying or transcoding + } // end if video_in_stream video_out_stream = avformat_new_stream(oc, video_out_codec); if ( !video_out_stream ) { @@ -342,6 +326,7 @@ bool VideoStore::open() { audio_first_dts = 0; if ( audio_in_stream ) { + Debug(2, "Have audio_in_stream"); audio_in_stream_index = audio_in_stream->index; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) audio_in_ctx = avcodec_alloc_context3(NULL); @@ -470,7 +455,7 @@ bool VideoStore::open() { AVDictionary *opts = NULL; // av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); // Shiboleth reports that this may break seeking in mp4 before it downloads - //av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0); + av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0); // av_dict_set(&opts, "movflags", // "frag_keyframe+empty_moov+default_base_moof", 0); if ( (ret = avformat_write_header(oc, &opts)) < 0 ) { diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 48c0499c3..86b0ee2ed 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -53,7 +53,7 @@ void Zone::Setup( id = p_id; label = new char[strlen(p_label)+1]; - strcpy( label, p_label ); + strcpy(label, p_label); type = p_type; polygon = p_polygon; alarm_rgb = p_alarm_rgb; @@ -89,10 +89,10 @@ void Zone::Setup( overload_count = 0; extend_alarm_count = 0; - pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE ); + pg_image = new Image(monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE); pg_image->Clear(); - pg_image->Fill( 0xff, polygon ); - pg_image->Outline( 0xff, polygon ); + pg_image->Fill(0xff, polygon); + pg_image->Outline(0xff, polygon); ranges = new Range[monitor->Height()]; for ( unsigned int y = 0; y < monitor->Height(); y++ ) { @@ -113,8 +113,10 @@ void Zone::Setup( } if ( config.record_diag_images ) { - snprintf(diag_path, sizeof(diag_path), config.record_diag_images_fifo ? "%s/diagpipe-%d-poly.jpg" : "%s/diag-%d-poly.jpg", monitor->getStorage()->Path(), id); - if (config.record_diag_images_fifo) + snprintf(diag_path, sizeof(diag_path), + config.record_diag_images_fifo ? "%s/diagpipe-%d-poly.jpg" : "%s/diag-%d-poly.jpg", + monitor->getStorage()->Path(), id); + if ( config.record_diag_images_fifo ) FifoStream::fifo_create_if_missing(diag_path); pg_image->WriteJpeg(diag_path, config.record_diag_images_fifo); } else { diff --git a/src/zms.cpp b/src/zms.cpp index c2df27591..ad7076e7a 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -95,7 +95,7 @@ int main(int argc, const char *argv[]) { Debug(1, "Query: %s", query); char temp_query[1024]; - strncpy(temp_query, query, sizeof(temp_query)); + strncpy(temp_query, query, sizeof(temp_query)-1); char *q_ptr = temp_query; char *parms[16]; // Shouldn't be more than this int parm_no = 0; @@ -184,7 +184,7 @@ int main(int argc, const char *argv[]) { logInit(log_id_string); if ( config.opt_use_auth ) { - User *user = 0; + User *user = NULL; if ( jwt_token_str != "" ) { // user = zmLoadTokenUser(jwt_token_str, config.auth_hash_ips); @@ -195,19 +195,11 @@ int main(int argc, const char *argv[]) { } else { Error("Bad username"); } - } else { - // if ( strcmp( config.auth_relay, "hashed" ) == 0 ) - { - if ( *auth ) { - user = zmLoadAuthUser(auth, config.auth_hash_ips); - } - } - // else if ( strcmp( config.auth_relay, "plain" ) == 0 ) - { - if ( username.length() && password.length() ) { - user = zmLoadUser(username.c_str(), password.c_str()); - } + if ( *auth ) { + user = zmLoadAuthUser(auth, config.auth_hash_ips); + } else if ( username.length() && password.length() ) { + user = zmLoadUser(username.c_str(), password.c_str()); } } if ( !user ) { @@ -218,11 +210,15 @@ int main(int argc, const char *argv[]) { return 0; } if ( !ValidateAccess(user, monitor_id) ) { + delete user; + user = NULL; fputs("HTTP/1.0 403 Forbidden\r\n\r\n", stdout); logTerm(); zmDbClose(); return 0; } + delete user; + user = NULL; } // end if config.opt_use_auth hwcaps_detect(); @@ -336,5 +332,5 @@ int main(int argc, const char *argv[]) { logTerm(); zmDbClose(); - return(0); + return 0; } diff --git a/src/zmu.cpp b/src/zmu.cpp index b435b7483..12f8f9338 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -476,14 +476,12 @@ int main(int argc, char *argv[]) { } // end if auth if ( mon_id > 0 ) { - //fprintf(stderr,"Monitor %d\n", mon_id); Monitor *monitor = Monitor::Load(mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY); if ( monitor ) { - //fprintf(stderr,"Monitor %d(%s)\n", monitor->Id(), monitor->Name()); if ( verbose ) { printf("Monitor %d(%s)\n", monitor->Id(), monitor->Name()); } - if ( ! monitor->connect() ) { + if ( !monitor->connect() ) { Error("Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name()); exit_zmu(-1); } @@ -495,13 +493,13 @@ int main(int argc, char *argv[]) { if ( verbose ) { printf("Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle")); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); printf("%d", state); have_output = true; } } if ( function & ZMU_TIME ) { - struct timeval timestamp = monitor->GetTimestamp( image_idx ); + struct timeval timestamp = monitor->GetTimestamp(image_idx); if ( verbose ) { char timestamp_str[64] = "None"; if ( timestamp.tv_sec ) @@ -511,7 +509,7 @@ int main(int argc, char *argv[]) { else printf("Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); printf("%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000); have_output = true; } @@ -520,7 +518,7 @@ int main(int argc, char *argv[]) { if ( verbose ) printf("Last read index: %d\n", monitor->GetLastReadIndex()); else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); printf("%d", monitor->GetLastReadIndex()); have_output = true; } @@ -529,7 +527,7 @@ int main(int argc, char *argv[]) { if ( verbose ) { printf("Last write index: %d\n", monitor->GetLastWriteIndex()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); printf("%d", monitor->GetLastWriteIndex()); have_output = true; } @@ -538,16 +536,16 @@ int main(int argc, char *argv[]) { if ( verbose ) { printf("Last event id: %" PRIu64 "\n", monitor->GetLastEventId()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); printf("%" PRIu64, monitor->GetLastEventId()); have_output = true; } } if ( function & ZMU_FPS ) { - if ( verbose ) + if ( verbose ) { printf("Current capture rate: %.2f frames per second\n", monitor->GetFPS()); - else { - if ( have_output ) printf("%c", separator); + } else { + if ( have_output ) fputc(separator, stdout); printf("%.2f", monitor->GetFPS()); have_output = true; } @@ -573,10 +571,16 @@ int main(int argc, char *argv[]) { if ( monitor->GetFunction() == Monitor::Function::MONITOR ) { printf("A Monitor in monitor mode cannot handle alarms. Please use NoDect\n"); } else { - if ( verbose ) - printf("Forcing alarm on\n"); + Monitor::State state = monitor->GetState(); + + if ( verbose ) { + printf("Forcing alarm on current state: %s, event %" PRIu64 "\n", + state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle"), + monitor->GetLastEventId() + ); + } monitor->ForceAlarmOn(config.forced_alarm_score, "Forced Web"); - while ( (monitor->GetState() != Monitor::ALARM) && !zm_terminate ) { + while ( ((state = monitor->GetState()) != Monitor::ALARM) && !zm_terminate ) { // Wait for monitor to notice. usleep(1000); } @@ -630,7 +634,7 @@ int main(int argc, char *argv[]) { else printf("Current brightness: %d\n", monitor->actionBrightness()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); if ( brightness >= 0 ) printf("%d", monitor->actionBrightness(brightness)); else @@ -645,7 +649,7 @@ int main(int argc, char *argv[]) { else printf("Current contrast: %d\n", monitor->actionContrast()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); if ( contrast >= 0 ) printf("%d", monitor->actionContrast(contrast)); else @@ -660,7 +664,7 @@ int main(int argc, char *argv[]) { else printf("Current hue: %d\n", monitor->actionHue()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); if ( hue >= 0 ) printf("%d", monitor->actionHue(hue)); else @@ -675,7 +679,7 @@ int main(int argc, char *argv[]) { else printf("Current colour: %d\n", monitor->actionColour()); } else { - if ( have_output ) printf("%c", separator); + if ( have_output ) fputc(separator, stdout); if ( colour >= 0 ) printf("%d", monitor->actionColour(colour)); else @@ -708,11 +712,11 @@ int main(int argc, char *argv[]) { } if ( function & ZMU_LIST ) { - std::string sql = "select Id, Function+0 from Monitors"; + std::string sql = "SELECT `Id`, `Function`+0 FROM `Monitors`"; if ( !verbose ) { - sql += "where Function != 'None'"; + sql += "WHERE `Function` != 'None'"; } - sql += " order by Id asc"; + sql += " ORDER BY Id ASC"; if ( mysql_query(&dbconn, sql.c_str()) ) { Error("Can't run query: %s", mysql_error(&dbconn)); diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index 539205e3b..f09b879fd 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -80,7 +80,7 @@ fi; if [ "$DISTROS" == "" ]; then if [ "$RELEASE" != "" ]; then - DISTROS="xenial,bionic,disco,eoan,trusty" + DISTROS="xenial,bionic,eoan,focal" else DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`; fi; @@ -110,7 +110,12 @@ else fi; if [ "$SNAPSHOT" == "stable" ]; then if [ "$BRANCH" == "" ]; then - BRANCH=$(git describe --tags $(git rev-list --tags --max-count=1)); + #REV=$(git rev-list --tags --max-count=1) + BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`; + if [ "$BRANCH" == "" ]; then + echo "Unable to determine latest stable branch!" + exit 0; + fi echo "Latest stable branch is $BRANCH"; fi; else @@ -220,8 +225,8 @@ IFS=',' ;for DISTRO in `echo "$DISTROS"`; do fi; # Generate Changlog - if [ "$DISTRO" == "trusty" ] || [ "$DISTRO" == "precise" ]; then - cp -Rpd distros/ubuntu1204 debian + if [ "$DISTRO" == "focal" ] || [ "$DISTRO" == "buster" ]; then + cp -Rpd distros/ubuntu2004 debian else if [ "$DISTRO" == "wheezy" ]; then cp -Rpd distros/debian debian diff --git a/version b/version index 2aeaa11ee..0035f2a76 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.35.0 +1.35.2 diff --git a/web/ajax/event.php b/web/ajax/event.php index 9c2e57c2c..fb9000cc6 100644 --- a/web/ajax/event.php +++ b/web/ajax/event.php @@ -1,5 +1,5 @@ diff --git a/web/ajax/log.php b/web/ajax/log.php index 36a29adc3..d5483b7ca 100644 --- a/web/ajax/log.php +++ b/web/ajax/log.php @@ -1,14 +1,15 @@ $value ) { if ( !in_array($field, $filterFields) ) { - ZM\Error("'$field' is not in valid filter fields " . print_r($filterField,true)); + ZM\Error("'$field' is not in valid filter fields " . print_r($filterField, true)); continue; } - if ( $field == 'Level' ){ + if ( $field == 'Level' ) { $where[] = $field.' <= ?'; $values[] = $value; } else { @@ -69,8 +70,8 @@ switch ( $_REQUEST['task'] ) { $string = $_POST['message']; - $file = !empty($_POST['file']) ? preg_replace( '/\w+:\/\/[\w.:]+\//', '', $_POST['file'] ) : ''; - if ( !empty( $_POST['line'] ) ) { + $file = !empty($_POST['file']) ? preg_replace('/\w+:\/\/[\w.:]+\//', '', $_POST['file']) : ''; + if ( !empty($_POST['line']) ) { $line = validInt($_POST['line']); } else { $line = NULL; @@ -82,6 +83,8 @@ switch ( $_REQUEST['task'] ) { } $level = $levels[$_POST['level']]; ZM\Logger::fetch()->logPrint($level, $string, $file, $line); + } else { + ZM\Error('Invalid log create: '.print_r($_POST, true)); } ajaxResponse(); break; @@ -93,12 +96,7 @@ switch ( $_REQUEST['task'] ) { $query = buildLogQuery('DELETE'); $result = dbQuery($query['sql'], $query['values']); - ajaxResponse( array( - 'result'=>'Ok', - 'deleted'=>$result->rowCount(), - ) ); - - + ajaxResponse(array('result'=>'Ok', 'deleted'=>$result->rowCount())); } case 'query' : { @@ -107,10 +105,12 @@ switch ( $_REQUEST['task'] ) { $total = dbFetchOne('SELECT count(*) AS Total FROM Logs', 'Total'); $query = buildLogQuery('SELECT *'); - $servers = ZM\Server::find(); + global $Servers; + if ( !$Servers ) + $Servers = ZM\Server::find(); $servers_by_Id = array(); # There is probably a better way to do this. - foreach ( $servers as $server ) { + foreach ( $Servers as $server ) { $servers_by_Id[$server->Id()] = $server; } @@ -120,11 +120,9 @@ switch ( $_REQUEST['task'] ) { foreach ( dbFetchAll($query['sql'], NULL, $query['values']) as $log ) { $log['DateTime'] = strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])); - #Warning("TimeKey: " . $log['TimeKey'] . 'Intval:'.intval($log['TimeKey']).' DateTime:'.$log['DateTime']); - #$log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']); $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $log['Message'] = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $log['Message']); - foreach( $filterFields as $field ) { + foreach ( $filterFields as $field ) { if ( !isset($options[$field]) ) $options[$field] = array(); $value = $log[$field]; @@ -141,21 +139,21 @@ switch ( $_REQUEST['task'] ) { } } $logs[] = $log; - } + } # end foreach log db row foreach ( $options as $field => $values ) { asort($options[$field]); } $available = count($logs); - ajaxResponse( array( - 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG)?strftime(DATE_FMT_CONSOLE_LONG):date(DATE_FMT_CONSOLE_LONG), + ajaxResponse(array( + 'updated' => preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG), 'total' => $total, 'available' => isset($available) ? $available : $total, 'logs' => $logs, 'state' => logState(), 'options' => $options, - ) ); + )); break; } case 'export' : @@ -163,9 +161,9 @@ switch ( $_REQUEST['task'] ) { if ( !canView('System') ) ajaxError('Insufficient permissions to export logs'); - $minTime = isset($_POST['minTime'])?$_POST['minTime']:NULL; - $maxTime = isset($_POST['maxTime'])?$_POST['maxTime']:NULL; - if ( !is_null($minTime) && !is_null($maxTime) && $minTime > $maxTime ) { + $minTime = isset($_POST['minTime']) ? $_POST['minTime'] : NULL; + $maxTime = isset($_POST['maxTime']) ? $_POST['maxTime'] : NULL; + if ( !is_null($minTime) && !is_null($maxTime) && ($minTime > $maxTime) ) { $tempTime = $minTime; $minTime = $maxTime; $maxTime = $tempTime; @@ -174,15 +172,17 @@ switch ( $_REQUEST['task'] ) { $filter = isset($_POST['filter'])?$_POST['filter']:array(); $sortField = 'TimeKey'; if ( isset($_POST['sortField']) ) { - if ( ! in_array( $_POST['sortField'], $filterFields ) and ( $_POST['sortField'] != 'TimeKey' ) ) { - ZM\Error("Invalid sort field " . $_POST['sortField'] ); + if ( !in_array($_POST['sortField'], $filterFields) and ($_POST['sortField'] != 'TimeKey') ) { + ZM\Error('Invalid sort field '.$_POST['sortField']); } else { $sortField = $_POST['sortField']; } } - $sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc':'desc'; + $sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc' : 'desc'; - $servers = ZM\Server::find(); + global $Servers; + if ( !$Servers ) + $Servers = ZM\Server::find(); $servers_by_Id = array(); # There is probably a better way to do this. foreach ( $servers as $server ) { @@ -193,11 +193,9 @@ switch ( $_REQUEST['task'] ) { $where = array(); $values = array(); if ( $minTime ) { - ZM\Logger::Debug("MinTime: $minTime"); if ( preg_match('/(.+)(\.\d+)/', $minTime, $matches) ) { # This handles sub second precision $minTime = strtotime($matches[1]).$matches[2]; - ZM\Logger::Debug("MinTime: $minTime"); } else { $minTime = strtotime($minTime); } @@ -225,11 +223,11 @@ switch ( $_REQUEST['task'] ) { } } if ( count($where) ) - $sql.= ' WHERE '.join( ' AND ', $where ); + $sql.= ' WHERE '.join(' AND ', $where); $sql .= ' ORDER BY '.$sortField.' '.$sortOrder; //$sql .= " limit ".dbEscape($limit); - $format = isset($_POST['format'])?$_POST['format']:'text'; - switch( $format ) { + $format = isset($_POST['format']) ? $_POST['format'] : 'text'; + switch ( $format ) { case 'text' : $exportExt = 'txt'; break; @@ -245,43 +243,40 @@ switch ( $_REQUEST['task'] ) { default : ZM\Fatal("Unrecognised log export format '$format'"); } - $exportKey = substr(md5(rand()),0,8); - $exportFile = "zm-log.$exportExt"; - if ( ! file_exists(ZM_DIR_EXPORTS) ) { - ZM\Logger::Debug('Creating ' . ZM_DIR_EXPORTS); - if ( ! mkdir(ZM_DIR_EXPORTS) ) { - ZM\Fatal("Can't create exports dir at '".ZM_DIR_EXPORTS."'"); - } + $exportKey = substr(md5(rand()), 0, 8); + $exportFile = 'zm-log.'.$exportExt; + if ( ! ( mkdir(ZM_DIR_EXPORTS) || file_exists(ZM_DIR_EXPORTS) ) ) { + ZM\Fatal('Can\'t create exports dir at \''.ZM_DIR_EXPORTS.'\''); } - $exportPath = ZM_DIR_EXPORTS."/zm-log-$exportKey.$exportExt"; + $exportPath = ZM_DIR_EXPORTS.'/zm-log-'.$exportKey.$exportExt; ZM\Logger::Debug("Exporting to $exportPath"); if ( !($exportFP = fopen($exportPath, 'w')) ) ZM\Fatal("Unable to open log export file $exportPath"); $logs = array(); foreach ( dbFetchAll($sql, NULL, $values) as $log ) { - $log['DateTime'] = preg_replace('/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey']); + $log['DateTime'] = preg_replace('/^\d+/', strftime('%Y-%m-%d %H:%M:%S', intval($log['TimeKey'])), $log['TimeKey']); $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $logs[] = $log; } - ZM\Logger::Debug(count($logs)." lines being exported by $sql " . implode(',',$values)); + ZM\Logger::Debug(count($logs).' lines being exported by '.$sql.implode(',', $values)); switch( $format ) { case 'text' : { foreach ( $logs as $log ) { if ( $log['Line'] ) - fprintf( $exportFP, "%s %s[%d].%s-%s/%d [%s]\n", - $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Line'], $log['Message'] ); + fprintf($exportFP, "%s %s[%d].%s-%s/%d [%s]\n", + $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Line'], $log['Message']); else - fprintf( $exportFP, "%s %s[%d].%s-%s [%s]\n", - $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Message'] ); + fprintf($exportFP, "%s %s[%d].%s-%s [%s]\n", + $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Message']); } break; } case 'tsv' : { # This line doesn't need fprintf, it could use fwrite - fprintf( $exportFP, join( "\t", + fprintf($exportFP, join("\t", translate('DateTime'), translate('Component'), translate('Server'), @@ -290,17 +285,17 @@ switch ( $_REQUEST['task'] ) { translate('Message'), translate('File'), translate('Line') - )."\n" ); + )."\n"); foreach ( $logs as $log ) { - fprintf( $exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); + fprintf($exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n", + $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']); } break; } case 'html' : { - fwrite( $exportFP, - ' - + fwrite($exportFP, +' '.translate('ZoneMinderLog').' @@ -339,34 +334,36 @@ switch ( $_REQUEST['task'] ) {

'.translate('ZoneMinderLog').'

-

'.htmlspecialchars(preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG )).'

+

'.htmlspecialchars(preg_match('/%/', DATE_FMT_CONSOLE_LONG) ? strftime(DATE_FMT_CONSOLE_LONG) : date(DATE_FMT_CONSOLE_LONG)).'

'.count($logs).' '.translate('Logs').'

- ' ); + '); foreach ( $logs as $log ) { $classLevel = $log['Level']; - if ( $classLevel < ZM\Logger::FATAL ) + if ( $classLevel < ZM\Logger::FATAL ) { $classLevel = ZM\Logger::FATAL; - elseif ( $classLevel > ZM\Logger::DEBUG ) + } else if ( $classLevel > ZM\Logger::DEBUG ) { $classLevel = ZM\Logger::DEBUG; + } $logClass = 'log-'.strtolower(ZM\Logger::$codes[$classLevel]); - fprintf( $exportFP, " \n", $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); + fprintf($exportFP, ' + ', $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line']); } - fwrite( $exportFP, + fwrite($exportFP, '
'.translate('DateTime').''.translate('Component').''.translate('Server').''.translate('Pid').''.translate('Level').''.translate('Message').''.translate('File').''.translate('Line').'
%s%s%s%d%s%s%s%s
%s%s%s%d%s%s%s%s
- ' ); + '); break; } case 'xml' : { - fwrite( $exportFP, + fwrite($exportFP, ' - - '.$_POST['selector'].'' ); + + '.$_POST['selector'].''); foreach ( $filter as $field=>$value ) if ( $value != '' ) fwrite( $exportFP, @@ -381,7 +378,7 @@ switch ( $_REQUEST['task'] ) { ' ); foreach ( $logs as $log ) { fprintf( $exportFP, - " + ' %s %s %s @@ -390,7 +387,8 @@ switch ( $_REQUEST['task'] ) { %s %d - \n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] ); + +', $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] ); } fwrite( $exportFP, ' @@ -419,7 +417,7 @@ switch ( $_REQUEST['task'] ) { ZM\Fatal('No log export format given'); $format = $_REQUEST['format']; - switch( $format ) { + switch ( $format ) { case 'text' : $exportExt = 'txt'; break; @@ -436,15 +434,15 @@ switch ( $_REQUEST['task'] ) { ZM\Fatal("Unrecognised log export format '$format'"); } - $exportFile = "zm-log.$exportExt"; - $exportPath = ZM_DIR_EXPORTS."/zm-log-$exportKey.$exportExt"; + $exportFile = 'zm-log.'.$exportExt; + $exportPath = ZM_DIR_EXPORTS.'/zm-log-'.$exportKey.$exportExt; header('Pragma: public'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); - header('Cache-Control: private', false ); // required by certain browsers + header('Cache-Control: private', false); // required by certain browsers header('Content-Description: File Transfer'); - header('Content-Disposition: attachment; filename="'.$exportFile.'"' ); + header('Content-Disposition: attachment; filename="'.$exportFile.'"'); header('Content-Transfer-Encoding: binary'); header('Content-Type: application/force-download'); header('Content-Length: '.filesize($exportPath)); @@ -452,6 +450,6 @@ switch ( $_REQUEST['task'] ) { exit(0); break; } -} +} // end switch ( $_REQUEST['task'] ) ajaxError('Unrecognised action or insufficient permissions'); ?> diff --git a/web/ajax/status.php b/web/ajax/status.php index cf6e4498a..3a8d14d03 100644 --- a/web/ajax/status.php +++ b/web/ajax/status.php @@ -96,6 +96,7 @@ $statusData = array( 'selector' => 'Events.MonitorId', 'elements' => array( 'Id' => true, + 'MonitorId' => true, 'Name' => true, 'Cause' => true, 'Notes' => true, diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index d9ad29b0d..5188c18b4 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -149,6 +149,10 @@ class EventsController extends AppController { )); $event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id']; $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; + + $this->loadModel('Frame'); + $event['Event']['MaxScoreFrameId'] = $this->Frame->findByEventid($id,'FrameId',array('Score'=>'desc','FrameId'=>'asc'))['Frame']['FrameId']; + $event['Event']['AlarmFrameId'] = $this->Frame->findByEventidAndType($id,'Alarm')['Frame']['FrameId']; $this->set(array( 'event' => $event, diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 92114a558..3818f69d3 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -379,6 +379,8 @@ class MonitorsController extends AppController { $args = ''; if ( $daemon == 'zmc' and $monitor['Type'] == 'Local' ) { $args = '-d ' . $monitor['Device']; + } else if ( $daemon == 'zmcontrol.pl' ) { + $args = '--id '.$id; } else { $args = '-m ' . $id; } diff --git a/web/api/app/Model/Monitor.php b/web/api/app/Model/Monitor.php index aa959f429..2de0b6fcb 100644 --- a/web/api/app/Model/Monitor.php +++ b/web/api/app/Model/Monitor.php @@ -119,7 +119,7 @@ class Monitor extends AppModel { ); public $actsAs = array( 'CakePHP-Enum-Behavior.Enum' => array( - 'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite'), + 'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite', 'VNC'), 'Function' => array('None','Monitor','Modect','Record','Mocord','Nodect'), 'Orientation' => array('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT'), 'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'), diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 71bc8a658..48efd49b0 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -11,7 +11,7 @@ class Monitor extends ZM_Object { protected $defaults = array( 'Id' => null, - 'Name' => '', + 'Name' => array('type'=>'text','filter_regexp'=>'/[^\w\-\.\(\)\:\/ ]/'), 'Notes' => '', 'ServerId' => 0, 'StorageId' => 0, @@ -39,7 +39,7 @@ class Monitor extends ZM_Object { 'Height' => null, 'Colours' => 4, 'Palette' => '0', - 'Orientation' => null, + 'Orientation' => 'ROTATE_0', 'Deinterlacing' => 0, 'DecoderHWAccelName' => null, 'DecoderHWAccelDevice' => null, @@ -442,14 +442,17 @@ class Monitor extends ZM_Object { $source = ''; if ( $this->{'Type'} == 'Local' ) { $source = $this->{'Device'}.' ('.$this->{'Channel'}.')'; - } elseif ( $this->{'Type'} == 'Remote' ) { + } else if ( $this->{'Type'} == 'Remote' ) { $source = preg_replace( '/^.*@/', '', $this->{'Host'} ); if ( $this->{'Port'} != '80' and $this->{'Port'} != '554' ) { $source .= ':'.$this->{'Port'}; } - } elseif ( $this->{'Type'} == 'File' || $this->{'Type'} == 'cURL' ) { - $source = preg_replace( '/^.*\//', '', $this->{'Path'} ); - } elseif ( $this->{'Type'} == 'Ffmpeg' || $this->{'Type'} == 'Libvlc' || $this->{'Type'} == 'WebSite' ) { + } else if ( $this->{'Type'} == 'VNC' ) { + $source = preg_replace( '/^.*@/', '', $this->{'Host'} ); + if ( $this->{'Port'} != '5900' ) { + $source .= ':'.$this->{'Port'}; + } + } else if ( $this->{'Type'} == 'Ffmpeg' || $this->{'Type'} == 'Libvlc' || $this->{'Type'} == 'WebSite' ) { $url_parts = parse_url( $this->{'Path'} ); if ( ZM_WEB_FILTER_SOURCE == 'Hostname' ) { # Filter out everything but the hostname @@ -458,7 +461,7 @@ class Monitor extends ZM_Object { } else { $source = $this->{'Path'}; } - } elseif ( ZM_WEB_FILTER_SOURCE == 'NoCredentials' ) { + } else if ( ZM_WEB_FILTER_SOURCE == 'NoCredentials' ) { # Filter out sensitive and common items unset($url_parts['user']); unset($url_parts['pass']); @@ -496,8 +499,16 @@ class Monitor extends ZM_Object { } } if ( !count($options) ) { - if ( $command == 'quit' ) { - $options['command'] = 'quit'; + + if ( $command == 'quit' or $command == 'start' or $command == 'stop' ) { + # These are special as we now run zmcontrol as a daemon through zmdc. + $status = daemonStatus('zmcontrol.pl', array('--id', $this->{'Id'})); + Logger::Debug("Current status $status"); + if ( $status or ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) ) { + daemonControl($command, 'zmcontrol.pl', '--id '.$this->{'Id'}); + return; + } + $options['command'] = $command; } else { Warning("No commands to send to zmcontrol from $command"); return false; @@ -532,15 +543,15 @@ class Monitor extends ZM_Object { } else if ( $this->ServerId() ) { $Server = $this->Server(); - $url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zmcontrol.json'; + $url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$command.'/zmcontrol.pl.json'; if ( ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_RELAY == 'hashed' ) { - $url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS ); + $url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS); } else if ( ZM_AUTH_RELAY == 'plain' ) { - $url = '?user='.$_SESSION['username']; - $url = '?pass='.$_SESSION['password']; + $url .= '?user='.$_SESSION['username']; + $url .= '?pass='.$_SESSION['password']; } else if ( ZM_AUTH_RELAY == 'none' ) { - $url = '?user='.$_SESSION['username']; + $url .= '?user='.$_SESSION['username']; } } Logger::Debug("sending command to $url"); @@ -548,12 +559,12 @@ class Monitor extends ZM_Object { $context = stream_context_create(); try { $result = file_get_contents($url, false, $context); - if ($result === FALSE) { /* Handle error */ - Error("Error restarting zma using $url"); + if ( $result === FALSE ) { /* Handle error */ + Error("Error sending command using $url"); return false; } } catch ( Exception $e ) { - Error("Except $e thrown trying to restart zma"); + Error("Exception $e thrown trying to send command to $url"); return false; } } else { diff --git a/web/includes/Object.php b/web/includes/Object.php index 6f2e4d8bf..09be86a9e 100644 --- a/web/includes/Object.php +++ b/web/includes/Object.php @@ -39,10 +39,13 @@ class ZM_Object { public function __call($fn, array $args){ $type = (array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn])) ? $this->defaults[$fn]['type'] : 'scalar'; if ( count($args) ) { - if ( $type == 'set' and is_array($args[0]) ) + if ( $type == 'set' and is_array($args[0]) ) { $this->{$fn} = implode(',', $args[0]); - else + } else if ( array_key_exists($fn, $this->defaults) && is_array($this->defaults[$fn]) && isset($this->defaults[$fn]['filter_regexp']) ) { + $this->{$fn} = preg_replace($this->defaults[$fn]['filter_regexp'], '', $args[0]); + } else { $this->{$fn} = $args[0]; + } } if ( property_exists($this, $fn) ) { @@ -63,7 +66,7 @@ class ZM_Object { public static function _find($class, $parameters = null, $options = null ) { $table = $class::$table; $filters = array(); - $sql = "SELECT * FROM `$table` "; + $sql = 'SELECT * FROM `'.$table.'` '; $values = array(); if ( $parameters ) { @@ -164,24 +167,11 @@ class ZM_Object { # perhaps should turn into a comma-separated string $this->{$k} = implode(',', $v); } else if ( is_string($v) ) { -if ( 0 ) { -# Remarking this out. We are setting a value, not asking for a default to be set. -# So don't do defaults here, do them somewhere else - if ( ($v == null) and array_key_exists($k, $this->defaults) ) { -Logger::Debug("$k => Have default for $v: "); - if ( is_array($this->defaults[$k]) ) { - $this->{$k} = $this->defaults[$k]['default']; - } else { - $this->{$k} = $this->defaults[$k]; - Logger::Debug("$k => Have default for $v: " . $this->{$k}); - } - } else { + if ( array_key_exists($k, $this->defaults) && is_array($this->defaults[$k]) && isset($this->defaults[$k]['filter_regexp']) ) { + $this->{$k} = preg_replace($this->defaults[$k]['filter_regexp'], '', trim($v)); + } else { $this->{$k} = trim($v); } -} else { - $this->{$k} = trim($v); -} - } else if ( is_integer($v) ) { $this->{$k} = $v; } else if ( is_bool($v) ) { @@ -216,7 +206,8 @@ Logger::Debug("$k => Have default for $v: "); } } } # end foreach default - } + } # end if defaults + foreach ( $new_values as $field => $value ) { if ( method_exists($this, $field) ) { @@ -250,12 +241,15 @@ Logger::Debug("$k => Have default for $v: "); # Input might be a command separated string, or an array } else { + if ( array_key_exists($field, $this->defaults) && is_array($this->defaults[$field]) && isset($this->defaults[$field]['filter_regexp']) ) { + $value = preg_replace($this->defaults[$field]['filter_regexp'], '', trim($value)); + } if ( $this->{$field} != $value ) { $changes[$field] = $value; } } } else if ( array_key_exists($field, $this->defaults) ) { - if ( is_array($this->defaults[$field]) ) { + if ( is_array($this->defaults[$field]) and isset($this->defaults[$field]['default']) ) { $default = $this->defaults[$field]['default']; } else { $default = $this->defaults[$field]; @@ -270,17 +264,6 @@ Logger::Debug("$k => Have default for $v: "); $changes[$field] = $value; } } - - #if ( (!array_key_exists($field, $this)) or ( $this->{$field} != $new_values[$field] ) ) { - #Logger::Debug("Checking default $field => $default_value changes becaause" . $new_values[$field].' != '.$new_values[$field]); - #$changes[$field] = $new_values[$field]; - ##} else if { - #Logger::Debug("Checking default $field => $default_value changes becaause " . $new_values[$field].' != '.$new_values[$field]); - ##array_push( $changes, [$field=>$defaults[$field]] ); - #} - #} else { - #Logger::Debug("Checking default $field => $default_value not in new_values"); - #} } # end foreach newvalue @@ -299,7 +282,7 @@ Logger::Debug("$k => Have default for $v: "); # Set defaults. Note that we only replace "" with null, not other values # because for example if we want to clear TimestampFormat, we clear it, but the default is a string value foreach ( $this->defaults as $field => $default ) { - if ( (!property_exists($this, $field)) or ($this->{$field} == '') ) { + if ( (!property_exists($this, $field)) or ($this->{$field} === '') ) { if ( is_array($default) ) { $this->{$field} = $default['default']; } else if ( $default == null ) { diff --git a/web/includes/Storage.php b/web/includes/Storage.php index 695da80c7..8a397db97 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -16,6 +16,7 @@ class Storage extends ZM_Object { 'Scheme' => 'Medium', 'ServerId' => 0, 'DoDelete' => 1, + 'Enabled' => 1, ); public static function find($parameters = array(), $options = array()) { return ZM_Object::_find(get_class(), $parameters, $options); diff --git a/web/includes/actions/group.php b/web/includes/actions/group.php index 9aa988540..b23659d18 100644 --- a/web/includes/actions/group.php +++ b/web/includes/actions/group.php @@ -21,42 +21,31 @@ // Group edit actions # Should probably verify that each monitor id is a valid monitor, that we have access to. # However at the moment, you have to have System permissions to do this -if ( ! canEdit('Groups') ) { +if ( !canEdit('Groups') ) { ZM\Warning('Need group edit permissions to edit groups'); return; } if ( $action == 'Save' ) { - $monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']); $group_id = null; - if ( !empty($_POST['gid']) ) { + if ( !empty($_POST['gid']) ) $group_id = $_POST['gid']; - dbQuery( - 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?', + $group = new ZM\Group($group_id); + $group->save( array( - $_POST['newGroup']['Name'], - ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), - $group_id, + 'Name'=> $_POST['newGroup']['Name'], + 'ParentId'=>( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), ) ); - dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id)); - } else { - dbQuery( - 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)', - array( - $_POST['newGroup']['Name'], - ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), - ) - ); - $group_id = dbInsertId(); - } + dbQuery('DELETE FROM `Groups_Monitors` WHERE `GroupId`=?', array($group_id)); + $group_id = $group->Id(); if ( $group_id ) { foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) { - dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid)); + dbQuery('INSERT INTO `Groups_Monitors` (`GroupId`,`MonitorId`) VALUES (?,?)', array($group_id, $mid)); } } $view = 'none'; $refreshParent = true; $closePopup = true; -} +} ?> diff --git a/web/includes/actions/login.php b/web/includes/actions/login.php index 68ca4e604..da1b60ed2 100644 --- a/web/includes/actions/login.php +++ b/web/includes/actions/login.php @@ -29,8 +29,13 @@ if ( ('login' == $action) && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == ' && defined('ZM_OPT_GOOG_RECAPTCHA_SITEKEY') && ZM_OPT_USE_GOOG_RECAPTCHA && ZM_OPT_GOOG_RECAPTCHA_SECRETKEY - && ZM_OPT_GOOG_RECAPTCHA_SITEKEY ) - { + && ZM_OPT_GOOG_RECAPTCHA_SITEKEY + ) { + if ( !isset($_REQUEST['g-recaptcha-response']) ) { + ZM\Error('reCaptcha authentication failed. No g-recpatcha-response in REQUEST: '); + unset($user); // unset should be ok here because we aren't in a function + return; + } $url = 'https://www.google.com/recaptcha/api/siteverify'; $fields = array ( 'secret' => ZM_OPT_GOOG_RECAPTCHA_SECRETKEY, diff --git a/web/includes/actions/monitor.php b/web/includes/actions/monitor.php index 94e527e29..78b64e13c 100644 --- a/web/includes/actions/monitor.php +++ b/web/includes/actions/monitor.php @@ -92,6 +92,9 @@ if ( $action == 'monitor' ) { if ( $monitor->Type() != 'WebSite' ) { $monitor->zmaControl('stop'); $monitor->zmcControl('stop'); + if ( $monitor->Controllable() ) { + $monitor->sendControlCommand('stop'); + } } # These are used in updating zones @@ -264,8 +267,7 @@ if ( $action == 'monitor' ) { $monitor->zmaControl('start'); if ( $monitor->Controllable() ) { - require_once('includes/control_functions.php'); - $monitor->sendControlCommand('quit'); + $monitor->sendControlCommand('start'); } } // really should thump zmwatch and maybe zmtrigger too. diff --git a/web/includes/actions/montage.php b/web/includes/actions/montage.php index 207c9a0f0..cd9a41aaf 100644 --- a/web/includes/actions/montage.php +++ b/web/includes/actions/montage.php @@ -36,7 +36,7 @@ if ( isset($_REQUEST['object']) ) { } $Layout->Positions($_REQUEST['Positions']); $Layout->save(); - session_start(); + zm_session_start(); $_SESSION['zmMontageLayout'] = $Layout->Id(); setcookie('zmMontageLayout', $Layout->Id(), 1); session_write_close(); diff --git a/web/includes/actions/version.php b/web/includes/actions/version.php index fde85427f..a203e1dd3 100644 --- a/web/includes/actions/version.php +++ b/web/includes/actions/version.php @@ -42,6 +42,8 @@ if ( $action == 'version' && isset($_REQUEST['option']) ) { $nextReminder += 24*60*60; } elseif ( $option == 'week' ) { $nextReminder += 7*24*60*60; + } elseif ( $option == 'month' ) { + $nextReminder += 30*24*60*60; } dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_NEXT_REMINDER'"); break; diff --git a/web/includes/config.php.in b/web/includes/config.php.in index 57064f22d..7052a061a 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -193,7 +193,7 @@ if ( ! defined('ZM_SERVER_ID') ) { } } -if ( ZM_TIMEZONE ) +if ( defined('ZM_TIMEZONE') and ZM_TIMEZONE ) ini_set('date.timezone', ZM_TIMEZONE); function process_configfile($configFile) { diff --git a/web/includes/functions.php b/web/includes/functions.php index e1b1e058f..f640569c3 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -39,7 +39,7 @@ function CSPHeaders($view, $nonce) { global $Servers; if ( ! $Servers ) $Servers = ZM\Server::find(); - + $additionalScriptSrc = implode(' ', array_map(function($S){return $S->Url();}, $Servers)); switch ($view) { case 'login': { @@ -47,7 +47,7 @@ function CSPHeaders($view, $nonce) { && defined('ZM_OPT_GOOG_RECAPTCHA_SITEKEY') && defined('ZM_OPT_GOOG_RECAPTCHA_SECRETKEY') && ZM_OPT_USE_GOOG_RECAPTCHA && ZM_OPT_GOOG_RECAPTCHA_SITEKEY && ZM_OPT_GOOG_RECAPTCHA_SECRETKEY) { - $additionalScriptSrc = ' https://www.google.com'; + $additionalScriptSrc .= ' https://www.google.com'; } // fall through } @@ -955,7 +955,7 @@ function reScale($dimension, $dummy) { $new_dimension = $dimension; for ( $i = 1; $i < func_num_args(); $i++ ) { $scale = func_get_arg($i); - if ( !empty($scale) && ($scale != '0') && ($scale != SCALE_BASE) ) + if ( !empty($scale) && ($scale != '0') && ($scale != 'auto') && ($scale != SCALE_BASE) ) $new_dimension = (int)(($new_dimension*$scale)/SCALE_BASE); } return $new_dimension; @@ -1065,7 +1065,7 @@ function parseSort($saveToSession=false, $querySep='&') { $sortColumn = 'E.StartTime'; break; } - if ( !$_REQUEST['sort_asc'] ) + if ( !isset($_REQUEST['sort_asc']) ) $_REQUEST['sort_asc'] = 0; $sortOrder = $_REQUEST['sort_asc'] ? 'asc' : 'desc'; $sortQuery = $querySep.'sort_field='.validHtmlStr($_REQUEST['sort_field']).$querySep.'sort_asc='.validHtmlStr($_REQUEST['sort_asc']); @@ -1235,6 +1235,7 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') { break; } $valueList = array(); + if ( !isset($term['val']) ) $term['val'] = ''; foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $term['val'])) as $value ) { switch ( $term['attr'] ) { @@ -2194,7 +2195,6 @@ function ajaxError($message, $code=HTTP_STATUS_OK) { if ( $code == HTTP_STATUS_OK ) { $response = array('result'=>'Error', 'message'=>$message); header('Content-type: application/json'); - #header('Content-type: text/plain'); exit(jsonEncode($response)); } header("HTTP/1.0 $code $message"); @@ -2211,7 +2211,6 @@ function ajaxResponse($result=false) { $response['message'] = $result; } header('Content-type: application/json'); - #header('Content-type: text/plain'); exit(jsonEncode($response)); } @@ -2643,4 +2642,19 @@ function random_colour() { str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT); } +function zm_random_bytes($length = 32){ + if ( !isset($length) || intval($length) <= 8 ) { + $length = 32; + } + if ( function_exists('random_bytes') ) { + return random_bytes($length); + } + if ( function_exists('mcrypt_create_iv') ) { + return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); + } + if ( function_exists('openssl_random_pseudo_bytes') ) { + return openssl_random_pseudo_bytes($length); + } + ZM\Error('No random_bytes function found.'); +} ?> diff --git a/web/includes/lang.php b/web/includes/lang.php index a567f7273..78a64a0e1 100644 --- a/web/includes/lang.php +++ b/web/includes/lang.php @@ -33,7 +33,7 @@ function loadLanguage($prefix='') { if ( $prefix ) $prefix = $prefix.'/'; - if ( isset($user['Language']) ) { + if ( isset($user['Language']) and $user['Language'] ) { $userLangFile = $prefix.'lang/'.$user['Language'].'.php'; if ( file_exists($userLangFile) ) { diff --git a/web/index.php b/web/index.php index 3625c9e35..b29a4e7e7 100644 --- a/web/index.php +++ b/web/index.php @@ -34,7 +34,7 @@ if ( version_compare(phpversion(), '4.1.0', '<') ) { } // Useful debugging lines for mobile devices -if ( true ) { +if ( false ) { ob_start(); phpinfo(INFO_VARIABLES); $fp = fopen('/tmp/env.html', 'w+'); @@ -52,6 +52,7 @@ require_once('includes/Event.php'); require_once('includes/Group.php'); require_once('includes/Monitor.php'); +global $Servers; $Servers = ZM\Server::find(); if ( @@ -154,8 +155,6 @@ if ( setcookie('zmCSS', $css, time()+3600*24*30*12*10); } - - # Running is global but only do the daemonCheck if it is actually needed $running = null; @@ -176,9 +175,8 @@ $user = null; if ( isset($_REQUEST['view']) ) $view = detaintPath($_REQUEST['view']); - # Add CSP Headers -$cspNonce = bin2hex(openssl_random_pseudo_bytes(16)); +$cspNonce = bin2hex(zm_random_bytes(16)); $request = null; if ( isset($_REQUEST['request']) ) diff --git a/web/js/logger.js b/web/js/logger.js index 0a1195043..20e52b726 100644 --- a/web/js/logger.js +++ b/web/js/logger.js @@ -49,16 +49,21 @@ function logReport( level, message, file, line ) { /* eslint-disable no-caller */ if ( arguments && arguments.callee && arguments.callee.caller && arguments.callee.caller.caller && arguments.callee.caller.caller.name ) { message += ' - '+arguments.callee.caller.caller.name+'()'; + //console.log("arguments"); + } else { + //message += new Error().stack; + //console.log("stack"); } /* eslint-enable no-caller */ if ( !debugReq ) { + debugParms = "view=request&request=log&task=create"; if ( Browser ) { - debugParms = "view=request&request=log&task=create&browser[name]="+Browser.name+"&browser[version]="+Browser.version+"&browser[platform]="+(Browser.Platform?Browser.Platform.name:'unknown'); + debugParms += "&browser[name]="+Browser.name+"&browser[version]="+Browser.version+"&browser[platform]="+(Browser.Platform?Browser.Platform.name:'unknown'); } else { - debugParms = "view=request&request=log&task=create&browser[name]=unknown&browser[version]=unknown&browser[platform]=unknown"; + debugParms += "&browser[name]=unknown&browser[version]=unknown&browser[platform]=unknown"; } - debugReq = new Request.JSON( {url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain'} ); + debugReq = new Request.JSON({url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain'}); } var requestParms = debugParms; requestParms += "&level="+level+"&message="+encodeURIComponent(message); @@ -71,57 +76,57 @@ function logReport( level, message, file, line ) { if ( line ) { requestParms += "&line="+line; } - debugReq.send( requestParms ); + debugReq.send(requestParms); } -function Panic( message ) { - console.error( message ); - logReport( "PNC", message ); - alert( "PANIC: "+message ); +function Panic(message) { + console.error(message); + logReport("PNC", message); + alert("PANIC: "+message); } -function Fatal( message ) { - console.error( message ); +function Fatal(message) { + console.error(message); logReport( "FAT", message ); alert( "FATAL: "+message ); } -function Error( message ) { - console.error( message ); - logReport( "ERR", message ); +function Error(message) { + console.error(message); + logReport("ERR", message); } -function Warning( message ) { - console.warn( message ); - logReport( "WAR", message ); +function Warning(message) { + console.warn(message); + logReport("WAR", message); } -function Info( message ) { - console.info( message ); - logReport( "INF", message ); +function Info(message) { + console.info(message); + logReport("INF", message); } -function Debug( message ) { - console.debug( message ); - //logReport( "DBG", message ); +function Debug(message) { + console.debug(message); + //logReport("DBG", message); } -function Dump( value, label ) { +function Dump(value, label) { if ( label ) { - console.debug( label+" => " ); + console.debug(label+" => "); } - console.debug( value ); + console.debug(value); } window.onerror = function( message, url, line ) { - logReport( "ERR", message, url, line ); + logReport("ERR", message, url, line); }; window.addEventListener("securitypolicyviolation", function logCSP(evt) { var level = evt.disposition == "enforce" ? "ERR" : "DBG"; var message = evt.blockedURI + " violated CSP " + evt.violatedDirective; - if (evt.sample) { + if ( evt.sample ) { message += " (Sample: " + evt.sample + ")"; } logReport(level, message, evt.sourceFile, evt.lineNumber); diff --git a/web/js/overlay.js b/web/js/overlay.js index 6517d2d7d..84547c53f 100644 --- a/web/js/overlay.js +++ b/web/js/overlay.js @@ -51,8 +51,8 @@ var Overlay = new Class({ }, show: function() { this.mask.show(); - window.addEventListener( 'resize', this.update.bind(this) ); - window.addEventListener( 'scroll', this.update.bind(this) ); + window.addEventListener( 'resize', this.update.bind(this), {passive: true} ); + window.addEventListener( 'scroll', this.update.bind(this), {passive: true} ); this.element.tween( 'opacity', [0, 1.0] ); this.element.show(); this.element.position(); @@ -80,8 +80,8 @@ var Overlay = new Class({ } updateOverlayLoading(); this.loading.setStyle( 'display', 'block' ); - window.addEventListener( 'resize', this.update.bind(this) ); - window.addEventListener( 'scroll', this.update.bind(this) ); + window.addEventListener( 'resize', this.update.bind(this), {passive: true} ); + window.addEventListener( 'scroll', this.update.bind(this), {passive: true} ); }, hideAnimation: function() { if ( this.loading ) { diff --git a/web/lang/cn_zh.php b/web/lang/cn_zh.php index 0a751023e..9cdd9c4f1 100644 --- a/web/lang/cn_zh.php +++ b/web/lang/cn_zh.php @@ -74,12 +74,14 @@ $SLANG = array( '24BitColour' => '24 位彩色', '32BitColour' => '32 位彩色', // Added - 2011-06-15 '8BitGrey' => '8 位灰度', + 'API' => 'API', // Added - 2020-04-09 + 'APIEnabled' => 'API已启用', // Added - 2020-04-09 'Action' => '活动动作', 'Actual' => '实际', 'AddNewControl' => '新建控制', 'AddNewMonitor' => '新建监视器', - 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 - 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 + 'AddNewServer' => '新建服务器', // Added - 2018-08-30 + 'AddNewStorage' => '新建存储', // Added - 2018-08-30 'AddNewUser' => '新建用户', 'AddNewZone' => '新建区域', 'Alarm' => '报警', @@ -90,11 +92,12 @@ $SLANG = array( 'AlarmMaximumFPS' => '报警最大帧率FPS', 'AlarmPx' => '报警像素', 'AlarmRGBUnset' => '你必须设置一个报警颜色(RGB)', - 'AlarmRefImageBlendPct'=> 'Alarm Reference Image Blend %ge', // Added - 2015-04-18 + 'AlarmRefImageBlendPct'=> '报警参考影像混合 %ge', // Added - 2015-04-18 'Alert' => '警报', 'All' => '全部', - 'AnalysisFPS' => 'Analysis FPS', // Added - 2015-07-22 - 'AnalysisUpdateDelay' => 'Analysis Update Delay', // Added - 2015-07-23 + 'AllTokensRevoked' => '已撤销所有tokens', // Added - 2020-04-09 + 'AnalysisFPS' => '分析帧率 FPS', // Added - 2015-07-22 + 'AnalysisUpdateDelay' => '分析更新延迟', // Added - 2015-07-23 'Apply' => '应用', 'ApplyingStateChange' => '状态改变生效', 'ArchArchived' => '仅限于存档', @@ -107,30 +110,31 @@ $SLANG = array( 'AttrArchiveStatus' => '存档状态', 'AttrAvgScore' => '平均分数', 'AttrCause' => '原因', - 'AttrDiskBlocks' => '磁碟区块', - 'AttrDiskPercent' => '磁碟百分比', - 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 + 'AttrDiskBlocks' => '磁盘区块', + 'AttrDiskPercent' => '磁盘百分比', + 'AttrDiskSpace' => '磁盘空间', // Added - 2018-08-30 'AttrDuration' => '过程', - 'AttrEndDate' => 'End Date', // Added - 2018-08-30 - 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 - 'AttrEndTime' => 'End Time', // Added - 2018-08-30 - 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 - 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 + 'AttrEndDate' => '结束日期', // Added - 2018-08-30 + 'AttrEndDateTime' => '结束日期/时间', // Added - 2018-08-30 + 'AttrEndTime' => '结束时间', // Added - 2018-08-30 + 'AttrEndWeekday' => '结束星期', // Added - 2018-08-30 + 'AttrFilterServer' => '过滤服务正运行在', // Added - 2018-08-30 'AttrFrames' => '帧', 'AttrId' => 'Id', 'AttrMaxScore' => '最大分数', 'AttrMonitorId' => '监视器 Id', 'AttrMonitorName' => '监视器名称', - 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 + 'AttrMonitorServer' => '监控服务正运行在', // Added - 2018-08-30 'AttrName' => '名称', 'AttrNotes' => '备注', - 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 - 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 - 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 - 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 - 'AttrStateId' => 'Run State', // Added - 2018-08-30 - 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 - 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 + 'AttrSecondaryStorageArea'=> '第二存储区域', // Added - 2020-04-09 + 'AttrStartDate' => '开始日期', // Added - 2018-08-30 + 'AttrStartDateTime' => '开始日期/时间', // Added - 2018-08-30 + 'AttrStartTime' => '开始时间', // Added - 2018-08-30 + 'AttrStartWeekday' => '开始星期', // Added - 2018-08-30 + 'AttrStateId' => '运行状态', // Added - 2018-08-30 + 'AttrStorageArea' => '存储区域', // Added - 2018-08-30 + 'AttrStorageServer' => '存储服务器', // Added - 2018-08-30 'AttrSystemLoad' => '系统负载', 'AttrTotalScore' => '总分数', 'Auto' => '自动', @@ -141,10 +145,10 @@ $SLANG = array( 'BackgroundFilter' => '在后台运行筛选器', 'BadAlarmFrameCount' => '报警帧数必须设为大于1的整数', 'BadAlarmMaxFPS' => '报警最大帧率必须是正整数或正浮点数', - 'BadAnalysisFPS' => 'Analysis FPS must be a positive integer or floating point value', // Added - 2015-07-22 - 'BadAnalysisUpdateDelay'=> 'Analysis update delay must be set to an integer of zero or more', // Added - 2015-07-23 + 'BadAnalysisFPS' => '分析帧率 FPS 必须是正整数或正浮点数', // Added - 2015-07-22 + 'BadAnalysisUpdateDelay'=> '分析更新延迟必须设为大于零的整数', // Added - 2015-07-23 'BadChannel' => '通道必须设为大于零的整数', - 'BadColours' => 'Target colour must be set to a valid value', // Added - 2011-06-15 + 'BadColours' => '颜色必须设置为有效值', // Added - 2011-06-15 'BadDevice' => '必须为器件设置有效值', 'BadFPSReportInterval' => 'FPS帧数报告间隔缓冲数必须是0以上整数', 'BadFormat' => '格式必须设为大于零的整数', @@ -155,9 +159,10 @@ $SLANG = array( 'BadLabelX' => '标签 X 坐标必须设为大于零的整数', 'BadLabelY' => '标签 Y 坐标必须设为大于零的整数', 'BadMaxFPS' => '最大帧数FPS必须设为正整数或着浮点数', - 'BadMotionFrameSkip' => 'Motion Frame skip count must be an integer of zero or more', + 'BadMotionFrameSkip' => '运动跳帧数必须设为大于零的整数', 'BadNameChars' => '名称只可以包含字母,数字,波折号和下划线', - 'BadPalette' => 'Palette must be set to a valid value', // Added - 2009-03-31 + 'BadNoSaveJPEGsOrVideoWriter'=> '保存为JPEGs和保存为视频同时禁用后。不会有任何记录 ', // Added - 2020-04-09 + 'BadPalette' => '调色板必须设为有效值', // Added - 2009-03-31 'BadPath' => '路径必须设为有效值', 'BadPort' => '端口必须设为有效数字', 'BadPostEventCount' => '之后事件影像数目必须设为大于零的整数', @@ -165,11 +170,11 @@ $SLANG = array( 'BadRefBlendPerc' => '参考混合百分比必须设为一个正整数', 'BadSectionLength' => '节长度必须设为30的整数倍', 'BadSignalCheckColour' => '信号检查颜色必须设为有效的RGB颜色字符', - 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 + 'BadSourceType' => '源类型 \"网站\" 要求 功能 设置为 \"监视\"', // Added - 2018-08-30 'BadStreamReplayBuffer' => '流重放缓冲必须为零或更多整数', 'BadWarmupCount' => '预热帪必须设为零或更多整数', 'BadWebColour' => 'Web颜色必须设为有效Web颜色字符', - 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 + 'BadWebSitePath' => '请输入一个完整的网站链接,包括http://或https://前缀。', // Added - 2018-08-30 'BadWidth' => '宽度必须设为有效值', 'Bandwidth' => '带宽', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -177,9 +182,9 @@ $SLANG = array( 'BlobSizes' => 'Blob大小', 'Blobs' => 'Blobs', 'Brightness' => '亮度', - 'Buffer' => 'Buffer', // Added - 2015-04-18 + 'Buffer' => '缓冲', // Added - 2015-04-18 'Buffers' => '缓冲器', - 'CSSDescription' => 'Change the default css for this computer', // Added - 2015-04-18 + 'CSSDescription' => '改变本机默认css', // Added - 2015-04-18 'CanAutoFocus' => '可以自动对焦', 'CanAutoGain' => '可以自动增益控制', 'CanAutoIris' => '可以自动光圈', @@ -204,8 +209,8 @@ $SLANG = array( 'CanMoveMap' => '可以映射网格移动', 'CanMoveRel' => '可以相对移动', 'CanPan' => '可以平移' , + 'CanReboot' => '可以重启', 'CanReset' => '可以复位', - 'CanReboot' => 'Can Reboot', 'CanSetPresets' => '可以进行预设', 'CanSleep' => '可以休眠', 'CanTilt' => '可以倾斜', @@ -224,22 +229,23 @@ $SLANG = array( 'CaptureHeight' => '捕获高度', 'CaptureMethod' => '捕获方式', 'CapturePalette' => '捕获调色板', - 'CaptureResolution' => 'Capture Resolution', // Added - 2015-04-18 + 'CaptureResolution' => '捕获分辨率', // Added - 2015-04-18 'CaptureWidth' => '捕获宽度', 'Cause' => '原因', 'CheckMethod' => '报警检查方式', - 'ChooseDetectedCamera' => 'Choose Detected Camera', // Added - 2009-03-31 + 'ChooseDetectedCamera' => '选择检测到的摄像头', // Added - 2009-03-31 + 'ChooseDetectedProfile'=> '选择检测到的流媒体', // Added - 2020-04-09 'ChooseFilter' => '选择筛选器', - 'ChooseLogFormat' => 'Choose a log format', // Added - 2011-06-17 + 'ChooseLogFormat' => '选择日志格式', // Added - 2011-06-17 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => '选择预置', - 'Clear' => 'Clear', // Added - 2011-06-16 - 'CloneMonitor' => 'Clone', // Added - 2018-08-30 + 'Clear' => '清除', // Added - 2011-06-16 + 'CloneMonitor' => '克隆', // Added - 2018-08-30 'Close' => '关闭', 'Colour' => '彩色', 'Command' => '命令', - 'Component' => 'Component', // Added - 2011-06-16 - 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 + 'Component' => '组件', // Added - 2011-06-16 + 'ConcurrentFilter' => '同时应用筛选器', // Added - 2018-08-30 'Config' => '配置', 'ConfiguredFor' => '配置标的', 'ConfirmDeleteEvents' => '确认希望删除所选事件?', @@ -257,24 +263,25 @@ $SLANG = array( 'ControlDevice' => '控制设备', 'ControlType' => '控制类型', 'Controllable' => '可控', - 'Current' => 'Current', // Added - 2015-04-18 + 'Current' => '现在', // Added - 2015-04-18 'Cycle' => '循环', 'CycleWatch' => '循环监视', - 'DateTime' => 'Date/Time', // Added - 2011-06-16 + 'DateTime' => '日期', // Added - 2011-06-16 'Day' => '日', 'Debug' => '调试', + 'DefaultCodec' => '默认即时观看方法', // Added - 2020-04-09 'DefaultRate' => '缺省速率', 'DefaultScale' => '缺省缩放', 'DefaultView' => '缺省视角', - 'Deinterlacing' => 'Deinterlacing', // Added - 2015-04-18 - 'Delay' => 'Delay', // Added - 2015-04-18 + 'Deinterlacing' => '去隔行', // Added - 2015-04-18 + 'Delay' => '延迟', // Added - 2015-04-18 'Delete' => '删除', 'DeleteAndNext' => '删除并下一个', 'DeleteAndPrev' => '删除并前一个', 'DeleteSavedFilter' => '删除存储过滤器', 'Description' => '描述', - 'DetectedCameras' => 'Detected Cameras', // Added - 2009-03-31 - 'DetectedProfiles' => 'Detected Profiles', // Added - 2015-04-18 + 'DetectedCameras' => '检测到的摄像头', // Added - 2009-03-31 + 'DetectedProfiles' => '检测到的流媒体', // Added - 2015-04-18 'Device' => '设备', 'DeviceChannel' => '设备通道', 'DeviceFormat' => '设备格式', @@ -283,10 +290,10 @@ $SLANG = array( 'Devices' => '设备', 'Dimensions' => '维度', 'DisableAlarms' => '关闭警报', - 'Disk' => '磁碟', - 'Display' => 'Display', // Added - 2011-01-30 - 'Displaying' => 'Displaying', // Added - 2011-06-16 - 'DoNativeMotionDetection'=> 'Do Native Motion Detection', + 'Disk' => '磁盘', + 'Display' => '显示', // Added - 2011-01-30 + 'Displaying' => '正在显示', // Added - 2011-06-16 + 'DoNativeMotionDetection'=> '在本机进行运动检测', 'Donate' => '请捐款', 'DonateAlready' => '不,我已经捐赠过了', 'DonateEnticement' => '迄今,您已经运行ZoneMinder有一阵子了,希望它能够有助于增强您家或者办公区域的安全。尽管ZoneMinder是,并将保持免费和开源,该项目依然在研发和支持中投入了资金和精力。如果您愿意支持今后的开发和新功能,那么请考虑为该项目捐款。捐款不是必须的,任何数量的捐赠,我们都很感谢。

如果您愿意捐款,请选择下列选项,或者访问 https://zoneminder.com/donate/ 捐赠主页。

感谢您使用ZoneMinder,并且不要忘记访问访问ZoneMinder.com的论坛以获得支持或建议,这可以提升您的ZoneMinder的体验。', @@ -297,11 +304,11 @@ $SLANG = array( 'DonateRemindWeek' => '现在不,1星期内再次提醒我', 'DonateYes' => '好,我现在就捐款', 'Download' => '下载', - 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 + 'DownloadVideo' => '下载视频', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Duration', 'Edit' => '编辑', - 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 + 'EditLayout' => '编辑布局', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => '启动报警', 'Enabled' => '已启动', @@ -318,8 +325,9 @@ $SLANG = array( 'Events' => '事件', 'Exclude' => '排除', 'Execute' => '执行', - 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 + 'Exif' => '嵌入EXIF信息到图片', // Added - 2018-08-30 'Export' => '导出', + 'ExportCompress' => '使用压缩', // Added - 2020-04-09 'ExportDetails' => '导出时间详情', 'ExportFailed' => '导出失败', 'ExportFormat' => '导出文件格式', @@ -327,7 +335,8 @@ $SLANG = array( 'ExportFormatZip' => 'Zip', 'ExportFrames' => '导出帧详情', 'ExportImageFiles' => '导出影像文件', - 'ExportLog' => 'Export Log', // Added - 2011-06-17 + 'ExportLog' => '导出日志', // Added - 2011-06-17 + 'ExportMatches' => '导出匹配项', // Added - 2020-04-09 'ExportMiscFiles' => '导出其他文件 (如果存在)', 'ExportOptions' => '导出选项', 'ExportSucceeded' => '导出成功', @@ -341,29 +350,30 @@ $SLANG = array( 'Feed' => '转送源', 'Ffmpeg' => 'Ffmpeg', 'File' => '文件', - 'Filter' => 'Filter', // Added - 2015-04-18 - 'FilterArchiveEvents' => '将全部匹配项存档', - 'FilterDeleteEvents' => '将全部匹配项删除', - 'FilterEmailEvents' => '将全部匹配项详情电邮出去', + 'Filter' => '过滤器', // Added - 2015-04-18 + 'FilterArchiveEvents' => '存档全部匹配项', + 'FilterCopyEvents' => '复制全部匹配项', // Added - 2020-04-09 + 'FilterDeleteEvents' => '删除全部匹配项', + 'FilterEmailEvents' => '邮件发送全部匹配项详情', 'FilterExecuteEvents' => '执行全部匹配项命令', - 'FilterLog' => 'Filter log', // Added - 2015-04-18 + 'FilterLog' => '过滤日志', // Added - 2015-04-18 'FilterMessageEvents' => '全部匹配项的信息详情', - 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 + 'FilterMoveEvents' => '移除全部匹配项', // Added - 2018-08-30 'FilterPx' => '过滤器像素', 'FilterUnset' => '您必须指定过滤器宽度和高度', - 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 + 'FilterUpdateDiskSpace'=> '刷新磁盘空间', // Added - 2018-08-30 'FilterUploadEvents' => '上传全部匹配项', 'FilterVideoEvents' => '为全部匹配项创建视频', 'Filters' => '过滤器', 'First' => '首先', 'FlippedHori' => '水平翻转', 'FlippedVert' => '垂直翻转', - 'FnMocord' => 'Mocord', // Added 2013.08.16. - 'FnModect' => 'Modect', // Added 2013.08.16. - 'FnMonitor' => 'Monitor', // Added 2013.08.16. + 'FnMocord' => '运动侦测并录制', // Added 2013.08.16. + 'FnModect' => '运动侦测', // Added 2013.08.16. + 'FnMonitor' => '监视', // Added 2013.08.16. 'FnNodect' => 'Nodect', // Added 2013.08.16. - 'FnNone' => 'None', // Added 2013.08.16. - 'FnRecord' => 'Record', // Added 2013.08.16. + 'FnNone' => '无', // Added 2013.08.16. + 'FnRecord' => '录制', // Added 2013.08.16. 'Focus' => '聚焦', 'ForceAlarm' => '强制报警', 'Format' => '格式', @@ -376,7 +386,7 @@ $SLANG = array( 'Function' => '功能', 'Gain' => '增益', 'General' => '一般', - 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 + 'GenerateDownload' => '创建下载项', // Added - 2018-08-30 'GenerateVideo' => '创建视频', 'GeneratingVideo' => '正在创建视频', 'GoToZoneMinder' => '访问 ZoneMinder.com', @@ -397,7 +407,7 @@ $SLANG = array( 'High' => '高', 'HighBW' => '高 B/W', 'Home' => '主页', - 'Hostname' => 'Hostname', // Added - 2018-08-30 + 'Hostname' => '主机名', // Added - 2018-08-30 'Hour' => '小时', 'Hue' => '色调', 'Id' => 'Id', @@ -408,6 +418,7 @@ $SLANG = array( 'Images' => '影像', 'In' => '在', 'Include' => '包含', + 'InvalidateTokens' => '使所有创建的tokens无效', // Added - 2020-04-09 'Inverted' => '反向', 'Iris' => '光圈', 'KeyString' => '密钥字符', @@ -415,19 +426,19 @@ $SLANG = array( 'Language' => '语言', 'Last' => '最后', 'Layout' => '布局', - 'Level' => 'Level', // Added - 2011-06-16 + 'Level' => '级别', // Added - 2011-06-16 'Libvlc' => 'Libvlc', 'LimitResultsPost' => '个结果', // This is used at the end of the phrase 'Limit to first N results only' 'LimitResultsPre' => '仅限于开始', // This is used at the beginning of the phrase 'Limit to first N results only' - 'Line' => 'Line', // Added - 2011-06-16 + 'Line' => '行', // Added - 2011-06-16 'LinkedMonitors' => '管理监视器', 'List' => '列表', - 'ListMatches' => 'List Matches', // Added - 2018-08-30 + 'ListMatches' => '列出匹配项', // Added - 2018-08-30 'Load' => '加载', 'Local' => '本地', - 'Log' => 'Log', // Added - 2011-06-16 + 'Log' => '日志', // Added - 2011-06-16 'LoggedInAs' => '登录为', - 'Logging' => 'Logging', // Added - 2011-06-16 + 'Logging' => '日志', // Added - 2011-06-16 'LoggingIn' => '登录', 'Login' => '登入', 'Logout' => '登出', @@ -465,7 +476,7 @@ $SLANG = array( 'MaximumFPS' => '最大帧率 FPS', 'Medium' => '中等', 'MediumBW' => '中等 B/W', - 'Message' => 'Message', // Added - 2011-06-16 + 'Message' => '消息', // Added - 2011-06-16 'MinAlarmAreaLtMax' => '最小报警区域应该小于最大区域', 'MinAlarmAreaUnset' => '您必须指定最小报警像素数量', 'MinBlobAreaLtMax' => '最小blob区必须小数最大区域', @@ -500,25 +511,25 @@ $SLANG = array( 'MinZoomSpeed' => '最小缩放速度', 'MinZoomStep' => '最小缩放步进', 'Misc' => '杂项', - 'Mode' => 'Mode', // Added - 2015-04-18 + 'Mode' => '模式', // Added - 2015-04-18 'Monitor' => '监视器', 'MonitorIds' => '监视器 Ids', 'MonitorPreset' => '监视器预设值', 'MonitorPresetIntro' => '从以下列表中选择一个合适的预设值.

请注意该方式可能覆盖您为该监视器配置的数值.

', - 'MonitorProbe' => 'Monitor Probe', // Added - 2009-03-31 - 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 + 'MonitorProbe' => '监视器探测', // Added - 2009-03-31 + 'MonitorProbeIntro' => '以下列表显示了检测到的模拟和网络摄像头,以及其可用状态

请从列表中选择你想要的项

请注意可能有些摄像头并没有检测到,而且选择一个摄像头可能覆盖一些你已经设置的配置。

', // Added - 2009-03-31 'Monitors' => '监视器', 'Montage' => '镜头组接', 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => '月', - 'More' => 'More', // Added - 2011-06-16 - 'MotionFrameSkip' => 'Motion Frame Skip', + 'More' => '更多', // Added - 2011-06-16 + 'MotionFrameSkip' => '运动侦测跳帧', 'Move' => '移动', 'Mtg2widgrd' => '2-wide grid', // Added 2013.08.15. 'Mtg3widgrd' => '3-wide grid', // Added 2013.08.15. 'Mtg3widgrx' => '3-wide grid, scaled, enlarge on alarm', // Added 2013.08.15. 'Mtg4widgrd' => '4-wide grid', // Added 2013.08.15. - 'MtgDefault' => 'Default', // Added 2013.08.15. + 'MtgDefault' => '默认', // Added 2013.08.15. 'MustBeGe' => '必须大于等于', 'MustBeLe' => '必须小于等于', 'MustConfirmPassword' => '您必须确认密码', @@ -534,9 +545,10 @@ $SLANG = array( 'NewState' => '新状态', 'NewUser' => '新用户', 'Next' => '下一个', + 'NextMonitor' => '下一个监视器', // Added - 2020-04-09 'No' => '不', - 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 - 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 + 'NoDetectedCameras' => '没有检测到摄像头', // Added - 2009-03-31 + 'NoDetectedProfiles' => '没有检测到流媒体', // Added - 2018-08-30 'NoFramesRecorded' => '该事件没有相关帧的记录', 'NoGroup' => '无组', 'NoSavedFilters' => '没有保存过滤器', @@ -548,25 +560,27 @@ $SLANG = array( 'NumPresets' => '数值预置', 'Off' => '关', 'On' => '开', - 'OnvifCredentialsIntro'=> 'Please supply user name and password for the selected camera.
If no user has been created for the camera then the user given here will be created with the given password.

', // Added - 2015-04-18 + 'OnvifCredentialsIntro'=> '请为所选摄像头提供用户名和密码。
如果还没有为这台摄像头创建过用户,那么将会使用提供的用户名和密码创建用户

', // Added - 2015-04-18 'OnvifProbe' => 'ONVIF', // Added - 2015-04-18 - 'OnvifProbeIntro' => 'The list below shows detected ONVIF cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 + 'OnvifProbeIntro' => '以下列表显示了检测到的ONVIF摄像头和其可用状态。

选择一个你想要的项

请注意可能有些摄像头并没有检测到,而且选择一个摄像头可能覆盖一些你已经设置的配置。

', // Added - 2015-04-18 'OpEq' => '等于', 'OpGt' => '大于', 'OpGtEq' => '大于等于', 'OpIn' => '在集', - 'OpIs' => 'is', // Added - 2018-08-30 - 'OpIsNot' => 'is not', // Added - 2018-08-30 + 'OpIs' => '是', // Added - 2018-08-30 + 'OpIsNot' => '不是', // Added - 2018-08-30 + 'OpLike' => '包含', // Added - 2020-04-09 'OpLt' => '小于', 'OpLtEq' => '小于等于', 'OpMatches' => '匹配', 'OpNe' => '不等于', 'OpNotIn' => '未在集', + 'OpNotLike' => '未包含', // Added - 2020-04-09 'OpNotMatches' => '不匹配', 'Open' => '打开', 'OptionHelp' => '选项帮助', 'OptionRestartWarning' => '这些改动在系统运行时可以不会完全生效.\n 当你设置完毕改动后\n请确认\n您重新启动 ZoneMinder.', - 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 + 'OptionalEncoderParam' => '编码参数(可选)', // Added - 2018-08-30 'Options' => '选项', 'OrEnterNewName' => '或输入新名词', 'Order' => '次序', @@ -579,10 +593,15 @@ $SLANG = array( 'PanRight' => '向右平移', 'PanTilt' => '平移/倾斜', 'Parameter' => '参数', + 'ParentGroup' => '父组', // Added - 2020-04-09 'Password' => '密码', 'PasswordsDifferent' => '新建密码和确认密码不一致', + 'PathToApi' => 'Api路径', // Added - 2020-04-09 + 'PathToIndex' => 'Index路径', // Added - 2020-04-09 + 'PathToZMS' => 'ZMS路径', // Added - 2020-04-09 'Paths' => '路径', 'Pause' => '暂停', + 'PauseCycle' => 'Pause Cycle', // Added - 2020-04-09 'Phone' => '电话', 'PhoneBW' => '电话 B/W', 'Pid' => 'PID', // Added - 2011-06-16 @@ -590,8 +609,9 @@ $SLANG = array( 'Pixels' => '像素', 'Play' => '播放', 'PlayAll' => '播放全部', + 'PlayCycle' => 'Play Cycle', // Added - 2020-04-09 'PleaseWait' => '请等待', - 'Plugins' => 'Plugins', + 'Plugins' => '插件', 'Point' => '点', 'PostEventImageBuffer' => '事件之后影像数', 'PreEventImageBuffer' => '时间之前影像数', @@ -599,18 +619,31 @@ $SLANG = array( 'Preset' => '预置', 'Presets' => '预置', 'Prev' => '前', - 'Probe' => 'Probe', // Added - 2009-03-31 - 'ProfileProbe' => 'Stream Probe', // Added - 2015-04-18 - 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 + 'PreviousMonitor' => '前一个监视器', // Added - 2020-04-09 + 'Privacy' => 'Privacy', // Added - 2020-04-09 + 'PrivacyAbout' => '关于', // Added - 2020-04-09 + 'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.', // Added - 2020-04-09 + 'PrivacyConclusionText'=> 'We are NOT collecting any image specific data from your cameras. We don’t know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.', // Added - 2020-04-09 + 'PrivacyContact' => '联系', // Added - 2020-04-09 + 'PrivacyContactText' => 'Please contact us here for any questions regarding our privacy policy or to have your information removed.

For support, there are three primary ways to engage with the community:

Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.

', // Added - 2020-04-09 + 'PrivacyCookies' => 'Cookies', // Added - 2020-04-09 + 'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.', // Added - 2020-04-09 + 'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:
  • Id
  • Name
  • Type
  • Function
  • Width
  • Height
  • Colours
  • MaxFPS
  • AlarmMaxFPS
', // Added - 2020-04-09 + 'PrivacyTelemetry' => 'Telemetry', // Added - 2020-04-09 + 'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:
  • A unique identifier (UUID)
  • City based location is gathered by querying ipinfo.io. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!
  • Current time
  • Total number of monitors
  • Total number of events
  • System architecture
  • Operating system kernel, distro, and distro version
  • Version of ZoneMinder
  • Total amount of memory
  • Number of cpu cores
', // Added - 2020-04-09 + 'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.', // Added - 2020-04-09 + 'Probe' => '探测', // Added - 2009-03-31 + 'ProfileProbe' => '流媒体探测', // Added - 2015-04-18 + 'ProfileProbeIntro' => '以下列表显示了所选摄像头可用的流媒体。

从列表中选择一个你想要的项

请注意ZoneMinder不能设置额外的配置并且选择摄像头可能会覆盖一些你已设置的配置。

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => '协议', - 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 - 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 + 'RTSPDescribe' => '使用 RTSP Response 媒体链接', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP传输协议', // Added - 2018-08-30 'Rate' => '速率', 'Real' => '实际', - 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 + 'RecaptchaWarning' => '你的reCaptcha秘匙无效。 请更正,否则reCaptcha无法工作', // Added - 2018-08-30 'Record' => '记录', - 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 + 'RecordAudio' => '记录事件时保存音频.', // Added - 2018-08-30 'RefImageBlendPct' => '参考影像混合 %ge', 'Refresh' => '刷新', 'Remote' => '远程', @@ -626,7 +659,7 @@ $SLANG = array( 'ReplayAll' => '全部事件', 'ReplayGapless' => '无间隙事件', 'ReplaySingle' => '单一事件', - 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 + 'ReportEventAudit' => '事件报表', // Added - 2018-08-30 'Reset' => '重置', 'ResetEventCounts' => '重置事件数', 'Restart' => '重启动', @@ -635,24 +668,29 @@ $SLANG = array( 'RestrictedMonitors' => '受限监视器', 'ReturnDelay' => '返回延时', 'ReturnLocation' => '返回位置', + 'RevokeAllTokens' => '撤销所有Tokens', // Added - 2020-04-09 'Rewind' => '重绕', 'RotateLeft' => '向左旋转', 'RotateRight' => '向右旋转', - 'RunLocalUpdate' => 'Please run zmupdate.pl to update', // Added - 2011-05-25 + 'RunAudit' => '审计', // Added - 2020-04-09 + 'RunEventNotification' => '事件提醒', // Added - 2020-04-09 + 'RunLocalUpdate' => '请运行zmupdate.pl来更新', // Added - 2011-05-25 'RunMode' => '运行模式', 'RunState' => '运行状态', + 'RunStats' => '状态检测', // Added - 2020-04-09 + 'RunTrigger' => '触发', // Added - 2020-04-09 'Running' => '运行', 'Save' => '保存', 'SaveAs' => '另存为', 'SaveFilter' => '存储过滤器', - 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 + 'SaveJPEGs' => '保存为JPEGs', // Added - 2018-08-30 'Scale' => '比例', 'Score' => '分数', 'Secs' => '秒', 'Sectionlength' => '段长度', 'Select' => '选择', - 'SelectFormat' => 'Select Format', // Added - 2011-06-17 - 'SelectLog' => 'Select Log', // Added - 2011-06-17 + 'SelectFormat' => '选择格式', // Added - 2011-06-17 + 'SelectLog' => '选择日志', // Added - 2011-06-17 'SelectMonitors' => '选择监视器', 'SelfIntersecting' => '多边形边线不得交叉', 'Set' => '设置', @@ -661,10 +699,11 @@ $SLANG = array( 'Settings' => '设置', 'ShowFilterWindow' => '显示过滤器视窗', 'ShowTimeline' => '显示时间轴', - 'SignalCheckColour' => '型号检查颜色', - 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 + 'Shutdown' => '关机', // Added - 2020-04-09 + 'SignalCheckColour' => '信号检查颜色', + 'SignalCheckPoints' => '信号检测点数目', // Added - 2018-08-30 'Size' => '大小', - 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 + 'SkinDescription' => '改变本机默认皮肤', // Added - 2011-01-30 'Sleep' => '睡眠', 'SortAsc' => '升序', 'SortBy' => '排序', @@ -682,10 +721,10 @@ $SLANG = array( 'State' => '状态', 'Stats' => '统计', 'Status' => '状况', - 'StatusConnected' => 'Capturing', // Added - 2018-08-30 - 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 - 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 - 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 + 'StatusConnected' => '正在捕获', // Added - 2018-08-30 + 'StatusNotRunning' => '未运行', // Added - 2018-08-30 + 'StatusRunning' => '未捕获', // Added - 2018-08-30 + 'StatusUnknown' => '未知', // Added - 2018-08-30 'Step' => '步进', 'StepBack' => '单步后退', 'StepForward' => '单步前进', @@ -696,14 +735,16 @@ $SLANG = array( 'Stills' => '静止', 'Stop' => '停止', 'Stopped' => '已停止', - 'StorageArea' => 'Storage Area', // Added - 2018-08-30 - 'StorageScheme' => 'Scheme', // Added - 2018-08-30 + 'StorageArea' => '存储区域', // Added - 2018-08-30 + 'StorageDoDelete' => '开始删除', // Added - 2020-04-09 + 'StorageScheme' => '存储方案', // Added - 2018-08-30 'Stream' => '流', 'StreamReplayBuffer' => '流重放影像缓冲', 'Submit' => '发送', 'System' => '系统', - 'SystemLog' => 'System Log', // Added - 2011-06-16 - 'TargetColorspace' => 'Target colorspace', // Added - 2015-04-18 + 'SystemLog' => '系统日志', // Added - 2011-06-16 + 'TZUnset' => '未设置 - 使用php.ini的配置', // Added - 2020-04-09 + 'TargetColorspace' => '色彩空间', // Added - 2015-04-18 'Tele' => 'Tele', 'Thumbnail' => '缩略图', 'Tilt' => '倾斜', @@ -711,18 +752,18 @@ $SLANG = array( 'TimeDelta' => '相对时间', 'TimeStamp' => '时间戳', 'Timeline' => '时间轴', - 'TimelineTip1' => 'Pass your mouse over the graph to view a snapshot image and event details.', // Added 2013.08.15. - 'TimelineTip2' => 'Click on the coloured sections of the graph, or the image, to view the event.', // Added 2013.08.15. + 'TimelineTip1' => '移动你的鼠标到图表上来查看快照图片和事件详情。', // Added 2013.08.15. + 'TimelineTip2' => '单击图形或图像的彩色部分来查看事件。', // Added 2013.08.15. 'TimelineTip3' => 'Click on the background to zoom in to a smaller time period based around your click.', // Added 2013.08.15. 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => '时间戳', 'TimestampLabelFormat' => '时间戳标签格式', - 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 + 'TimestampLabelSize' => '字体大小', // Added - 2018-08-30 'TimestampLabelX' => '时间戳标签 X', 'TimestampLabelY' => '时间戳标签 Y', 'Today' => '今天', 'Tools' => '工具', - 'Total' => 'Total', // Added - 2011-06-16 + 'Total' => '总', // Added - 2011-06-16 'TotalBrScore' => '总
分数', 'TrackDelay' => '轨迹延时', 'TrackMotion' => '轨迹运动', @@ -737,23 +778,24 @@ $SLANG = array( 'Update' => '更新', 'UpdateAvailable' => '有新版本的ZoneMinder.', 'UpdateNotNecessary' => '无须更新', - 'Updated' => 'Updated', // Added - 2011-06-16 - 'Upload' => 'Upload', // Added - 2011-08-23 + 'Updated' => '已更新', // Added - 2011-06-16 + 'Upload' => '上传', // Added - 2011-08-23 'UseFilter' => '使用筛选器', 'UseFilterExprsPost' => ' 筛选器 表达式', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => '使用 ', // This is used at the beginning of the phrase 'use N filter expressions' - 'UsedPlugins' => 'Used Plugins', + 'UsedPlugins' => '已使用的插件', 'User' => '用户', 'Username' => '用户名', 'Users' => '用户', 'V4L' => 'V4L', // Added - 2015-04-18 - 'V4LCapturesPerFrame' => 'Captures Per Frame', // Added - 2015-04-18 - 'V4LMultiBuffer' => 'Multi Buffering', // Added - 2015-04-18 + 'V4LCapturesPerFrame' => '每帧捕获', // Added - 2015-04-18 + 'V4LMultiBuffer' => '多缓冲', // Added - 2015-04-18 'Value' => '数值', 'Version' => '版本', 'VersionIgnore' => '忽略该版本', 'VersionRemindDay' => '一天内再次提醒', 'VersionRemindHour' => '一小时内再次提醒', + 'VersionRemindMonth' => '一个月内再次提醒', // Added - 2020-04-09 'VersionRemindNever' => '不再提醒新版本', 'VersionRemindWeek' => '一周内再次提醒', 'Video' => '视频', @@ -764,17 +806,18 @@ $SLANG = array( 'VideoGenParms' => '视频产生参数', 'VideoGenSucceeded' => '视频产生成功!', 'VideoSize' => '视频尺寸', - 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 + 'VideoWriter' => '保存为视频', // Added - 2018-08-30 'View' => '查看', 'ViewAll' => '查看全部', 'ViewEvent' => '查看事件', + 'ViewMatches' => '查看匹配项', // Added - 2020-04-09 'ViewPaged' => '查看分页', 'Wake' => '唤醒', 'WarmupFrames' => '预热帪', 'Watch' => '观察', 'Web' => 'Web', 'WebColour' => 'Web颜色', - 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 + 'WebSiteUrl' => '网站链接', // Added - 2018-08-30 'Week' => '周', 'White' => '白', 'WhiteBalance' => '白平衡', @@ -797,7 +840,7 @@ $SLANG = array( 'ZoneMinMaxBlobs' => '最小/最大污渍区数 Blobs', 'ZoneMinMaxFiltArea' => '最小/最大过滤区域', 'ZoneMinMaxPixelThres' => '最小/最大像素阈值(0-255)', - 'ZoneMinderLog' => 'ZoneMinder Log', // Added - 2011-06-17 + 'ZoneMinderLog' => 'ZoneMinder日志', // Added - 2011-06-17 'ZoneOverloadFrames' => '忽略过载帪数', 'Zones' => '区域', 'Zoom' => '缩放', @@ -814,7 +857,7 @@ $CLANG = array( 'MonitorCount' => '%1$s %2$s', // For example '4 Monitors' (from Vlang below) 'MonitorFunction' => '监视器 %1$s 功能', 'RunningRecentVer' => '您运行的是最新版的 ZoneMinder, v%s.', - 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', // Added - 2011-05-25 + 'VersionMismatch' => '版本不匹配, 系统版本是 %1$s, 数据库版本是 %2$s.', // Added - 2011-05-25 ); // The next section allows you to describe a series of word ending and counts used to diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index 0570005d3..b2a91cd45 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -798,6 +798,7 @@ $SLANG = array( 'VersionRemindHour' => 'Remind again in 1 hour', 'VersionRemindNever' => 'Don\'t remind about new versions', 'VersionRemindWeek' => 'Remind again in 1 week', + 'VersionRemindMonth' => 'Remind again in 1 month', 'Version' => 'Version', 'ViewMatches' => 'View Matches', 'VideoFormat' => 'Video Format', diff --git a/web/skins/classic/css/base/export.css b/web/skins/classic/css/base/export.css index 757b49057..8cccfddad 100644 --- a/web/skins/classic/css/base/export.css +++ b/web/skins/classic/css/base/export.css @@ -52,3 +52,42 @@ td.monoRow { background-color: #ffffff; } +ul.tabs { + margin: 0; + margin-bottom: -1px; + padding: 0; + float: left; + list-style: none; + height: 32px; + border-bottom: 1px solid #7f7fb2; + border-left: 1px solid #7f7fb2; + width: 100%; +} +ul.tabs li { + float: left; + margin: 0; + padding: 0; + height: 31px; + line-height: 31px; + border: 1px solid #7f7fb2; + border-left: none; + margin-bottom: -1px; + background: #fff; + overflow: hidden; + position: relative; +} +ul.tabs li a { + text-decoration: none; + color: #000; + display: block; + font-size: 1.2em; + padding: 0 20px; + outline: none; +} +ul.tabs li a:hover { + background: #ccc; +} +html ul.tabs li.active, html ul.tabs li.active a:hover { + background: #dddddd; + border-bottom: 1px solid #e0e0e0; +} diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index a963dc0e7..cc58073e1 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -445,14 +445,14 @@ th.table-th-sort span.table-th-sort-span { float: right; width: 12px; height: 12px; - background: url("/skins/classic/graphics/arrow-s-u.png") no-repeat 0 0; + background: url("../skins/classic/graphics/arrow-s-u.png") no-repeat 0 0; } th.table-th-sort-rev span.table-th-sort-span { float: right; width: 12px; height: 12px; - background: url("/skins/classic/graphics/arrow-s-d.png") no-repeat 0 0; + background: url("../skins/classic/graphics/arrow-s-d.png") no-repeat 0 0; } .table-tr-odd { diff --git a/web/skins/classic/css/classic/export.css b/web/skins/classic/css/classic/export.css index 757b49057..e69de29bb 100644 --- a/web/skins/classic/css/classic/export.css +++ b/web/skins/classic/css/classic/export.css @@ -1,54 +0,0 @@ -body { - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size:10px; - font-weight: normal; - color: #333333; -} - -table { - border-collapse: collapse; -} - -th, td { - border: 1px solid #7f7fb2; - text-align: center; - padding: 2px 4px; -} - -a:link { - color: #7f7fb2; - text-decoration: none -} - -a:visited { - color: #7f7fb2; - text-decoration: none -} - -a:hover { - color: #666699; - text-decoration: underline -} - -img.thumb { - width: 40px; -} - -td.monoRow { - line-height: 200%; - text-align: center; - vertical-align: middle; -} - -#eventFrames tr.alarm { - background-color: #fa8072; -} - -#eventFrames tr.bulk { - background-color: #cccccc; -} - -#eventFrames tr.normal { - background-color: #ffffff; -} - diff --git a/web/skins/classic/css/dark/export.css b/web/skins/classic/css/dark/export.css index 757b49057..e69de29bb 100644 --- a/web/skins/classic/css/dark/export.css +++ b/web/skins/classic/css/dark/export.css @@ -1,54 +0,0 @@ -body { - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size:10px; - font-weight: normal; - color: #333333; -} - -table { - border-collapse: collapse; -} - -th, td { - border: 1px solid #7f7fb2; - text-align: center; - padding: 2px 4px; -} - -a:link { - color: #7f7fb2; - text-decoration: none -} - -a:visited { - color: #7f7fb2; - text-decoration: none -} - -a:hover { - color: #666699; - text-decoration: underline -} - -img.thumb { - width: 40px; -} - -td.monoRow { - line-height: 200%; - text-align: center; - vertical-align: middle; -} - -#eventFrames tr.alarm { - background-color: #fa8072; -} - -#eventFrames tr.bulk { - background-color: #cccccc; -} - -#eventFrames tr.normal { - background-color: #ffffff; -} - diff --git a/web/skins/classic/includes/control_functions.php b/web/skins/classic/includes/control_functions.php index 80cfe4aff..61138612f 100644 --- a/web/skins/classic/includes/control_functions.php +++ b/web/skins/classic/includes/control_functions.php @@ -25,15 +25,15 @@ function controlFocus($monitor, $cmds) { ?>
- - - + + +
CanAutoFocus() ) { ?> - - + + @@ -48,15 +48,15 @@ function controlZoom($monitor, $cmds) { ?>
- - - + + +
CanAutoZoom() ) { ?> - - + + @@ -70,15 +70,15 @@ function controlIris($monitor, $cmds) { ?>
- - - + + +
CanAutoIris() ) { ?> - - + + @@ -93,15 +93,15 @@ function controlWhite($monitor, $cmds) { ?>
- - - + + +
CanAutoWhite() ) { ?> - - + + @@ -122,19 +122,19 @@ function controlPanTilt($monitor, $cmds) { $hasTilt = $control->CanTilt(); $hasDiag = $hasPan && $hasTilt && $control->CanMoveDiag(); ?> - - - - + + + + - + - - - - + + + +
Id() ) ) as $row ) { + foreach ( dbFetchAll($sql, NULL, array($monitor->Id())) as $row ) { $labels[$row['Preset']] = $row['Label']; } @@ -162,7 +162,7 @@ function controlPresets($monitor, $cmds) { NumPresets(); $i++ ) { ?> - + @@ -171,7 +171,7 @@ function controlPresets($monitor, $cmds) { HasHomePreset() ) { ?> - + CanSetPresets() ) { @@ -196,22 +196,22 @@ function controlPower($monitor, $cmds) { CanWake() ) { ?> - + CanSleep() ) { ?> - + CanReset() ) { ?> - + CanReboot() ) { ?> - + @@ -223,9 +223,7 @@ function controlPower($monitor, $cmds) { function ptzControls($monitor) { $control = $monitor->Control(); - //ZM\Error("Control: " . print_r($control,true)); $cmds = $control->commands(); - //ZM\Error("Cmds: " . print_r($cmds, true)); ob_start(); ?>
diff --git a/web/skins/classic/includes/export_functions.php b/web/skins/classic/includes/export_functions.php index d43c697e3..5ec45b022 100644 --- a/web/skins/classic/includes/export_functions.php +++ b/web/skins/classic/includes/export_functions.php @@ -20,52 +20,18 @@ function exportHeader($title) { ?> + <?php echo $title ?> - @@ -95,21 +61,21 @@ html ul.tabs li.active, html ul.tabs li.active a:hover { Id()); $otherlinks = ''; - if( $exportFrames ) $otherlinks .= ''.translate('Frames').','; - if( $exportImages ) $otherlinks .= ''.translate('Images').','; - $otherlinks = substr($otherlinks,0,-1); + if ( $exportFrames ) $otherlinks .= ' '.translate('Frames').','; + if ( $exportImages ) $otherlinks .= ' '.translate('Images').','; + $otherlinks = substr($otherlinks, 0, -1); ?>
-

: Name()) ?> ()

+

Name()).( (!empty($otherlinks)) ? ' ('.$otherlinks.') ' : '' ) ?>

@@ -134,21 +100,21 @@ function exportEventDetail($event, $exportFrames, $exportImages) { } function exportEventFrames($event, $exportDetail, $exportImages) { - $sql = 'SELECT *, unix_timestamp( TimeStamp ) AS UnixTimeStamp FROM Frames WHERE EventID = ? ORDER BY FrameId'; + $sql = 'SELECT *, unix_timestamp(TimeStamp) AS UnixTimeStamp FROM Frames WHERE EventID = ? ORDER BY FrameId'; $frames = dbFetchAll($sql, NULL, array($event->Id())); ob_start(); exportHeader(translate('Frames').' '.$event->Id()); $otherlinks = ''; - if ( $exportDetail ) $otherlinks .= ''.translate('Event').','; - if ( $exportImages ) $otherlinks .= ''.translate('Images').','; - $otherlinks = substr($otherlinks,0,-1); + if ( $exportDetail ) $otherlinks .= ' '.translate('Event').','; + if ( $exportImages ) $otherlinks .= ' '.translate('Images').','; + $otherlinks = substr($otherlinks, 0, -1); ?>
-

: Name()) ?> ()

+

Name()).( (!empty($otherlinks)) ? ' ('.$otherlinks.')':'') ?>

Id() ?>
Name()) ?>
@@ -208,26 +174,45 @@ function exportEventFrames($event, $exportDetail, $exportImages) { Id()); $otherlinks = ''; - if( $exportDetail ) $otherlinks .= ''.translate('Event').','; - if( $exportFrames ) $otherlinks .= ''.translate('Frames').','; - $otherlinks = substr($otherlinks,0,-1); + if ( $exportDetail ) $otherlinks .= ' '.translate('Event').','; + if ( $exportFrames ) $otherlinks .= ' '.translate('Frames').','; + $otherlinks = substr($otherlinks, 0, -1); $filelist = array_keys($myfilelist); - sort($filelist,SORT_NUMERIC); + sort($filelist, SORT_NUMERIC); $slides = '"'.implode('","',$filelist).'"'; $listcount = count($filelist); ?> -

: Name()) ?> ()

+

Name()).( (!empty($otherlinks)) ? ' ('.$otherlinks.') ' : '' ) ?>

DefaultVideo() ) { @@ -247,12 +232,12 @@ function exportEventImages($event, $exportDetail, $exportFrames, $myfilelist) { if ( $Monitor->VideoWriter() == '2' ) { # Passthrough $Rotation = $event->Orientation(); - if ( in_array($event->Orientation(),array('ROTATE_90','ROTATE_270')) ) + if ( in_array($event->Orientation(), array('ROTATE_90','ROTATE_270')) ) $Zoom = $event->Height()/$event->Width(); - } + } # end if passthrough ?>
-
+'; + return $html; +} // end function eventlist_html -function exportEventImagesMaster($eids) { +function exportEventImagesMaster($eids, $exportDetail, $exportFrames) { ob_start(); exportHeader(translate('Images').' Master'); ?> @@ -620,10 +614,10 @@ function exportEventImagesMaster($eids) { $eids)); - foreach ($events as $event) { + foreach ( $events as $event ) { //get monitor id and event id - $eventMonitorId[$eid] = $event->MonitorId(); - $eventPath[$eid] = $event->Relative_Path(); + $eventMonitorId[$event->Id()] = $event->MonitorId(); + $eventPath[$event->Id()] = $event->Relative_Path(); } $monitors = array_values(array_flip(array_flip($eventMonitorId))); //unique monitors and reindex the array @@ -631,11 +625,10 @@ function exportEventImagesMaster($eids) { //* if ( !empty($monitors) ) { - $tmp = dbFetchAll('SELECT Id,Name FROM Monitors WHERE Id IN ('.implode(',', $monitors).') '); + $tmp = dbFetchAll('SELECT Id, Name FROM Monitors WHERE Id IN ('.implode(',', $monitors).') '); foreach ( $tmp as $row ) { $monitorNames[$row['Id']] = $row['Name']; } } //*/ - //trigger_error(print_r($monitorNames,1)); ?>
    @@ -647,31 +640,32 @@ function exportEventImagesMaster($eids) { ?>
-
+
@@ -313,13 +313,13 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { - + '. makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', ''.validHtmlStr($Monitor->Source()).'', canEdit('Monitors') ).''; if ( $show_storage_areas ) { ?> - + translate('ReplayGapless'), ); -if ( isset( $_REQUEST['streamMode'] ) ) +if ( isset($_REQUEST['streamMode']) ) $streamMode = validHtmlStr($_REQUEST['streamMode']); else $streamMode = 'video'; @@ -108,7 +108,16 @@ if ( $Monitor->VideoWriter() == '2' ) { $Zoom = $Event->Height()/$Event->Width(); } -// These are here to figure out the next/prev event +// These are here to figure out the next/prev event, however if there is no filter, then default to one that specifies the Monitor +if ( !isset($_REQUEST['filter']) ) { + $_REQUEST['filter'] = array( + 'Query'=>array( + 'terms'=>array( + array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$Event->MonitorId()) + ) + ) + ); +} parseSort(); parseFilter($_REQUEST['filter']); $filterQuery = $_REQUEST['filter']['query']; @@ -132,16 +141,16 @@ if ( !$Event->Id() ) { ?>
Id() ?> - Id() . ' ' . $Monitor->Name() ?> + Id().' '.validHtmlStr($Monitor->Name()) ?> Cause()) ?> - StartTime() ) ) ?> + StartTime())) ?> Length().'s' ?> Frames() ?>/AlarmFrames() ?> TotScore() ?>/AvgScore() ?>/MaxScore() ?> DiskSpace(null)) . ' on ' . $Event->Storage()->Name(). - ( $Event->SecondaryStorageId() ? ', ' . $Event->SecondaryStorage()->Name() :'' ) + human_filesize($Event->DiskSpace(null)) . ' on ' . validHtmlStr($Event->Storage()->Name()). + ( $Event->SecondaryStorageId() ? ', '.validHtmlStr($Event->SecondaryStorage()->Name()) : '' ) ?>
@@ -263,7 +272,12 @@ if ( (ZM_WEB_STREAM_METHOD == 'mpeg') && ZM_MPEG_LIVE_FORMAT ) {

: Replay - : x + : +'-8x', -400=>'-4x', -200=>'-2x', -100=>'-1x', 0=>translate('Stop'), 100 => '1x', 200=>'2x', 400=>'4x', 800=>'8x' ); +echo htmlSelect('rate', $rates, intval($rate), array('id'=>'rateValue')); +?> + : 0s : 1x
diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index cfbe90383..034e908ef 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -258,7 +258,7 @@ while ( $event_row = dbFetchNext($results) ) { echo '', $Group->depth()); $html .= ''; + $html .= ''; if ( canEdit('Groups') ) { $html .= ''; } diff --git a/web/skins/classic/views/js/console.js.php b/web/skins/classic/views/js/console.js.php index f5e65332d..e6af2a8b6 100644 --- a/web/skins/classic/views/js/console.js.php +++ b/web/skins/classic/views/js/console.js.php @@ -1,20 +1,24 @@ var consoleRefreshTimeout = ; 0 ) { if ( ZM_DYN_DONATE_REMINDER_TIME < time() ) { $showDonatePopup = true; } } else { $nextReminder = time() + 30*24*60*60; - dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" ); + dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'"); } } -} +} // end if canEdit('System') ?> var showVersionPopup = ; var showDonatePopup = ; diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 4aeb673e5..8b51e0cec 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -1,4 +1,7 @@ var vid = null; +var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame. +var intervalRewind; +var revSpeed = .5; // Function called when video.js hits the end of the video function vjsReplay() { @@ -39,7 +42,7 @@ function vjsReplay() { streamNext(true); break; } -} +} // end function vjsReplay $j.ajaxSetup({timeout: AJAX_TIMEOUT}); //sets timeout for all getJSON. @@ -67,14 +70,14 @@ function renderAlarmCues(containerEl) { var spanTimeStart = 0; var spanTimeEnd = 0; var alarmed = 0; - var alarmHtml = ""; + var alarmHtml = ''; var pixSkew = 0; var skip = 0; var num_cueFrames = cueFrames.length; for ( var i = 0; i < num_cueFrames; i++ ) { skip = 0; frame = cueFrames[i]; - if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm. + if ( (frame.Type == 'Alarm') && (alarmed == 0) ) { //From nothing to alarm. End nothing and start alarm. alarmed = 1; if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan spanTimeEnd = frame.Delta * 100; @@ -88,24 +91,24 @@ function renderAlarmCues(containerEl) { } alarmHtml += ''; spanTimeStart = spanTimeEnd; - } else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing. + } else if ( (frame.Type !== 'Alarm') && (alarmed == 1) ) { //from alarm to nothing. End alarm and start nothing. futNone = 0; indexPlus = i+1; if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < num_cueFrames) { //alarm is too short and there is more event continue; } - while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan - if (indexPlus >= cueFrames.length) break; //check if end of event. + while ( futNone < minAlarm ) { //check ahead to see if there's enough for a nonespan + if ( indexPlus >= cueFrames.length ) break; //check if end of event. futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100); - if (cueFrames[indexPlus].Type == "Alarm") { + if ( cueFrames[indexPlus].Type == 'Alarm' ) { i = --indexPlus; skip = 1; break; } indexPlus++; } - if (skip == 1) continue; //javascript doesn't support continue 2; + if ( skip == 1 ) continue; //javascript doesn't support continue 2; spanTimeEnd = frame.Delta *100; spanTime = spanTimeEnd - spanTimeStart; alarmed = 0; @@ -118,7 +121,7 @@ function renderAlarmCues(containerEl) { } alarmHtml += ''; spanTimeStart = spanTimeEnd; - } else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm + } else if ( (frame.Type == 'Alarm') && (alarmed == 1) && (i + 1 >= cueFrames.length) ) { //event ends on an alarm spanTimeEnd = frame.Delta * 100; spanTime = spanTimeEnd - spanTimeStart; alarmed = 0; @@ -148,7 +151,7 @@ function changeScale() { } else { eventViewer = $j(vid ? '#videoobj' : '#evtStream'); } - if ( scale == '0' ) { + if ( scale == '0' || scale == 'auto' ) { var newSize = scaleToFit(eventData.Width, eventData.Height, eventViewer, bottomEl); newWidth = newSize.width; newHeight = newSize.height; @@ -188,6 +191,32 @@ function changeReplayMode() { refreshWindow(); } +function changeRate() { + var rate = parseInt($j('select[name="rate"]').val()); + if ( ! rate ) { + pauseClicked(); + } else if ( rate < 0 ) { + if ( vid ) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back. + revSpeed = rates[rates.indexOf(-1*rate)-1]/100; + clearInterval(intervalRewind); + intervalRewind = setInterval(function() { + if ( vid.currentTime() <= 0 ) { + clearInterval(intervalRewind); + vid.pause(); + } else { + vid.playbackRate(0); + vid.currentTime(vid.currentTime() - (revSpeed/2)); //Half of reverse speed because our interval is 500ms. + } + }, 500); //500ms is a compromise between smooth reverse and realistic performance + } // end if vid + } else { // Forward rate + if ( vid ) { + vid.playbackRate(rate/100); + } + } + Cookie.write('zmEventRate', rate, {duration: 10*365}); +} // end function changeRate + var streamParms = "view=request&request=stream&connkey="+connKey; if ( auth_hash ) { streamParms += '&auth='+auth_hash; @@ -233,7 +262,8 @@ function getCmdResponse( respObj, respText ) { if ( streamStatus.paused == true ) { streamPause( ); } else { - $j('#rateValue').html(streamStatus.rate); + console.log('streamStatus.rate: ' + streamStatus.rate); + $j('select[name="rate"]').val(streamStatus.rate*100); Cookie.write('zmEventRate', streamStatus.rate*100, {duration: 10*365}); streamPlay( ); } @@ -268,23 +298,18 @@ var streamReq = new Request.JSON( { function pauseClicked() { if ( vid ) { + if ( intervalRewind ) { + stopFastRev(); + } vid.pause(); } else { streamReq.send(streamParms+"&command="+CMD_PAUSE); - streamPause(); - } -} - -function vjsPause() { - if ( intervalRewind ) { - stopFastRev(); } streamPause(); } function streamPause( ) { $j('#modeValue').html('Paused'); - $j('#rateValue').html('0'); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('playBtn'), 'inactive' ); setButtonState( $('fastFwdBtn'), 'unavail' ); @@ -294,6 +319,10 @@ function streamPause( ) { } function playClicked( ) { + var rate_select = $j('select[name="rate"]'); + if ( ! rate_select.val() ) { + $j('select[name="rate"]').val(100); + } if ( vid ) { if ( vid.paused() ) { vid.play(); @@ -302,21 +331,20 @@ function playClicked( ) { } } else { streamReq.send(streamParms+"&command="+CMD_PLAY); - streamPlay(); } + streamPlay(); } function vjsPlay() { //catches if we change mode programatically if ( intervalRewind ) { stopFastRev(); } - $j('#rateValue').html(vid.playbackRate()); + $j('select[name="rate"]').val(vid.playbackRate()*100); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); streamPlay(); } function streamPlay( ) { - $j('#modeValue').html('Replay'); setButtonState( $('pauseBtn'), 'inactive' ); setButtonState( $('playBtn'), 'active' ); setButtonState( $('fastFwdBtn'), 'inactive' ); @@ -338,16 +366,13 @@ function streamFastFwd( action ) { if ( rates.indexOf(vid.playbackRate()*100)-1 == -1 ) { setButtonState($('fastFwdBtn'), 'unavail'); } - $j('#rateValue').html(vid.playbackRate()); + $j('select[name="rate"]').val(vid.playbackRate()*100); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); } else { streamReq.send(streamParms+"&command="+CMD_FASTFWD); } } -var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame. -var intervalRewind; -var revSpeed = .5; function streamSlowFwd( action ) { if ( vid ) { @@ -368,6 +393,7 @@ function streamSlowRev( action ) { function stopFastRev() { clearInterval(intervalRewind); vid.playbackRate(1); + $j('select[name="rate"]').val(vid.playbackRate()*100); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); revSpeed = .5; } @@ -385,7 +411,7 @@ function streamFastRev( action ) { setButtonState( $('fastRevBtn'), 'unavail' ); } clearInterval(intervalRewind); - $j('#rateValue').html(-revSpeed); + $j('select[name="rate"]').val(-revSpeed*100); Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365}); intervalRewind = setInterval(function() { if (vid.currentTime() <= 0) { @@ -576,7 +602,7 @@ function getEventResponse(respObj, respText) { CurEventDefVideoPath = null; $j('#modeValue').html('Replay'); $j('#zoomValue').html('1'); - $j('#rateValue').html('1'); + $j('#rate').val('100'); vjsPanZoom('zoomOut'); } else { drawProgressBar(); @@ -1047,7 +1073,7 @@ function initPage() { $j('.vjs-progress-control').append('
');//add a place for videojs only on first load vid.on('ended', vjsReplay); vid.on('play', vjsPlay); - vid.on('pause', vjsPause); + vid.on('pause', pauseClicked); vid.on('click', function(event) { handleClick(event); }); @@ -1055,7 +1081,8 @@ function initPage() { $j('#progressValue').html(secsToTime(Math.floor(vid.currentTime()))); }); - if ( rate > 1 ) { + // rate is in % so 100 would be 1x + if ( rate > 0 ) { // rate should be 100 = 1x, etc. vid.playbackRate(rate/100); } @@ -1079,7 +1106,10 @@ function initPage() { } nearEventsQuery(eventData.Id); initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues - if ( scale == '0' ) changeScale(); + if ( scale == '0' || scale == 'auto' ) changeScale(); + document.querySelectorAll('select[name="rate"]').forEach(function(el) { + el.onchange = window['changeRate']; + }); } // Kick everything off diff --git a/web/skins/classic/views/js/event.js.php b/web/skins/classic/views/js/event.js.php index 3dae5abfe..7ba03b621 100644 --- a/web/skins/classic/views/js/event.js.php +++ b/web/skins/classic/views/js/event.js.php @@ -34,7 +34,7 @@ var eventData = { StartTime: 'StartTime() ?>', EndTime: 'EndTime() ?>', Frames: 'Frames() ?>', - MonitorName: 'Name() ?>' + MonitorName: 'Name()) ?>' }; var monitorUrl = 'Storage()->Server()->UrlToIndex(); ?>'; @@ -46,7 +46,7 @@ var rate = ''; // really only used when setting up initial pl var scale = ""; var LabelFormat = "LabelFormat())?>"; -var canEditEvents = ; +var canEditEvents = ; var streamTimeout = ; var canStreamNative = ; @@ -55,8 +55,8 @@ var streamMode = ''; // // Strings // -var deleteString = ""; -var causeString = ""; +var deleteString = ""; +var causeString = ""; var WEB_LIST_THUMB_WIDTH = ''; var WEB_LIST_THUMB_HEIGHT = ''; var popup = ''; diff --git a/web/skins/classic/views/js/export.js b/web/skins/classic/views/js/export.js index d553f9385..7dd7f6c6d 100644 --- a/web/skins/classic/views/js/export.js +++ b/web/skins/classic/views/js/export.js @@ -65,7 +65,7 @@ function exportResponse(respObj, respText) { //window.location.replace( thisUrl+'?view='+currentView+'&'+eids.join('&')+'&exportFile='+respObj.exportFile+'&generated='+((respObj.result=='Ok')?1:0) ); } -function exportEvent( ) { +function exportEvents( ) { var parms = 'view=event&request=event&action=export'; parms += '&'+$('contentForm').toQueryString(); var query = new Request.JSON( { @@ -87,7 +87,7 @@ function initPage() { if ( exportReady ) { startDownload.pass(exportFile).delay(1500); } - document.getElementById('exportButton').addEventListener('click', exportEvent); + document.getElementById('exportButton').addEventListener('click', exportEvents); } window.addEventListener('DOMContentLoaded', initPage); diff --git a/web/skins/classic/views/js/filter.js b/web/skins/classic/views/js/filter.js index 19457d7af..bcf67c00b 100644 --- a/web/skins/classic/views/js/filter.js +++ b/web/skins/classic/views/js/filter.js @@ -151,6 +151,11 @@ function deleteFilter( element ) { form.submit(); } } +var escape = document.createElement('textarea'); +function escapeHTML(html) { + escape.textContent = html; + return escape.innerHTML; +} function parseRows(rows) { for ( var rowNum = 0; rowNum < rows.length; rowNum++ ) { //Each row is a term @@ -245,7 +250,7 @@ function parseRows(rows) { } else if ( attr == 'MonitorName' ) { //Monitor names var monitorSelect = $j('').attr('name', queryPrefix + rowNum + '][val]').attr('id', queryPrefix + rowNum + '][val]'); for ( var monitor_id in monitors ) { - monitorSelect.append(''); + monitorSelect.append(''); } var monitorVal = inputTds.eq(4).children().val(); inputTds.eq(4).html(monitorSelect).children().val(monitorVal); diff --git a/web/skins/classic/views/js/groups.js b/web/skins/classic/views/js/groups.js index 31c9402a2..8ff1b61ff 100644 --- a/web/skins/classic/views/js/groups.js +++ b/web/skins/classic/views/js/groups.js @@ -8,8 +8,13 @@ function setGroup( element ) { form.submit(); } -function editGroup( gid ) { - createPopup( '?view=group&gid='+gid, 'zmGroup', 'group' ); +function editGroup( element ) { + var gid = element.getAttribute('data-group-id'); + if ( !gid ) { + console.log('No group id found in editGroup'); + } else { + createPopup('?view=group&gid='+gid, 'zmGroup'+gid, 'group'); + } } function deleteGroup( element ) { diff --git a/web/skins/classic/views/js/log.js b/web/skins/classic/views/js/log.js index 811fa5f28..f0aee7c00 100644 --- a/web/skins/classic/views/js/log.js +++ b/web/skins/classic/views/js/log.js @@ -175,7 +175,7 @@ function clearLog() { var clearReq = new Request.JSON({url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: clearResponse}); var tbody = $(logTable).getElement('tbody'); var rows = tbody.getElements('tr'); - if ( rows ) { + if ( rows && rows.length ) { var minTime = rows[0].getElement('td').get('text'); clearParms += "&minTime="+encodeURIComponent(minTime); var maxTime = rows[rows.length-1].getElement('td').get('text'); diff --git a/web/skins/classic/views/js/monitor.js.php b/web/skins/classic/views/js/monitor.js.php index 04fa06dcb..6a6db3ce9 100644 --- a/web/skins/classic/views/js/monitor.js.php +++ b/web/skins/classic/views/js/monitor.js.php @@ -138,11 +138,7 @@ function validateForm( form ) { if ( (form.elements['newMonitor[SaveJPEGs]'].value == '0') && (form.elements['newMonitor[VideoWriter]'].value == '0') ) { warnings[warnings.length] = ""; } -console.log(form.elements['newMonitor[SaveJPEGs]'].value); -console.log(form.elements['newMonitor[VideoWriter]'].value); - } -console.log(warnings); if ( warnings.length ) { if ( !confirm(warnings.join("\n")) ) { return false; diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index d294ed813..0a281dd01 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -13,6 +13,8 @@ function Monitor(monitorData) { this.streamCmdParms = 'view=request&request=stream&connkey='+this.connKey; if ( auth_hash ) { this.streamCmdParms += '&auth='+auth_hash; + } else if ( auth_relay ) { + this.streamCmdParms += '&'+auth_relay; } this.streamCmdTimer = null; this.type = monitorData.type; @@ -210,7 +212,6 @@ function Monitor(monitorData) { * @param {*} element - the event data passed by onchange callback */ function selectLayout(element) { - console.log(element); layout = $j(element).val(); if ( layout_id = parseInt(layout) ) { @@ -221,8 +222,8 @@ function selectLayout(element) { // Need to clear the current positioning, and apply the new monitor_frame = $j('#monitorFrame'+monitor.id); - if ( ! monitor_frame ) { - console.log("Error finding frame for " + monitor.id); + if ( !monitor_frame ) { + console.log('Error finding frame for ' + monitor.id); continue; } @@ -262,6 +263,10 @@ function selectLayout(element) { if ( streamImg.nodeName == 'IMG' ) { var src = streamImg.src; src = src.replace(/width=[\.\d]+/i, 'width=0' ); + if ( $j('#height').val() == 'auto' ) { + src = src.replace(/height=[\.\d]+/i, 'height=0' ); + streamImg.style.height = 'auto'; + } if ( src != streamImg.src ) { streamImg.src = ''; streamImg.src = src; diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index 2d482f771..fe9ccd3cd 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -971,6 +971,7 @@ function initPage() { imagedone(this, this.monId, false); }; loadImage2Monitor(monId, monitorImageURL[monId]); + monitorCanvasObj[monId].addEventListener('click', clickMonitor, false); } } // end foreach monitor @@ -986,15 +987,6 @@ function initPage() { ctx = canvas.getContext('2d'); drawGraph(); } - for ( i=0, len=monitorPtr.length; i < len; i += 1 ) { - var monitor_id = monitorPtr[i]; - monitor_canvas = $('Monitor'+monitor_id); - if ( ! monitor_canvas ) { - console.log("No canvas found for monitor " + monitor_id); - continue; - } - monitor_canvas.addEventListener('click', clickMonitor, false); - } setSpeed(speedIndex); //setFit(fitMode); // will redraw //setLive(liveMode); // will redraw diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index 4076d1dbe..4e3db5665 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -3,7 +3,7 @@ var server_utc_offset = getOffset($now); -echo $offset . '; // ' . floor($offset / 3600) . ' hours '; +echo $offset.'; // '.floor($offset / 3600).' hours '; ?> var currentScale=; @@ -182,18 +182,18 @@ foreach ( $monitors as $m ) { echo " monitorLoading[" . $m->Id() . "]=false;\n"; echo " monitorImageURL[" . $m->Id() . "]='".$m->getStreamSrc( array('mode'=>'single','scale'=>$defaultScale*100), '&' )."';\n"; echo " monitorLoadingStageURL[" . $m->Id() . "] = '';\n"; - echo " monitorColour[" . $m->Id() . "]=\"" . $m->WebColour() . "\";\n"; - echo " monitorWidth[" . $m->Id() . "]=" . $m->ViewWidth() . ";\n"; - echo " monitorHeight[" . $m->Id() . "]=" . $m->ViewHeight() . ";\n"; + echo " monitorColour[" . $m->Id() . "]=\"" . validHtmlStr($m->WebColour()) . "\";\n"; + echo " monitorWidth[" . $m->Id() . "]=" . validHtmlStr($m->ViewWidth()) . ";\n"; + echo " monitorHeight[" . $m->Id() . "]=" . validHtmlStr($m->ViewHeight()) . ";\n"; echo " monitorIndex[" . $m->Id() . "]=" . $numMonitors . ";\n"; echo " monitorServerId[" . $m->Id() . "]='" .($m->ServerId() ? $m->ServerId() : '0'). "';\n"; - echo " monitorName[" . $m->Id() . "]=\"" . $m->Name() . "\";\n"; + echo " monitorName[" . $m->Id() . "]=\"" . validHtmlStr($m->Name()) . "\";\n"; echo " monitorLoadStartTimems[" . $m->Id() . "]=0;\n"; echo " monitorLoadEndTimems[" . $m->Id() . "]=0;\n"; echo " monitorNormalizeScale[" . $m->Id() . "]=" . sqrt($avgArea / ($m->Width() * $m->Height() )) . ";\n"; $zoomScale=1.0; - if(isset($_REQUEST[ 'z' . $m->Id() ]) ) - $zoomScale = floatval( validHtmlStr($_REQUEST[ 'z' . $m->Id() ]) ); + if ( isset($_REQUEST['z'.$m->Id()]) ) + $zoomScale = floatval(validHtmlStr($_REQUEST['z'.$m->Id()])); echo " monitorZoomScale[" . $m->Id() . "]=" . $zoomScale . ";\n"; echo " monitorPtr[" . $numMonitors . "]=" . $m->Id() . ";\n"; $numMonitors += 1; @@ -205,14 +205,14 @@ var maxTimeSecs=parseInt($maxTimeSecs); var minTime='$minTime'; var maxTime='$maxTime'; "; -echo "var rangeTimeSecs=" . ( $maxTimeSecs - $minTimeSecs + 1) . ";\n"; -if(isset($defaultCurrentTime)) - echo "var currentTimeSecs=parseInt(" . strtotime($defaultCurrentTime) . ");\n"; +echo 'var rangeTimeSecs='.($maxTimeSecs - $minTimeSecs + 1).";\n"; +if ( isset($defaultCurrentTime) ) + echo 'var currentTimeSecs=parseInt('.strtotime($defaultCurrentTime).");\n"; else - echo "var currentTimeSecs=parseInt(" . ($minTimeSecs + $maxTimeSecs)/2 . ");\n"; + echo 'var currentTimeSecs=parseInt('.(($minTimeSecs + $maxTimeSecs)/2).");\n"; echo 'var speeds=['; -for ($i=0; $i0)?', ':'') . $speeds[$i]; echo "];\n"; ?> diff --git a/web/skins/classic/views/js/postlogin.js.php b/web/skins/classic/views/js/postlogin.js.php index 1edede98b..a062b4c52 100644 --- a/web/skins/classic/views/js/postlogin.js.php +++ b/web/skins/classic/views/js/postlogin.js.php @@ -21,12 +21,13 @@ } ?>'; - if ( querySuffix == '?view=login' ) { + if ( querySuffix == '?view=login' || querySuffix == '' ) { // If we didn't redirect elsewhere, then don't show login page, go to console querySuffix = '?view=console'; } var newUrl = querySuffix; - console.log("Redirecting to" + newUrl + ' ' + thisUrl); +console.log("Current location: " + window.location); +console.log("Redirecting to (" + newUrl + ') from :' + thisUrl); window.location.replace(newUrl); } ).delay( 500 ); diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index 303e91d85..8a38afea4 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -48,7 +48,7 @@ function changeScale() { streamImg.src = streamImg.src.replace(/scale=\d+/i, 'scale='+(scale== 'auto' ? autoScale : scale)); } else { - console.error('No element found for liveStream.'); + console.error('No element found for liveStream'+monitorId); } } @@ -546,42 +546,62 @@ function getEventCmdResponse( respObj, respText ) { } ); for ( var i = 0; i < dbEvents.length; i++ ) { - var event = dbEvents[i]; - var row = $('event'+event.Id); + var zm_event = dbEvents[i]; + var row = $('event'+zm_event.Id); var newEvent = (row == null ? true : false); if ( newEvent ) { - row = new Element( 'tr', {'id': 'event'+event.Id} ); - new Element( 'td', {'class': 'colId'} ).inject( row ); - new Element( 'td', {'class': 'colName'} ).inject( row ); - new Element( 'td', {'class': 'colTime'} ).inject( row ); - new Element( 'td', {'class': 'colSecs'} ).inject( row ); - new Element( 'td', {'class': 'colFrames'} ).inject( row ); - new Element( 'td', {'class': 'colScore'} ).inject( row ); - new Element( 'td', {'class': 'colDelete'} ).inject( row ); + row = new Element('tr', {'id': 'event'+zm_event.Id}); + new Element('td', {'class': 'colId'}).inject(row); + new Element('td', {'class': 'colName'}).inject(row); + new Element('td', {'class': 'colTime'}).inject(row); + new Element('td', {'class': 'colSecs'}).inject(row); + new Element('td', {'class': 'colFrames'}).inject(row); + new Element('td', {'class': 'colScore'}).inject(row); + new Element('td', {'class': 'colDelete'}).inject(row); - var link = new Element( 'a', {'href': '#', 'events': {'click': createEventPopup.pass( [event.Id, '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', event.Width, event.Height] )}}); - link.set( 'text', event.Id ); - link.inject( row.getElement( 'td.colId' ) ); + var link = new Element('a', { + 'href': '#', + 'events': { + 'click': createEventPopup.pass( [ + zm_event.Id, + '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', + zm_event.Width, + zm_event.Height + ] ) + } + }); + link.set('text', zm_event.Id); + link.inject(row.getElement('td.colId')); - link = new Element( 'a', {'href': '#', 'events': {'click': createEventPopup.pass( [event.Id, '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', event.Width, event.Height] )}}); - link.set( 'text', event.Name ); - link.inject( row.getElement( 'td.colName' ) ); + link = new Element('a', { + 'href': '#', + 'events': { + 'click': createEventPopup.pass( [ + zm_event.Id, + '&filter[Query][terms][0][attr]=MonitorId&filter[Query][terms][0][op]=%3d&filter[Query][terms][0][val]='+monitorId+'&page=1&popup=1', + zm_event.Width, + zm_event.Height + ] ) + } + }); + link.set('text', zm_event.Name); + link.inject(row.getElement('td.colName')); - row.getElement( 'td.colTime' ).set( 'text', event.StartTime ); - row.getElement( 'td.colSecs' ).set( 'text', event.Length ); + row.getElement('td.colTime').set('text', zm_event.StartTime); + row.getElement('td.colSecs').set('text', zm_event.Length); - link = new Element( 'a', {'href': '#', 'events': {'click': createFramesPopup.pass( [event.Id, event.Width, event.Height] )}}); - link.set( 'text', event.Frames+'/'+event.AlarmFrames ); - link.inject( row.getElement( 'td.colFrames' ) ); + link = new Element('a', {'href': '#', 'events': {'click': createFramesPopup.pass( [zm_event.Id, zm_event.Width, zm_event.Height] )}}); + link.set('text', zm_event.Frames+'/'+zm_event.AlarmFrames); + link.inject(row.getElement('td.colFrames')); - link = new Element( 'a', {'href': '#', 'events': {'click': createFramePopup.pass( [event.Id, '0', event.Width, event.Height] )}}); - link.set( 'text', event.AvgScore+'/'+event.MaxScore ); - link.inject( row.getElement( 'td.colScore' ) ); + link = new Element('a', {'href': '#', 'events': {'click': createFramePopup.pass( [zm_event.Id, '0', zm_event.Width, zm_event.Height] )}}); + link.set('text', zm_event.AvgScore+'/'+zm_event.MaxScore); + link.inject(row.getElement('td.colScore')); - link = new Element( 'button', { + link = new Element('button', { 'type': 'button', 'title': deleteString, - 'data-event-id': event.Id, + 'data-event-id': zm_event.Id, 'events': { 'click': function(e) { var event_id = e.target.getAttribute('data-event-id'); @@ -608,14 +628,14 @@ function getEventCmdResponse( respObj, respText ) { } } } else { - row.getElement('td.colName a').set('text', event.Name); - row.getElement('td.colSecs').set('text', event.Length); - row.getElement('td.colFrames a').set('text', event.Frames+'/'+event.AlarmFrames); - row.getElement('td.colScore a').set('text', event.AvgScore+'/'+event.MaxScore); + row.getElement('td.colName a').set('text', zm_event.Name); + row.getElement('td.colSecs').set('text', zm_event.Length); + row.getElement('td.colFrames a').set('text', zm_event.Frames+'/'+zm_event.AlarmFrames); + row.getElement('td.colScore a').set('text', zm_event.AvgScore+'/'+zm_event.MaxScore); row.removeClass('recent'); } row.addClass('updated'); - } + } // end foreach event var rows = $(eventListBody).getElements('tr'); for ( var i = 0; i < rows.length; i++ ) { @@ -631,7 +651,7 @@ function getEventCmdResponse( respObj, respText ) { } } else { checkStreamForErrors('getEventCmdResponse', respObj); - } + } // end if objresult == ok var eventCmdTimeout = eventsRefreshTimeout; if ( alarmState == STATE_ALARM || alarmState == STATE_ALERT ) { @@ -672,8 +692,13 @@ function getControlResponse(respObj, respText) { } } -function controlCmd( control, event, xtell, ytell ) { - var locParms = ""; +function controlCmd(event) { + button = event.target; + control = button.getAttribute('value'); + xtell = button.getAttribute('xtell'); + ytell = button.getAttribute('ytell'); + + var locParms = ''; if ( event && (xtell || ytell) ) { console.log(event); var target = event.target; @@ -683,22 +708,22 @@ function controlCmd( control, event, xtell, ytell ) { var y = event.pageY - coords.top; if ( xtell ) { - var xge = parseInt( (x*100)/coords.width ); + var xge = parseInt((x*100)/coords.width); if ( xtell == -1 ) { xge = 100 - xge; } else if ( xtell == 2 ) { xge = 2*(50 - xge); } - locParms += "&xge="+xge; + locParms += '&xge='+xge; } if ( ytell ) { - var yge = parseInt( (y*100)/coords.height ); + var yge = parseInt((y*100)/coords.height); if ( ytell == -1 ) { yge = 100 - yge; } else if ( ytell == 2 ) { yge = 2*(50 - yge); } - locParms += "&yge="+yge; + locParms += '&yge='+yge; } } controlReq.send(controlParms+"&control="+control+locParms); @@ -742,10 +767,14 @@ function handleClick( event ) { function appletRefresh() { if ( streamStatus && (!streamStatus.paused && !streamStatus.delayed) ) { - var streamImg = $('liveStream'); - var parent = streamImg.getParent(); - streamImg.dispose(); - streamImg.inject( parent ); + var streamImg = $('liveStream'+monitorId); + if ( streamImg ) { + var parent = streamImg.getParent(); + streamImg.dispose(); + streamImg.inject( parent ); + } else { + console.error("Nothing found for liveStream"+monitorId); + } if ( appletRefreshTime ) { appletRefresh.delay( appletRefreshTime*1000 ); } @@ -798,20 +827,24 @@ function initPage() { eventCmdTimer = eventCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout ); watchdogCheck.pass('event').periodical(eventsRefreshTimeout*2); - if ( canStreamNative || streamMode == 'single' ) { + if ( canStreamNative || (streamMode == 'single') ) { var streamImg = $('imageFeed').getElement('img'); if ( !streamImg ) { streamImg = $('imageFeed').getElement('object'); } - if ( streamMode == 'single' ) { - streamImg.addEvent('click', fetchImage.pass(streamImg)); - fetchImage.pass(streamImg).periodical(imageRefreshTimeout); + if ( !streamImg ) { + console.error('No streamImg found for imageFeed'); } else { - streamImg.addEvent('click', function(event) { - handleClick(event); - }); - } - } + if ( streamMode == 'single' ) { + streamImg.addEvent('click', fetchImage.pass(streamImg)); + fetchImage.pass(streamImg).periodical(imageRefreshTimeout); + } else { + streamImg.addEvent('click', function(event) { + handleClick(event); + }); + } + } // end if have streamImg + } // streamMode native or single if ( refreshApplet && appletRefreshTime ) { appletRefresh.delay(appletRefreshTime*1000); diff --git a/web/skins/classic/views/login.php b/web/skins/classic/views/login.php index 76ebeafd8..296a54887 100644 --- a/web/skins/classic/views/login.php +++ b/web/skins/classic/views/login.php @@ -22,18 +22,17 @@ xhtmlHeaders(__FILE__, translate('Login')); - - '; - } ?> - + + '; +} ?> - diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index b0bf9a258..b9aac5890 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -123,7 +123,8 @@ $sourceTypes = array( 'Libvlc' => translate('Libvlc'), 'cURL' => 'cURL (HTTP(S) only)', 'WebSite'=> 'Web Site', - 'NVSocket' => translate('NVSocket') + 'NVSocket' => translate('NVSocket'), + 'VNC' => translate('VNC'), ); if ( !ZM_HAS_V4L ) unset($sourceTypes['Local']); @@ -367,8 +368,8 @@ $fastblendopts_alarm = array( ); $label_size = array( - 'Default' => 1, - 'Large' => 2 + 1 => translate('Default'), + 2 => translate('Large'), ); $codecs = array( @@ -642,7 +643,7 @@ switch ( $tab ) { - + Type() != 'WebSite' ) { @@ -727,22 +728,19 @@ switch ( $tab ) { $breakCount = (int)(ceil(count($optTriggers))); $breakCount = min(3, $breakCount); $optCount = 0; - foreach( $optTriggers as $optTrigger ) { - if ( !ZM_OPT_X10 && $optTrigger == 'X10' ) + foreach ( $optTriggers as $optTrigger ) { + if ( $optTrigger == 'X10' and !ZM_OPT_X10 ) continue; if ( $optCount && ($optCount%$breakCount == 0) ) echo '
'; + echo 'Triggers()) && in_array($optTrigger, $monitor->Triggers()) ) ? ' checked="checked"' : ''). '/> '. $optTrigger; + $optCount ++; + } # end foreach trigger option + if ( !$optCount ) { + echo ''.translate('NoneAvailable').''; + } ?> - Triggers() ) && in_array( $optTrigger, $monitor->Triggers() ) ) { ?> checked="checked"/>  - - - Type() == 'NVSocket' ) { include('_monitor_source_nvsocket.php'); + } else if ( $monitor->Type() == 'VNC' ) { +?> + + + + + + + + + + + + + + + + +Type() == 'Remote' ) { ?> + + + -Method() ); } else { -?> - -Method() ); } ?> - + + + - + Type() == 'File' ) { ?> @@ -809,23 +829,44 @@ include('_monitor_source_nvsocket.php'); Type() == 'cURL' ) { ?> - - - + + + Type() == 'WebSite' ) { ?> - - - - + + + + + + + + + + + + + + + + Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { + } else if ( $monitor->Type() == 'Ffmpeg' || $monitor->Type() == 'Libvlc' ) { ?> - - + + + + + + + + - + Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { + if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { ?> - + + + + Type() == 'Local' ) { + } + if ( $monitor->Type() == 'Local' ) { ?> - - + + + +Type() != 'WebSite' ) { ?> - + + + + - Type() == 'Remote' ) { ?> - Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>> + Protocol()!= 'rtsp' ) { echo ' style="display:none;"'; } ?>> + + + Type() == 'Remote' break; } case 'storage' : @@ -916,7 +966,7 @@ if ( $monitor->Type() == 'Local' ) { @@ -994,7 +1044,10 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor ?> - + + + + - - - + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - + + - translate('None'), - '0' => translate('Home'), - '1' => translate('Preset').' 1', - ); -?> - + diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index d2cd18323..e04ce8f68 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -66,17 +66,19 @@ foreach ( $layouts as $l ) { } } foreach ( $layouts as $l ) { - if ( $l->Name() != "Freeform" ) + if ( $l->Name() != 'Freeform' ) $layoutsById[$l->Id()] = $l; } -session_start(); +zm_session_start(); $layout_id = ''; if ( isset($_COOKIE['zmMontageLayout']) ) { $layout_id = $_SESSION['zmMontageLayout'] = $_COOKIE['zmMontageLayout']; -#} elseif ( isset($_SESSION['zmMontageLayout']) ) { - #$layout_id = $_SESSION['zmMontageLayout']; + ZM\Logger::Debug("Using layout $layout_id"); +} elseif ( isset($_SESSION['zmMontageLayout']) ) { + $layout_id = $_SESSION['zmMontageLayout']; + ZM\Logger::Debug("Using layout $layout_id from session"); } $options = array(); @@ -85,6 +87,8 @@ $Positions = ''; if ( $layout_id and is_numeric($layout_id) and isset($layoutsById[$layout_id]) ) { $Layout = $layoutsById[$layout_id]; $Positions = json_decode($Layout->Positions(), true); +} else { + ZM\Logger::Debug("Layout not found"); } if ( $Layout and ( $Layout->Name() != 'Freeform' ) ) { // Use layout instead of other options @@ -169,19 +173,19 @@ if ( $showZones ) { - + 'changeSize')); ?> - + 'changeSize')); ?> - + 'changeScale')); ?> - 'selectLayout(this);')); ?> + 'selectLayout')); ?> diff --git a/web/skins/classic/views/montagereview.php b/web/skins/classic/views/montagereview.php index 0d3571523..615a04c0c 100644 --- a/web/skins/classic/views/montagereview.php +++ b/web/skins/classic/views/montagereview.php @@ -320,7 +320,7 @@ getBodyTopHTML(); Id().' ' .$m->Name().'" width="' . $m->Width() * $defaultScale . '" height="' . $m->Height() * $defaultScale . '" id="Monitor' . $m->Id() . '" style="border:1px solid ' . $m->WebColour() . '" monitor_id="'.$m->Id().'">No Canvas Support!! + echo 'No Canvas Support!! '; } ?> diff --git a/web/skins/classic/views/report_event_audit.php b/web/skins/classic/views/report_event_audit.php index f3f508623..6cafaf67e 100644 --- a/web/skins/classic/views/report_event_audit.php +++ b/web/skins/classic/views/report_event_audit.php @@ -25,12 +25,12 @@ $filterbar = ob_get_contents(); ob_end_clean(); noCacheHeaders(); -xhtmlHeaders( __FILE__, translate('Console') ); +xhtmlHeaders( __FILE__, translate('Console')); if ( isset($_REQUEST['minTime']) ) { $minTime = validHtmlStr($_REQUEST['minTime']); } else { - $minTime = strftime('%FT%T',time() - (2*3600) ); + $minTime = strftime('%FT%T', time() - (2*3600)); } if ( isset($_REQUEST['maxTime']) ) { $maxTime = validHtmlStr($_REQUEST['maxTime']); @@ -47,13 +47,13 @@ $filter = array( ), ); if ( count($selected_monitor_ids) ) { - $filter['Query']['terms'][] = (array('attr'=>'MonitorId', 'op'=>'IN', 'val'=>implode(',',$selected_monitor_ids), 'cnj'=>'and')); + $filter['Query']['terms'][] = (array('attr'=>'MonitorId', 'op'=>'IN', 'val'=>implode(',', $selected_monitor_ids), 'cnj'=>'and')); } else if ( ( $group_id != 0 || isset($_SESSION['ServerId']) || isset($_SESSION['StorageId']) || isset($_SESSION['Status']) ) ) { # this should be redundant - for ($i=0; $i < count($displayMonitors); $i++) { - if ($i == '0') { + for ( $i=0; $i < count($displayMonitors); $i++ ) { + if ( $i == 0 ) { $filter['Query']['terms'][] = array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'and', 'obr'=>'1'); - } else if ($i == (count($displayMonitors)-1)) { + } else if ( $i == count($displayMonitors)-1 ) { $filter['Query']['terms'][] = array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'or', 'cbr'=>'1'); } else { $filter['Query']['terms'][] = array('attr'=>'MonitorId', 'op'=>'=', 'val'=>$displayMonitors[$i]['Id'], 'cnj'=>'or'); @@ -70,11 +70,11 @@ $eventsSql = 'SELECT *, FROM Events AS E WHERE 1 > 0 '; -if ( ! empty($user['MonitorIds']) ) { +if ( !empty($user['MonitorIds']) ) { $eventsSql .= ' AND MonitorId IN ('.$user['MonitorIds'].')'; } if ( count($selected_monitor_ids) ) { - $eventsSql .= ' AND MonitorId IN (' . implode(',',$selected_monitor_ids).')'; + $eventsSql .= ' AND MonitorId IN ('.implode(',', $selected_monitor_ids).')'; } if ( isset($minTime) && isset($maxTime) ) { $eventsSql .= " AND EndTime > '" . $minTime . "' AND StartTime < '" . $maxTime . "'"; @@ -87,10 +87,10 @@ if ( !$result ) { return; } $EventsByMonitor = array(); -while( $event = $result->fetch(PDO::FETCH_ASSOC) ) { +while ( $event = $result->fetch(PDO::FETCH_ASSOC) ) { $Event = new ZM\Event($event); if ( ! isset($EventsByMonitor[$event['MonitorId']]) ) - $EventsByMonitor[$event['MonitorId']] = array( 'Events'=>array(), 'MinGap'=>0, 'MaxGap'=>0, 'FileMissing'=>array(), 'ZeroSize'=>array() ); + $EventsByMonitor[$event['MonitorId']] = array('Events'=>array(), 'MinGap'=>0, 'MaxGap'=>0, 'FileMissing'=>array(), 'ZeroSize'=>array()); if ( count($EventsByMonitor[$event['MonitorId']]['Events']) ) { $last_event = end($EventsByMonitor[$event['MonitorId']]['Events']); @@ -103,7 +103,7 @@ while( $event = $result->fetch(PDO::FETCH_ASSOC) ) { $EventsByMonitor[$event['MonitorId']]['MaxGap'] = $gap; } # end if has previous events - if ( ! $Event->file_exists() ) { + if ( !$Event->file_exists() ) { $EventsByMonitor[$event['MonitorId']]['FileMissing'][] = $Event; } else if ( ! $Event->file_size() ) { $EventsByMonitor[$event['MonitorId']]['ZeroSize'][] = $Event; @@ -122,8 +122,8 @@ while( $event = $result->fetch(PDO::FETCH_ASSOC) ) {
- to - + to +
@@ -145,10 +145,10 @@ while( $event = $result->fetch(PDO::FETCH_ASSOC) ) { array( 'terms' => array( - array('attr'=>'Id','op'=>'IN', 'val'=>implode(',',array_map(function($Event){return $Event->Id();},$FileMissing))) + array('attr'=>'Id', 'op'=>'IN', 'val'=>implode(',', array_map(function($Event){return $Event->Id();}, $FileMissing))) ) ) ); @@ -188,7 +188,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { $ZeroSize_filter = array( 'Query' => array( 'terms' => array( - array('attr'=>'Id','op'=>'IN', 'val'=>implode(',',array_map(function($Event){return $Event->Id();},$ZeroSize))) + array('attr'=>'Id', 'op'=>'IN', 'val'=>implode(',', array_map(function($Event){return $Event->Id();}, $ZeroSize))) ) ) ); @@ -198,27 +198,28 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { - + - - + + $_REQUEST['id'])) ) ) { $view = 'error'; return; - $newStorage['ServerId'] = ''; } } else { - $newStorage = array(); - $newStorage['Name'] = translate('NewStorage'); - $newStorage['Path'] = ''; - $newStorage['Type'] = 'local'; - $newStorage['Url'] = ''; - $newStorage['Scheme'] = 'Medium'; - $newStorage['StorageId'] = ''; - $newStorage['ServerId'] = ''; - $newStorage['DoDelete'] = 1; + $newStorage = new ZM\Storage(); + $newStorage->Name(translate('NewStorage')); } $type_options = array( 'local' => translate('Local'), 's3fs' => translate('s3fs') ); @@ -55,12 +50,12 @@ foreach ( $servers as $S ) { } $focusWindow = true; -xhtmlHeaders(__FILE__, translate('Storage').' - '.$newStorage['Name']); +xhtmlHeaders(__FILE__, translate('Storage').' - '.$newStorage->Name()); ?>
@@ -71,33 +66,40 @@ xhtmlHeaders(__FILE__, translate('Storage').' - '.$newStorage['Name']);
- + - + - + - + - + - + + + + + diff --git a/web/skins/classic/views/timeline.php b/web/skins/classic/views/timeline.php index 2fe419efd..794b99c20 100644 --- a/web/skins/classic/views/timeline.php +++ b/web/skins/classic/views/timeline.php @@ -212,11 +212,14 @@ if ( isset($minTime) && isset($maxTime) ) { if ( !isset($minTime) || !isset($maxTime) ) { // Dynamically determine range $row = dbFetchOne($rangeSql); - - if ( !isset($minTime) ) - $minTime = $row['MinTime']; - if ( !isset($maxTime) ) - $maxTime = $row['MaxTime']; + if ( $row ) { + if ( !isset($minTime) ) + $minTime = $row['MinTime']; + if ( !isset($maxTime) ) + $maxTime = $row['MaxTime']; + } else { + # Errors will be reported by db functions + } } if ( empty($minTime) ) @@ -307,7 +310,7 @@ $monEventSlots = array(); $monFrameSlots = array(); $events_result = dbQuery($eventsSql); if ( !$events_result ) { - Fatal('SQL-ERR'); + ZM\Fatal('SQL-ERR'); return; } @@ -552,7 +555,7 @@ if ( $mode == 'overlay' ) { $top += $chart['graph']['activityBarHeight']+1+$chart['graph']['eventBarHeight']+1; } } else { - Warning("No mode $mode"); + ZM\Warning("No mode $mode"); } preg_match('/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $minTime, $startMatches); diff --git a/web/skins/classic/views/version.php b/web/skins/classic/views/version.php index 3ced8c613..6552c4cd5 100644 --- a/web/skins/classic/views/version.php +++ b/web/skins/classic/views/version.php @@ -33,6 +33,7 @@ if ( verNum(ZM_DYN_CURR_VERSION) != verNum(ZM_DYN_LAST_VERSION) and canEdit('Sys 'hour' => translate('VersionRemindHour'), 'day' => translate('VersionRemindDay'), 'week' => translate('VersionRemindWeek'), + 'month' => translate('VersionRemindMonth'), 'never' => translate('VersionRemindNever') ) ); } @@ -64,16 +65,16 @@ if ( ZM_DYN_DB_VERSION && (ZM_DYN_DB_VERSION != ZM_VERSION) ) { } else { ?> - +

-

+

- + DefaultScale(), ZM_WEB_DEFAULT_SCALE); + $scale = $monitor->DefaultScale(); } $connkey = generateConnKey(); $streamMode = getStreamMode(); -noCacheHeaders(); - $popup = ((isset($_REQUEST['popup'])) && ($_REQUEST['popup'] == 1)); +noCacheHeaders(); xhtmlHeaders(__FILE__, $monitor->Name().' - '.translate('Feed')); ?>

All

-
+ "; - echo '

Monitor: ' . $monitorNames[$monitor_id] . '

'; + foreach ( $monitors as $monitor_id ) { + echo '
'; + echo '

Monitor: '.$monitorNames[$monitor_id].'

'; foreach ( $events as $event ) { if ( $event->MonitorId() == $monitor_id ) { - eventlist_html($event); + echo eventlist_html($event, $exportDetail, $exportFrames); } # end if its the right monitor } # end foreach event echo '
'; } # end foreach monitor ?> -
- @@ -681,7 +675,7 @@ function exportEventImagesMaster($eids) { @@ -751,7 +763,14 @@ function loadintoIframe(iframeid, url) { return ob_get_clean(); } -function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc) { +function exportFileList( + $event, + $exportDetail, + $exportFrames, + $exportImages, + $exportVideo, + $exportMisc +) { if ( !canView('Events') or !$event ) { return; @@ -773,28 +792,31 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex if ( $exportDetail ) { $file = 'zmEventDetail.html'; - if ( !($fp = fopen($eventPath.'/'.$file, 'w')) ) { - ZM\Fatal("Can't open event detail export file '$file'"); + if ( $fp = fopen($eventPath.'/'.$file, 'w') ) { + fwrite($fp, exportEventDetail($event, $exportFrames, $exportImages)); + fclose($fp); + $exportFileList[$file] = $file; + } else { + ZM\Error("Can't open event detail export file '$file'"); } - fwrite($fp, exportEventDetail($event, $exportFrames, $exportImages)); - fclose($fp); - $exportFileList[$file] = $file; } + if ( $exportFrames ) { $file = 'zmEventFrames.html'; - if ( !($fp = fopen($eventPath.'/'.$file, 'w')) ) { - ZM\Fatal("Can't open event frames export file '$file'"); + if ( $fp = fopen($eventPath.'/'.$file, 'w') ) { + fwrite($fp, exportEventFrames($event, $exportDetail, $exportImages)); + fclose($fp); + $exportFileList[$file] = $file; + } else { + ZM\Error("Can't open event frames export file '$file'"); } - fwrite($fp, exportEventFrames($event, $exportDetail, $exportImages)); - fclose($fp); - $exportFileList[$file] = $file; } if ( $exportImages ) { $filesLeft = array(); $myfilelist = array(); foreach ( $files as $file ) { - if ( preg_match('/-(?:capture|analyse).jpg$/', $file ) ) { + if ( preg_match('/-(?:capture|analyse).jpg$/', $file) ) { $myfilelist[$file] = $exportFileList[$file] = $file; } else { $filesLeft[$file] = $file; @@ -805,11 +827,13 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex // create an image slider if ( !empty($myfilelist) ) { $file = 'zmEventImages.html'; - if ( !($fp = fopen($file, 'w')) ) - ZM\Fatal("Can't open event images export file '$file'"); - fwrite($fp, exportEventImages($event, $exportDetail, $exportFrames, $myfilelist)); - fclose($fp); - $exportFileList[$file] = $file; + if ( $fp = fopen($eventPath.'/'.$file, 'w') ) { + fwrite($fp, exportEventImages($event, $exportDetail, $exportFrames, $myfilelist)); + fclose($fp); + $exportFileList[$file] = $file; + } else { + ZM\Error("Can't open event images export file '$file'"); + } } } # end if exportImages @@ -823,7 +847,7 @@ function exportFileList($event, $exportDetail, $exportFrames, $exportImages, $ex } } $files = $filesLeft; - } # end if exportVideo + } # end if exportVideo if ( $exportMisc ) { foreach ( $files as $file ) { @@ -869,13 +893,14 @@ function exportEvents( # Ensure that we are going to be able to do this. if ( ! ( mkdir($export_dir) or file_exists($export_dir) ) ) { - ZM\Fatal("Can't create exports dir at '$export_dir'"); - } else { - ZM\Logger::Debug("Successfully created dir '$export_dir'"); + ZM\Error("Can't create exports dir at '$export_dir'"); + return false; } + ZM\Logger::Debug("Successfully created dir '$export_dir'"); chmod($export_dir, 0700); if ( !chdir($export_dir) ) { - ZM\Fatal("Can't chdir to $export_dir"); + ZM\Error("Can't chdir to $export_dir"); + return; } $export_root = 'zmExport'; @@ -886,43 +911,37 @@ function exportEvents( if ( !is_array($eids) ) { $eids = array($eids); } - ZM\Logger::Debug('Eids: ' . print_r($eids,true)); foreach ( $eids as $eid ) { $event = new ZM\Event($eid); $event_dir = $export_dir.'/'.$event->Id(); - if ( !(mkdir($event_dir) or file_exists($event_dir) ) ) { + if ( !(mkdir($event_dir) or file_exists($event_dir)) ) { ZM\Error("Can't mkdir $event_dir"); } $event_exportFileList = exportFileList($event, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc); - ZM\Logger::Debug("File list for event $eid " . print_r($event_exportFileList, true)); - $exportFileList = array_merge($exportFileList,$event_exportFileList); + $exportFileList = array_merge($exportFileList, $event_exportFileList); foreach ( $event_exportFileList as $file ) { - if ( preg_match('/\.html$/', $file) ) - continue; - #exec('cp -as '.$event->Path().'/../'.$file.' '.$export_dir.'/'.$file, $output, $return); + #if ( preg_match('/\.html$/', $file) ) + #continue; $cmd = 'cp -as '.$event->Path().'/'.$file.' '.$export_dir.'/'.$event->Id().'/'.$file. ' 2>&1'; exec($cmd, $output, $return); ZM\Logger::Debug($cmd.' return code: '.$return.' output: '.print_r($output,true)); - } + } # end foreach event_exportFile } # end foreach event - // create an master image - if ( $exportImages ) { - if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/jquery.js', $export_dir.'/jquery.js') ) - ZM\Error('Failed linking jquery.js'); - //if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/video.js', $export_dir.'/video.js') ) - //Error("Failed linking video.js"); + if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/jquery.js', $export_dir.'/jquery.js') ) + ZM\Error('Failed linking jquery.js'); + //if ( !symlink(ZM_PATH_WEB.'/'.ZM_SKIN_PATH.'/js/video.js', $export_dir.'/video.js') ) + //Error("Failed linking video.js"); - $html_eventMaster_file = 'zmEventImagesMaster_'.date('Ymd_His'). '.html'; - $html_eventMaster_path = $export_dir.'/'.$html_eventMaster_file; + $html_eventMaster_file = 'zmEventImagesMaster.html'; + $html_eventMaster_path = $export_dir.'/'.$html_eventMaster_file; - if ( ($fp = fopen($html_eventMaster_path, 'w')) ) { - fwrite($fp, exportEventImagesMaster($eids)); - fclose($fp); - $exportFileList[] = $html_eventMaster_file; - } else { - ZM\Error("Can't open event images export file '$html_eventMaster_path'"); - } + if ( ($fp = fopen($html_eventMaster_path, 'w')) ) { + fwrite($fp, exportEventImagesMaster($eids, $exportDetail, $exportFrames)); + fclose($fp); + $exportFileList[] = $html_eventMaster_file; + } else { + ZM\Error("Can't open event images export file '$html_eventMaster_path'"); } $listFile = $export_dir.'/'.$export_listFile; @@ -949,10 +968,10 @@ function exportEvents( $exportFormat .= '.gz'; } if ( $exportStructure == 'flat' ) { - if (preg_match("/BSD/i", $version)) { - $command .= " -s '#^.*/##'"; + if ( preg_match('/BSD/i', $version) ) { + $command .= ' -s \'#^.*/##\''; } else { - $command .= " --xform='s#^.+/##x'"; + $command .= ' --xform=\'s#^.+/##x\''; } } $command .= ' --file='.escapeshellarg($archive); @@ -974,10 +993,10 @@ function exportEvents( return false; } - //clean up temporary files + // clean up temporary files if ( !empty($html_eventMaster) ) { unlink($monitorPath.'/'.$html_eventMaster); } return '?view=archive%26type='.$exportFormat.'%26connkey='.$connkey; -} +} // end function exportEvents diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index 3042a5488..51a4a0820 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -1,6 +1,6 @@ + + @@ -146,11 +148,7 @@ if ( $css != 'base' ) - - @@ -341,33 +339,35 @@ if ( $user and $user['Username'] ) {
  • >
  • >
  • keyboard_arrow_
  • - + + @@ -396,7 +396,7 @@ if ( (!ZM_OPT_USE_AUTH) or $user ) { ?>
  • : true)); $storage_paths = null; $storage_areas_with_no_server_id = array(); foreach ( $storage_areas as $area ) { diff --git a/web/skins/classic/includes/timeline_functions.php b/web/skins/classic/includes/timeline_functions.php index b5cc3909c..1c0995df4 100644 --- a/web/skins/classic/includes/timeline_functions.php +++ b/web/skins/classic/includes/timeline_functions.php @@ -1,6 +1,6 @@ $maxLines ) { @@ -29,20 +29,21 @@ function getYScale( $range, $minLines, $maxLines ) { } $scale['lines'] = (int)(($scale['range']-1)/$scale['divisor'])+1; - return( $scale ); + return $scale; } -function getSlotFrame( $slot ) { +function getSlotFrame($slot) { $slotFrame = isset($slot['frame'])?$slot['frame']['FrameId']:1; + # FIXME what's with this false? if ( false && $slotFrame ) { $slotFrame -= $monitor['PreEventCount']; if ( $slotFrame < 1 ) $slotFrame = 1; } - return( $slotFrame ); + return $slotFrame; } -function parseFilterToTree( $filter ) { +function parseFilterToTree($filter) { if ( count($filter['terms']) <= 0 ) { return false; } @@ -68,73 +69,82 @@ function parseFilterToTree( $filter ) { 'or' => 4, ); - for ( $i = 0; $i <= count($terms); $i++ ) { - if ( !empty($terms[$i]['cnj']) ) { + for ( $i = 0; $i < count($terms); $i++ ) { + $term = $terms[$i]; + if ( !empty($term['cnj']) ) { while( true ) { if ( !count($postfixStack) ) { - $postfixStack[] = array('type'=>'cnj', 'value'=>$terms[$i]['cnj'], 'sqlValue'=>$terms[$i]['cnj']); + $postfixStack[] = array('type'=>'cnj', 'value'=>$term['cnj'], 'sqlValue'=>$term['cnj']); break; } elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) { - $postfixStack[] = array('type'=>'cnj', 'value'=>$terms[$i]['cnj'], 'sqlValue'=>$terms[$i]['cnj']); + $postfixStack[] = array('type'=>'cnj', 'value'=>$term['cnj'], 'sqlValue'=>$term['cnj']); break; - } elseif ( $priorities[$terms[$i]['cnj']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { - $postfixStack[] = array('type'=>'cnj', 'value'=>$terms[$i]['cnj'], 'sqlValue'=>$terms[$i]['cnj']); + } elseif ( $priorities[$term['cnj']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { + $postfixStack[] = array('type'=>'cnj', 'value'=>$term['cnj'], 'sqlValue'=>$term['cnj']); break; } else { $postfixExpr[] = array_pop($postfixStack); } } - } - if ( !empty($terms[$i]['obr']) ) { - for ( $j = 0; $j < $terms[$i]['obr']; $j++ ) { - $postfixStack[] = array('type'=>'obr', 'value'=>$terms[$i]['obr']); + } # end if ! empty cnj + + if ( !empty($term['obr']) ) { + for ( $j = 0; $j < $term['obr']; $j++ ) { + $postfixStack[] = array('type'=>'obr', 'value'=>$term['obr']); } } - if ( !empty($terms[$i]['attr']) ) { + if ( !empty($term['attr']) ) { $dtAttr = false; - switch ( $terms[$i]['attr']) { + switch ( $term['attr']) { case 'MonitorName': - $sqlValue = 'M.'.preg_replace( '/^Monitor/', '', $terms[$i]['attr']); + $sqlValue = 'M.'.preg_replace( '/^Monitor/', '', $term['attr']); break; case 'ServerId': $sqlValue .= 'M.ServerId'; - break; + break; + case 'StorageServerId': + $sqlValue .= 'S.ServerId'; + break; + case 'FilterServerId': + $sqlValue .= ZM_SERVER_ID; + break; case 'DateTime': case 'StartDateTime': - $sqlValue = "E.StartTime"; + $sqlValue = 'E.StartTime'; $dtAttr = true; break; case 'Date': case 'StartDate': - $sqlValue = "to_days( E.StartTime )"; + $sqlValue = 'to_days(E.StartTime)'; $dtAttr = true; break; case 'Time': case 'StartTime': - $sqlValue = "extract( hour_second from E.StartTime )"; + $sqlValue = 'extract(hour_second from E.StartTime)'; break; case 'Weekday': case 'StartWeekday': - $sqlValue = "weekday( E.StartTime )"; + $sqlValue = 'weekday(E.StartTime)'; break; case 'EndDateTime': - $sqlValue = "E.EndTime"; + $sqlValue = 'E.EndTime'; $dtAttr = true; break; case 'EndDate': - $sqlValue = "to_days( E.EndTime )"; + $sqlValue = 'to_days(E.EndTime)'; $dtAttr = true; break; case 'EndTime': - $sqlValue = "extract( hour_second from E.EndTime )"; + $sqlValue = 'extract(hour_second from E.EndTime)'; break; case 'EndWeekday': - $sqlValue = "weekday( E.EndTime )"; + $sqlValue = 'weekday(E.EndTime)'; break; case 'Id': case 'Name': case 'MonitorId': case 'StorageId': + case 'SecondaryStorageId': case 'Length': case 'Frames': case 'AlarmFrames': @@ -145,7 +155,7 @@ function parseFilterToTree( $filter ) { case 'Notes': case 'StateId': case 'Archived': - $sqlValue = "E.".$terms[$i]['attr']; + $sqlValue = 'E.'.$term['attr']; break; case 'DiskPercent': // Need to specify a storage area, so need to look through other terms looking for a storage area, else we default to ZM_EVENTS_PATH @@ -174,53 +184,66 @@ function parseFilterToTree( $filter ) { $sqlValue = getDiskBlocks($StorageArea); break; default : - $sqlValue = $terms[$i]['attr']; + $sqlValue = $term['attr']; break; } if ( $dtAttr ) { - $postfixExpr[] = array('type'=>'attr', 'value'=>$terms[$i]['attr'], 'sqlValue'=>$sqlValue, 'dtAttr'=>true); + $postfixExpr[] = array('type'=>'attr', 'value'=>$term['attr'], 'sqlValue'=>$sqlValue, 'dtAttr'=>true); } else { - $postfixExpr[] = array('type'=>'attr', 'value'=>$terms[$i]['attr'], 'sqlValue'=>$sqlValue); + $postfixExpr[] = array('type'=>'attr', 'value'=>$term['attr'], 'sqlValue'=>$sqlValue); } } # end if attr - if ( isset($terms[$i]['op']) ) { - if ( empty($terms[$i]['op']) ) { - $terms[$i]['op'] = '='; + if ( isset($term['op']) ) { + if ( empty($term['op']) ) { + $term['op'] = '='; } - switch ( $terms[$i]['op']) { + switch ( $term['op']) { case '=' : case '!=' : case '>=' : case '>' : case '<' : case '<=' : - $sqlValue = $terms[$i]['op']; + case 'LIKE' : + case 'NOT LIKE': + $sqlValue = $term['op']; break; - case '=~' : + case '=~' : $sqlValue = 'regexp'; break; case '!~' : $sqlValue = 'not regexp'; break; case '=[]' : + case 'IN' : $sqlValue = 'in ('; break; case '![]' : $sqlValue = 'not in ('; break; + case 'IS' : + case 'IS NOT' : + if ( $term['val'] == 'Odd' ) { + $sqlValue .= ' % 2 = 1'; + } else if ( $term['val'] == 'Even' ) { + $sqlValue .= ' % 2 = 0'; + } else { + $sqlValue .= ' '.$term['op']; + } + break; default : - Error('Unknown operator in filter '. $terms[$i]['op']); + ZM\Error('Unknown operator in filter '.$term['op']); } while( true ) { if ( !count($postfixStack) ) { - $postfixStack[] = array('type'=>'op', 'value'=>$terms[$i]['op'], 'sqlValue'=>$sqlValue); + $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue); break; } elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) { - $postfixStack[] = array('type'=>'op', 'value'=>$terms[$i]['op'], 'sqlValue'=>$sqlValue); + $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue); break; - } elseif ( $priorities[$terms[$i]['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { - $postfixStack[] = array('type'=>'op', 'value'=>$terms[$i]['op'], 'sqlValue'=>$sqlValue ); + } elseif ( $priorities[$term['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { + $postfixStack[] = array('type'=>'op', 'value'=>$term['op'], 'sqlValue'=>$sqlValue ); break; } else { $postfixExpr[] = array_pop($postfixStack); @@ -228,17 +251,23 @@ function parseFilterToTree( $filter ) { } // end while } // end if operator - if ( isset($terms[$i]['val']) ) { + if ( isset($term['val']) ) { $valueList = array(); - foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $terms[$i]['val'])) as $value ) { - switch ( $terms[$i]['attr'] ) { + foreach ( preg_split('/["\'\s]*?,["\'\s]*?/', preg_replace('/^["\']+?(.+)["\']+?$/', '$1', $term['val'])) as $value ) { + switch ( $term['attr'] ) { case 'MonitorName': case 'Name': case 'Cause': case 'Notes': - $value = "'$value'"; - break; - case 'ServerId': + if ( $term['op'] == 'LIKE' || $term['op'] == 'NOT LIKE' ) { + $value = '%'.$value.'%'; + } + $value = dbEscape($value); + break; + case 'MonitorServerId': + case 'FilterServerId': + case 'StorageServerId': + case 'ServerId': if ( $value == 'ZM_SERVER_ID' ) { $value = ZM_SERVER_ID; } else if ( $value == 'NULL' ) { @@ -260,17 +289,17 @@ function parseFilterToTree( $filter ) { case 'Date': case 'EndDate': case 'StartDate': - $value = "to_days('".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."' )"; + $value = 'to_days(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; break; case 'Time': case 'EndTime': case 'StartTime': - $value = "extract( hour_second from '".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."' )"; + $value = 'extract(hour_second from \''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; break; case 'Weekday': case 'EndWeekday': case 'StartWeekday': - $value = "weekday( '".strftime(STRF_FMT_DATETIME_DB, strtotime($value))."' )"; + $value = 'weekday(\''.strftime(STRF_FMT_DATETIME_DB, strtotime($value)).'\')'; break; default : if ( $value != 'NULL' ) @@ -278,11 +307,11 @@ function parseFilterToTree( $filter ) { } // end switch attribute $valueList[] = $value; } // end foreach value - $postfixExpr[] = array('type'=>'val', 'value'=>$terms[$i]['val'], 'sqlValue'=>join(',', $valueList)); + $postfixExpr[] = array('type'=>'val', 'value'=>$term['val'], 'sqlValue'=>join(',', $valueList)); } // end if has val - if ( !empty($terms[$i]['cbr']) ) { - for ( $j = 0; $j < $terms[$i]['cbr']; $j++ ) { + if ( !empty($term['cbr']) ) { + for ( $j = 0; $j < $term['cbr']; $j++ ) { while ( count($postfixStack) ) { $element = array_pop($postfixStack); if ( $element['type'] == 'obr' ) { @@ -292,8 +321,9 @@ function parseFilterToTree( $filter ) { $postfixExpr[] = $element; } } - } - } + } #end if cbr + } # end foreach term + while ( count($postfixStack) ) { $postfixExpr[] = array_pop($postfixStack); } @@ -314,11 +344,11 @@ function parseFilterToTree( $filter ) { $node = array('data'=>$element, 'count'=>2+$left['count']+$right['count'], 'right'=>$right, 'left'=>$left); $exprStack[] = $node; } else { - Fatal("Unexpected element type '".$element['type']."', value '".$element['value']."'"); + ZM\Fatal('Unexpected element type \''.$element['type'].'\', value \''.$element['value'].'\''); } } if ( count($exprStack) != 1 ) { - Fatal('Expression stack has '.count($exprStack).' elements'); + ZM\Fatal('Expression stack has '.count($exprStack).' elements'); } return array_pop($exprStack); } @@ -345,188 +375,153 @@ function parseTreeToInfix($tree) { return _parseTreeToInfix($tree); } -function _parseTreeToSQL( $node, $cbr=false ) -{ +function _parseTreeToSQL($node, $cbr=false) { $expression = ''; - if ( $node ) - { - if ( isset($node['left']) ) - { - if ( !empty($node['data']['bracket']) ) - $expression .= '( '; - $expression .= _parseTreeToSQL( $node['left'] ); - } - $inExpr = $node['data']['type'] == 'op' && ($node['data']['value'] == '=[]' || $node['data']['value'] == '![]'); - $expression .= $node['data']['sqlValue']; - if ( !$inExpr ) - $expression .= ' '; - if ( $cbr ) - $expression .= ') '; - if ( isset($node['right']) ) - { - $expression .= _parseTreeToSQL( $node['right'], $inExpr ); - if ( !empty($node['data']['bracket']) ) - $expression .= ') '; - } - } - return( $expression ); + if ( !$node ) + return $expression; + + if ( isset($node['left']) ) { + if ( !empty($node['data']['bracket']) ) + $expression .= '( '; + $expression .= _parseTreeToSQL($node['left']); + } + $inExpr = $node['data']['type'] == 'op' && ( + $node['data']['value'] == '=[]' + or + $node['data']['value'] == '![]' + or + $node['data']['value'] == 'IN' + or + $node['data']['value'] == 'NOT IN' + ); + $expression .= $node['data']['sqlValue']; + if ( !$inExpr ) + $expression .= ' '; + if ( $cbr ) + $expression .= ') '; + if ( isset($node['right']) ) { + $expression .= _parseTreeToSQL($node['right'], $inExpr); + if ( !empty($node['data']['bracket']) ) + $expression .= ') '; + } # end if right + return $expression; } -function parseTreeToSQL( $tree ) -{ - return( _parseTreeToSQL( $tree ) ); +function parseTreeToSQL($tree) { + return _parseTreeToSQL($tree); } -function _parseTreeToFilter( $node, &$terms, &$level ) -{ +function _parseTreeToFilter($node, &$terms, &$level) { $elements = array(); - if ( $node ) - { - if ( isset($node['left']) ) - { + if ( $node ) { + if ( isset($node['left']) ) { if ( !empty($node['data']['bracket']) ) $terms[$level]['obr'] = 1; _parseTreeToFilter( $node['left'], $terms, $level ); } - if ( $node['data']['type'] == 'cnj' ) - { + if ( $node['data']['type'] == 'cnj' ) { $level++; } $terms[$level][$node['data']['type']] = $node['data']['value']; - if ( isset($node['right']) ) - { - _parseTreeToFilter( $node['right'], $terms, $level ); + if ( isset($node['right']) ) { + _parseTreeToFilter($node['right'], $terms, $level); if ( !empty($node['data']['bracket']) ) $terms[$level]['cbr'] = 1; } } } -function parseTreeToFilter( $tree ) -{ +function parseTreeToFilter($tree) { $terms = array(); - if ( isset($tree) ) - { + if ( isset($tree) ) { $level = 0; - _parseTreeToFilter( $tree, $terms, $level ); + _parseTreeToFilter($tree, $terms, $level); } - return( array( 'Query' => array( 'terms' => $terms ) ) ); + return array('Query' => array('terms' => $terms)); } -function parseTreeToQuery( $tree ) -{ - $filter = parseTreeToFilter( $tree ); - parseFilter( $filter, false, '&' ); - return( $filter['query'] ); +function parseTreeToQuery($tree) { + $filter = parseTreeToFilter($tree); + parseFilter($filter, false, '&'); + return $filter['query']; } -function _drawTree( $node, $level ) -{ - if ( isset($node['left']) ) - { - _drawTree( $node['left'], $level+1 ); +function _drawTree($node, $level) { + if ( isset($node['left']) ) { + _drawTree($node['left'], $level+1); } - echo str_repeat( ".", $level*2 ).$node['data']['value']."
    "; - if ( isset($node['right']) ) - { - _drawTree( $node['right'], $level+1 ); + echo str_repeat('.', $level*2).$node['data']['value'].'
    '; + if ( isset($node['right']) ) { + _drawTree($node['right'], $level+1); } } -function drawTree( $tree ) -{ - _drawTree( $tree, 0 ); +function drawTree($tree) { + _drawTree($tree, 0); } -function _extractDatetimeRange( &$node, &$minTime, &$maxTime, &$expandable, $subOr ) -{ +function _extractDatetimeRange(&$node, &$minTime, &$maxTime, &$expandable, $subOr) { $pruned = $leftPruned = $rightPruned = false; - if ( $node ) - { - if ( isset($node['left']) && isset($node['right']) ) - { - if ( $node['data']['type'] == 'cnj' && $node['data']['value'] == 'or' ) - { - $subOr = true; - } - elseif ( !empty($node['left']['data']['dtAttr']) ) - { - if ( $subOr ) - { - $expandable = false; - } - elseif ( $node['data']['type'] == 'op' ) - { - if ( $node['data']['value'] == '>' || $node['data']['value'] == '>=' ) - { - if ( !$minTime || $minTime > $node['right']['data']['sqlValue'] ) - { - $minTime = $node['right']['data']['value']; - return( true ); - } - } - if ( $node['data']['value'] == '<' || $node['data']['value'] == '<=' ) - { - if ( !$maxTime || $maxTime < $node['right']['data']['sqlValue'] ) - { - $maxTime = $node['right']['data']['value']; - return( true ); - } - } - } - else - { - Fatal( "Unexpected node type '".$node['data']['type']."'" ); - } - return( false ); - } + if ( !($node and isset($node['left']) and isset($node['right']) ) ) { + return $pruned; + } - $leftPruned = _extractDatetimeRange( $node['left'], $minTime, $maxTime, $expandable, $subOr ); - $rightPruned = _extractDatetimeRange( $node['right'], $minTime, $maxTime, $expandable, $subOr ); + if ( $node['data']['type'] == 'cnj' && $node['data']['value'] == 'or' ) { + $subOr = true; + } else if ( !empty($node['left']['data']['dtAttr']) ) { + if ( $subOr ) { + $expandable = false; + } elseif ( $node['data']['type'] == 'op' ) { + if ( $node['data']['value'] == '>' || $node['data']['value'] == '>=' ) { + if ( !$minTime || $minTime > $node['right']['data']['sqlValue'] ) { + $minTime = $node['right']['data']['value']; + return true; + } + } else if ( $node['data']['value'] == '<' || $node['data']['value'] == '<=' ) { + if ( !$maxTime || $maxTime < $node['right']['data']['sqlValue'] ) { + $maxTime = $node['right']['data']['value']; + return true; + } + } + } else { + ZM\Fatal("Unexpected node type '".$node['data']['type']."'"); + } + return false; + } - if ( $leftPruned && $rightPruned ) - { - $pruned = true; - } - elseif ( $leftPruned ) - { - $node = $node['right']; - } - elseif ( $rightPruned ) - { - $node = $node['left']; - } - } - } - return( $pruned ); + $leftPruned = _extractDatetimeRange( $node['left'], $minTime, $maxTime, $expandable, $subOr ); + $rightPruned = _extractDatetimeRange( $node['right'], $minTime, $maxTime, $expandable, $subOr ); + + if ( $leftPruned && $rightPruned ) { + $pruned = true; + } else if ( $leftPruned ) { + $node = $node['right']; + } else if ( $rightPruned ) { + $node = $node['left']; + } + return $pruned; } -function extractDatetimeRange( &$tree, &$minTime, &$maxTime, &$expandable ) -{ - $minTime = ""; - $maxTime = ""; +function extractDatetimeRange( &$tree, &$minTime, &$maxTime, &$expandable ) { + $minTime = ''; + $maxTime = ''; $expandable = true; _extractDateTimeRange( $tree, $minTime, $maxTime, $expandable, false ); } -function appendDatetimeRange( &$tree, $minTime, $maxTime=false ) -{ +function appendDatetimeRange( &$tree, $minTime, $maxTime=false ) { $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'StartDateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); $valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$minTime, 'sqlValue'=>$minTime ), 'count'=>0 ); $opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'>=', 'sqlValue'=>'>=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode ); - if ( isset($tree) ) - { + if ( isset($tree) ) { $cnjNode = array( 'data'=>array( 'type'=>'cnj', 'value'=>'and', 'sqlValue'=>'and' ), 'count'=>2+$tree['count']+$opNode['count'], 'left'=>$tree, 'right'=>$opNode ); $tree = $cnjNode; - } - else - { + } else { $tree = $opNode; } - if ( $maxTime ) - { + if ( $maxTime ) { $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'StartDateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); $valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$maxTime, 'sqlValue'=>$maxTime ), 'count'=>0 ); $opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'<=', 'sqlValue'=>'<=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode ); diff --git a/web/skins/classic/js/base.js b/web/skins/classic/js/base.js index 48747b98d..d70cd3b6a 100644 --- a/web/skins/classic/js/base.js +++ b/web/skins/classic/js/base.js @@ -63,7 +63,7 @@ var popupSizes = { 'shutdown': {'width': 400, 'height': 400}, 'state': {'width': 400, 'height': 170}, 'stats': {'width': 840, 'height': 200}, - 'storage': {'width': 600, 'height': 405}, + 'storage': {'width': 600, 'height': 425}, 'timeline': {'width': 760, 'height': 540}, 'user': {'width': 460, 'height': 720}, 'version': {'width': 360, 'height': 210}, diff --git a/web/skins/classic/js/classic.js b/web/skins/classic/js/classic.js index b3b72d967..447f31aae 100644 --- a/web/skins/classic/js/classic.js +++ b/web/skins/classic/js/classic.js @@ -63,7 +63,7 @@ var popupSizes = { 'settings': {'width': 220, 'height': 225}, 'state': {'width': 370, 'height': 134}, 'stats': {'width': 840, 'height': 200}, - 'storage': {'width': 600, 'height': 405}, + 'storage': {'width': 600, 'height': 425}, 'timeline': {'width': 760, 'height': 540}, 'user': {'width': 360, 'height': 720}, 'version': {'width': 360, 'height': 140}, diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 7e136cf6a..2012837e8 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -182,8 +182,8 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { return; } - el.onclick = function() { - window[fnName](); + el.onclick = function(ev) { + window[fnName](ev); }; }); @@ -453,10 +453,37 @@ function convertLabelFormat(LabelFormat, monitorName) { //convert label format from strftime to moment's format (modified from //https://raw.githubusercontent.com/benjaminoakes/moment-strftime/master/lib/moment-strftime.js //added %f and %N below (TODO: add %Q) - var replacements = {"a": 'ddd', "A": 'dddd', "b": 'MMM', "B": 'MMMM', "d": 'DD', "e": 'D', "F": 'YYYY-MM-DD', "H": 'HH', "I": 'hh', "j": 'DDDD', "k": 'H', "l": 'h', "m": 'MM', "M": 'mm', "p": 'A', "S": 'ss', "u": 'E', "w": 'd', "W": 'WW', "y": 'YY', "Y": 'YYYY', "z": 'ZZ', "Z": 'z', 'f': 'SS', 'N': "["+monitorName+"]", '%': '%'}; + var replacements = { + 'a': 'ddd', + 'A': 'dddd', + 'b': 'MMM', + 'B': 'MMMM', + 'd': 'DD', + 'e': 'D', + 'F': 'YYYY-MM-DD', + 'H': 'HH', + 'I': 'hh', + 'j': 'DDDD', + 'k': 'H', + 'l': 'h', + 'm': 'MM', + 'M': 'mm', + 'p': 'A', + 'r': 'hh:mm:ss A', + 'S': 'ss', + 'u': 'E', + 'w': 'd', + 'W': 'WW', + 'y': 'YY', + 'Y': 'YYYY', + 'z': 'ZZ', + 'Z': 'z', + 'f': 'SS', + 'N': '['+monitorName+']', + '%': '%'}; var momentLabelFormat = Object.keys(replacements).reduce(function(momentFormat, key) { var value = replacements[key]; - return momentFormat.replace("%" + key, value); + return momentFormat.replace('%' + key, value); }, LabelFormat); return momentLabelFormat; } @@ -467,7 +494,7 @@ function addVideoTimingTrack(video, LabelFormat, monitorName, duration, startTim var labelFormat = convertLabelFormat(LabelFormat, monitorName); startTime = moment(startTime); - for (var i = 0; i <= duration; i++) { + for ( var i = 0; i <= duration; i++ ) { cues[i] = {id: i, index: i, startTime: i, endTime: i+1, text: startTime.format(labelFormat)}; startTime.add(1, 's'); } diff --git a/web/skins/classic/js/skin.js.php b/web/skins/classic/js/skin.js.php index a9f6b85af..5250e654f 100644 --- a/web/skins/classic/js/skin.js.php +++ b/web/skins/classic/js/skin.js.php @@ -71,6 +71,22 @@ var focusWindow = ; var imagePrefix = ""; var auth_hash; - -auth_hash = ''; - +var auth_relay; + diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index a69efa0bb..64371b0a5 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -278,7 +278,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { ?>
  • lens - ' : '>') . $monitor['Name'] ?>
    + ' : '>') . validHtmlStr($monitor['Name']) ?>
    ', @@ -288,7 +288,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { $Groups = $Group->Parents(); array_push( $Groups, $Group ); } - return implode(' > ', array_map(function($Group){ return ''.$Group->Name().''; }, $Groups )); + return implode(' > ', array_map(function($Group){ return ''.validHtmlStr($Group->Name()).''; }, $Groups )); }, $Monitor->GroupIds() ) ); ?>
    Name(); ?>Name()); ?> Name(); } ?>Name()); } ?> '; $imgSrc = $event->getThumbnailSrc(array(),'&'); $streamSrc = $event->getStreamSrc(array( - 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single'), '&'); + 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single', 'rate'=>'400'), '&'); $imgHtml = ''. validHtmlStr('Event '.$event->Id()) .''; echo ''.$imgHtml.''; diff --git a/web/skins/classic/views/export.php b/web/skins/classic/views/export.php index c008b0f2b..6abfd62bf 100644 --- a/web/skins/classic/views/export.php +++ b/web/skins/classic/views/export.php @@ -101,7 +101,7 @@ if ( isset($_REQUEST['eid']) and $_REQUEST['eid'] ) { $eventsSql .= $_REQUEST['filter']['sql']; } $eventsSql .= " ORDER BY $sortColumn $sortOrder"; - if ( isset($_REQUEST['filter']['Query']['limit']) ) + if ( isset($_REQUEST['filter']['Query']['limit']) ) $eventsSql .= ' LIMIT '.validInt($_REQUEST['filter']['Query']['limit']); } # end if filter @@ -244,30 +244,28 @@ while ( $event_row = dbFetchNext($results) ) { - -

    +

    -

    - + diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index e9af1b049..e6df44b76 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -156,8 +156,10 @@ foreach ( dbFetchAll('SELECT `Id`, `Name` FROM `Monitors` ORDER BY lower(`Name`) $zones = array(); foreach ( dbFetchAll('SELECT Id, Name, MonitorId FROM Zones ORDER BY lower(`Name`) ASC') as $zone ) { if ( visibleMonitor($zone['MonitorId']) ) { - $zone['Name'] = validHtmlStr($monitors[$zone['MonitorId']]->Name().': '.$zone['Name']); - $zones[$zone['Id']] = new ZM\Zone($zone); + if ( isset($monitors[$zone['MonitorId']]) ) { + $zone['Name'] = validHtmlStr($monitors[$zone['MonitorId']]->Name().': '.$zone['Name']); + $zones[$zone['Id']] = new ZM\Zone($zone); + } } } diff --git a/web/skins/classic/views/groups.php b/web/skins/classic/views/groups.php index cba26aaea..9840d87f7 100644 --- a/web/skins/classic/views/groups.php +++ b/web/skins/classic/views/groups.php @@ -69,11 +69,11 @@ function group_line( $Group ) { $html .= str_repeat('
     '; if ( canEdit('Groups') ) { - $html .= ''. validHtmlStr($Group->Id() . ' ' . $Group->Name()).''; + $html .= ''.validHtmlStr($Group->Id().' '.$Group->Name()).''; } else { $html .= validHtmlStr($Group->Name()); } - $html .= ''. monitorIdsToNames($Group->MonitorIds(), 30).''. validHtmlStr(monitorIdsToNames($Group->MonitorIds(), 30)).'
    Enabled() ) { ?> checked="checked"/>Enabled() ? ' checked="checked"' : '' ?>/>
    Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?>
    +Protocol() || $monitor->Protocol() == 'http' ) { -?> -
    Method() ); ?>
    Method() ); ?>
    ()
    ()
    ()
    ()
     ()Method() ); ?>
    )Method()) ?>
     (Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>) (Type()), 'zmOptionHelp', 'optionhelp', '?' ) ?>)
    Colours() ); ?> -
    Colours()) ?>
    () @@ -890,23 +933,30 @@ if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) { Orientation());?>
    Deinterlacing())?>
    Deinterlacing())?>
     () RTSPDescribe() ) { ?> checked="checked"/>
     ()RTSPDescribe() ) { ?> checked="checked"/>
    'Default'); - foreach ( ZM\Storage::find(NULL, array('order'=>'lower(Name)')) as $Storage ) { + foreach ( ZM\Storage::find(array('Enabled'=>true), array('order'=>'lower(Name)')) as $Storage ) { $storage_areas[$Storage->Id()] = $Storage->Name(); } echo htmlSelect('newMonitor[StorageId]', $storage_areas, $monitor->StorageId()); @@ -945,11 +995,11 @@ if ( $monitor->Type() == 'Local' ) { $videowriteropts[1] = 'X264 Encode'; - if ($monitor->Type() == 'Ffmpeg' ) + if ( $monitor->Type() == 'Ffmpeg' ) $videowriteropts[2] = 'H264 Camera Passthrough'; else $videowriteropts[2] = array('text'=>'H264 Camera Passthrough - only for FFMPEG','disabled'=>1); - echo htmlselect('newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter()); + echo htmlSelect('newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter()); ?>
    Type() == 'Ffmpeg' ) { ?> RecordAudio() ) { ?> checked="checked"/> @@ -1009,22 +1062,52 @@ echo htmlSelect('newMonitor[OutputContainer]', $videowriter_containers, $monitor case 'timestamp' : { ?> -
    LabelSize()) ?>
    Controllable() ) { ?> checked="checked"/>
    Controllable() ) { ?> checked="checked"/>
    ControlId()); @@ -1058,20 +1142,19 @@ if ( canEdit('Control') ) { TrackMotion() ) { ?> checked="checked"/>
    ReturnLocation()); ?> translate('None'), + '0' => translate('Home'), + '1' => translate('Preset').' 1', + ); +echo htmlSelect('newMonitor[ReturnLocation]', $return_options, $monitor->ReturnLocation()); ?>
    -
    +
    +
    ', array_map(function($group_id){ $Group = new ZM\Group($group_id); $Groups = $Group->Parents(); array_push($Groups, $Group); return implode(' > ', array_map(function($Group){ return ''.$Group->Name().''; }, $Groups )); - }, $Monitor->GroupIds() ) ); + }, $Monitor->GroupIds())); ?>
    Server()->Name()?>Server()->Name())?> Id()])?count($EventsByMonitor[$Monitor->Id()]['Events']):0 ?>link_to($FirstEvent->Id().' at ' . $FirstEvent->StartTime()) : 'none'?>link_to($LastEvent->Id().' at ' . $LastEvent->StartTime()) : 'none'?>link_to($FirstEvent->Id().' at '.$FirstEvent->StartTime()) : 'none'?>link_to($LastEvent->Id().' at '.$LastEvent->StartTime()) : 'none'?> - '.count($FileMissing).'' : '0' ?> + '.count($FileMissing).'' : '0' ?> - '.count($ZeroSize).'' : '0' ?> + '.count($ZeroSize).'' : '0' ?>
    'Remote / No Specific Server') + $ServersById, $newStorage['ServerId']); ?>'Remote / No Specific Server') + $ServersById, $newStorage->ServerId()); ?>
    Type()); ?>
    Scheme()); ?>
    - />Yes - />No + DoDelete() ? 'checked="checked"' : '' ?>/>Yes + DoDelete() ? '' : 'checked="checked"' ?>/>No +
    + Enabled() ? 'checked="checked"' : '' ?>/>Yes + Enabled() ? '' : 'checked="checked"' ?>/>No